前端diff文件对比使用worker进行优化

前端diff文件对比使用worker进行优化

worker的使用:React中使用worker线程
diff实现文件对比差异功能:react项目配合diff实现文件对比差异功能

本文只是简单记录,如遇到相同问题的,没看懂我写的可以私信联系:

以下是对文件对比进行优化的写法:

diff.js

/*
 * @Descripttion: 
 * @version: 
 * @Author: ZhangJunQing
 * @Date: 2021-11-12 17:35:44
 * @LastEditors: ZhangJunQing
 * @LastEditTime: 2022-05-12 16:15:59
 */
import React, {
    
     Fragment } from 'react'
import {
    
     Modal, Divider, Spin } from 'antd';
import RSButton from '@com/RSButton'
import Worker from './workerJS/differ.worker'
// const Diff = require('diff');
const pStyleObj = {
    
    
    fontSize: "0",
    marginBottom: "0",
    padding: "0",
    lineHeight: "2px",
    wordBreak: "break-word",
    width: "100%",
    // minHeight: `${560}px`,
    overflowX: "auto"
}
const styleObj = {
    
    
    lineHeight: "22px",
    border: "2px solid #fff",
    paddingTop: "2px",
    paddingBottom: "2px",
    borderRadius: "4px",
    fontSize: "16px",
    paddingLeft: "10px",
    marginBottom: "0",
}
export default class DiffString extends React.PureComponent {
    
    
    /**
     * oldStr  为上次文件  已生效配置
     * 
     * newStr 为最新文件  待下发
     * 
 
     * 
     * isModalVisible modal显示与否
     * 
     */
    static defaultProps = {
    
    
        isModalVisible: false,
        title: "预览文件差异",
        leftTitle: "已生效配置",
        rightTitle: "待下发",
        changeIsModalVisibleState: () => {
    
     },
        oldStr: "",
        newStr: "",

        isDiffWords: false, //是否开启单词对比   但是这个也是在行对比的基础上进行改造的
        // 这个变量是为了 空行  使两边相同的内容行对齐
        isSplitBRFlag: true,//是否为 对齐查看   这个变量目前不做动态改变  和changeisSplitBRFlagFun方法配合
        headerHeight: 1,
    }
    /**
     * 
     * 注意:
     * 
     *  当开启 单词对比后   isDiffWords  :true   需要 设置  isSplitBRFlag false
     * 
     *     * contextBoxLoading  加载文件的loading
     */

    state = {
    
    
        isOriginFlag: false, //切换比较模式   默认为文件差异对比   true为源文件对比
        defaultAddColor: "green",
        defaultDelColor: "red",
        defaultColor: "grey",
        DiffLeft: [],
        DiffRight: [],
        leftSourceList: [],
        rightSourceList: [],
        contextBoxLoading: false,
    }
    myWorker = null
    componentDidMount() {
    
    
        this.myWorker = new Worker();
    }
    componentWillReceiveProps(props) {
    
    
        const {
    
     newStr, oldStr, isModalVisible } = props
        if (!isModalVisible) {
    
    
            this.myWorker.terminate()
            return false
        } else {
    
    
            this.myWorker = new Worker();
        }
        if (newStr === '' && oldStr === '') return
        console.time("收到数据直至结束花费时间")
        this.setState({
    
    
            contextBoxLoading: true
        })
        this.myWorker.postMessage({
    
     newStr: newStr.replace(/\r/ig, ''), oldStr: oldStr.replace(/\r/ig, '') })
        const that = this
        this.myWorker.onmessage = function (e) {
    
    
            const {
    
     DiffLeft, leftSourceList, rightSourceList } = e.data
            that.setState({
    
    
                DiffLeft,
                DiffRight: DiffLeft,
                leftSourceList,
                rightSourceList,
                contextBoxLoading: false
            }, () => {
    
    
                console.timeEnd("收到数据直至渲染结束花费时间")
            })
        }
    }
    componentWillUnmount() {
    
    
        this.myWorker.terminate()
    }
    // 源文件切换变量
    changeOriginFileFun = () => {
    
    
        this.setState({
    
    
            isOriginFlag: !this.state.isOriginFlag,
        })
    }


    // 是否为 对齐查看   
    // changeisSplitBRFlagFun = () => {
    
    
    //     this.setState({
    
    
    //         isSplitBRFlag: !this.state.isSplitBRFlag
    //     })
    // }


    /**
     * 每次进来都显示 对比文件页面
     */
    handleCancel() {
    
    
        this.setState({
    
    
            isOriginFlag: false,//默认还是显示 对比文件页面
        })
        this.props.changeIsModalVisibleState()
    }


    /**
     * 这个方法 事件委托
     * 
     * 点击p标签 改变当前p标签的样式
     * 以及更改对面等行的p标签的样式
     * 
     * 点击左边 有背景的为defaultDelColor
     * 点击右边 有背景的为defaultAddColor
     * 不是增加不是删除的的为defaultColor
     * 
     * 2px solid #fff  默认边框 
     * 如果只给当前点击的加边框 对引起整个页面抖动
     * 
     * 所有每一个p标签都添加一个边框  颜色为底色
     * 
     * 这样每次点击的时候把所有的颜色都重置
     * 
     * 然后再将点击的p换颜色 即可
     */
    changeRightLineFun = (flag, ev) => {
    
    
        ev = ev || window.event;
        let target = ev.target || ev.srcElement;
        let index;
        // 点击空P标签
        if (target.style.userSelect === 'none') {
    
    
            return false;
        }
        let {
    
     defaultAddColor, defaultDelColor } = this.state
        let RightFileId = flag === 'right' ? document.getElementById('RightFileId') : document.getElementById('LeftFileId')
        let LeftFileId = flag === 'right' ? document.getElementById('LeftFileId') : document.getElementById('RightFileId')
        let LeftFileIdP = LeftFileId.getElementsByTagName('p')
        let RightFileIdP = RightFileId.getElementsByTagName('p')
        if (target.nodeName.toLowerCase() == "p") {
    
    
            for (let i = 0; i < RightFileIdP.length; i++) {
    
    
                if (RightFileIdP[i] === target)
                    index = i;
                RightFileIdP[i].style.border = "2px solid #fff"
            }
            for (let i = 0; i < LeftFileIdP.length; i++) {
    
    
                if (LeftFileIdP[i] === target)
                    index = i;
                LeftFileIdP[i].style.border = "2px solid #fff"
            }
            if (flag === 'right') {
    
    
                if (target.parentNode.style.color === defaultAddColor) {
    
    
                    target.style.border = `2px solid ${
      
      defaultAddColor}`;
                } else {
    
    
                    target.style.border = "2px solid #000";
                }
            } else {
    
    
                if (target.parentNode.style.color === defaultDelColor) {
    
    
                    target.style.border = `2px solid ${
      
      defaultDelColor}`;
                } else {
    
    
                    target.style.border = "2px solid #000";
                }
            }
            LeftFileIdP[index].style.border = "2px solid #000";
        }
    }
    // 左右盒子样式
    returnDivStyle = (item) => {
    
    
        let {
    
     defaultAddColor, defaultDelColor, defaultColor, } = this.state;
        return {
    
    
            color: item ?.added ? defaultAddColor :
                item ?.removed ? defaultDelColor : defaultColor,
            fontWeight: item ?.added ? '600' :
                item ?.removed ? '600' : '600',
            ...pStyleObj
        }
    }
    // 判断是否换行
    returBrOrNULL = (idx, item) => {
    
    
        return idx !== item.value.split('\n').length - 1 ? <br /> : null
    }
    // 显示P标签
    returnPStyle = () => {
    
    
        let {
    
     defaultColor, } = this.state;
        return {
    
     backgroundColor: "#fff", whiteSpace: "pre", cursor: 'pointer', color: defaultColor, ...styleObj }
    }
    // 隐藏P标签
    returnTransparentP = (item) => {
    
    
        return (
            item ?.value && item.value.split('\n').map((child, idx) => {
    
    
                //隐藏的元素 为了空格
                return <Fragment key={
    
    idx} >
                    {
    
    child && <p style={
    
    {
    
     whiteSpace: "pre", color: "transparent", userSelect: "none", ...styleObj }} >占位置</p>}
                    {
    
    this.returBrOrNULL(idx, item)}
                </Fragment>
            })
        )
    }
    // 左侧和右侧标题
    returnH2Fun = (title) => {
    
    
        const {
    
     headerHeight } = this.props
        return <h2 style={
    
    {
    
     fontWeight: "700", fontSize: "18px", lineHeight: "25px", height: `${
      
      25 * headerHeight}px` }}>{
    
    title}</h2>
    }
    // 返回右侧domlist
    returnRightDomFun = () => {
    
    
        let {
    
     isOriginFlag, defaultAddColor, defaultDelColor, defaultColor, DiffRight, rightSourceList } = this.state;
        const {
    
     isSplitBRFlag, isDiffWords } = this.props
        let domList = isOriginFlag ? rightSourceList : DiffRight
        return domList.map((item, index) => {
    
    
            return (
                <Fragment key={
    
    index} >
                    <div
                        onClick={
    
    (e) => {
    
     this.changeRightLineFun("right", e) }}
                        style={
    
    this.returnDivStyle()}>{
    
    !item ? null : item.removed ? (isSplitBRFlag ?
                            //隐藏的元素 为了空格
                            this.returnTransparentP(item) : null)
                            : item.value.split('\n').map((i, idx) => {
    
    
                                return <Fragment key={
    
    idx} >
                                    {
    
    /* 默认    */}
                                    {
    
    i && !isDiffWords ? <p style={
    
    {
    
     backgroundColor: `${
      
      item.added ? "rgb(221,255,221)" : "#fff"}`, whiteSpace: "pre", cursor: 'pointer', ...styleObj }} key={
    
    idx}>{
    
    i}</p> : null}
                                    {
    
    /* 开启单词对比 */}
                                    {
    
    i && isDiffWords ? <p style={
    
    this.returnPStyle()} >{
    
    
                                        i.split(':')[0]}:<span style={
    
    {
    
    
                                            backgroundColor: `${
      
      item.added ? "rgb(221,255,221)" : "#fff"}`, color: item.added ? defaultAddColor :
                                                item.removed ? defaultDelColor : defaultColor,
                                        }}>{
    
    i.split(':')[1]}</span></p> : null}
                                    {
    
    this.returBrOrNULL(idx, item)}
                                </Fragment>
                            })} </div>
                </Fragment>
            )
        })
    }
    // 返回左侧domlist
    returnLeftDomFun = () => {
    
    
        let {
    
     isOriginFlag, defaultAddColor, defaultDelColor, defaultColor, DiffLeft, leftSourceList } = this.state;
        const {
    
     isSplitBRFlag, isDiffWords } = this.props
        let domList = isOriginFlag ? leftSourceList : DiffLeft;
        return domList.map((item, index) => {
    
    
            return (
                <Fragment key={
    
    index} >
                    <div
                        onClick={
    
    (e) => {
    
     this.changeRightLineFun("left", e) }}
                        style={
    
    this.returnDivStyle(item)}>{
    
    !item ? null : item.added ? (isSplitBRFlag ?
                            this.returnTransparentP(item) : null)
                            : item.value.split('\n').map((i, idx) => {
    
    
                                return <Fragment key={
    
    idx} >
                                    {
    
    /* 默认    */}
                                    {
    
    i && !isDiffWords ? <p style={
    
    {
    
     backgroundColor: `${
      
      item.removed ? "rgb(255,227,227)" : "#fff"}`, whiteSpace: "pre", cursor: 'pointer', ...styleObj }} >{
    
    i}</p> : null}
                                    {
    
    /* 开启单词对比 */}
                                    {
    
    i && isDiffWords ? <p style={
    
    this.returnPStyle()} >{
    
    
                                        i.split(':')[0]}:<span style={
    
    {
    
    
                                            backgroundColor: `${
      
      item.removed ? "rgb(255,227,227)" : "#fff"}`, color: item.added ? defaultAddColor :
                                                item.removed ? defaultDelColor : defaultColor,
                                        }}>{
    
    i.split(':')[1]}</span></p> : null}
                                    {
    
    this.returBrOrNULL(idx, item)}
                                </Fragment>
                            })} </div>
                </Fragment>
            )
        })
    }
    render() {
    
    
        let {
    
     isOriginFlag, defaultAddColor, defaultDelColor, defaultColor, contextBoxLoading, } = this.state
        let {
    
     title, width, isModalVisible, oldStr, newStr, fileName, isDiffWords, isSplitBRFlag, rightTitle, leftTitle, headerHeight, ...rest } = this.props
        return !isModalVisible ? null : (
            <Modal
                title={
    
    <div style={
    
    {
    
     display: "flex", justifyContent: "space-between" }}><span style={
    
    {
    
     fontWeight: "500" }}>{
    
    title}</span><span style={
    
    {
    
     paddingLeft: "50px" }}>{
    
    fileName ? `文件名称:${
      
      fileName}` : ""}</span><h4 style={
    
    {
    
     float: "right" }}>灰色:<span style={
    
    {
    
     color: defaultColor }}>无变化</span>;绿色:<span style={
    
    {
    
     color: defaultAddColor }}>新增</span>;红色:<span style={
    
    {
    
     color: defaultDelColor }}>删除</span></h4></div>}
                visible={
    
    isModalVisible}
                maskClosable={
    
    true}
                style={
    
    {
    
     top: "20%", minWidth: "1000px" }}
                closable={
    
    false}
                width={
    
    width}
                {
    
    ...rest}
                footer={
    
    [
                    <RSButton rsType="noIcon" key="1" title={
    
    !isOriginFlag ? "查看源文件" : "查看对比文件"} onClick={
    
    () => this.changeOriginFileFun()}></RSButton>,
                    // <RSButton rsType="noIcon" key={1} title="切换对比方式" onClick={() => this.changeisSplitBRFlagFun()}></RSButton>,
                    <RSButton rsType="noIcon" key="2" title="关闭" onClick={
    
    () => this.handleCancel()}></RSButton>
                ]}
            >
                <Spin tip="请稍等,可能需要一些时间..." spinning={
    
    contextBoxLoading} size="large" style={
    
    {
    
     width: "100%", height: '100%' }}>
                    {
    
    /* 绑定 key   目前这个key有两种形式 false true 字符的形式 正好对应 查看源文件和对比文件两个key  */}
                    <div style={
    
    {
    
     height: "600px", overflowY: "scroll", width: "100%" }} key={
    
    String(isOriginFlag)}>
                        {
    
    /* // 为了每次都处于文件顶端  */}
                        <div style={
    
    {
    
     wordBreak: "break-all", display: "flex", width: "100%" }} >
                            <div style={
    
    {
    
     width: "49%", paddingLeft: "20px" }} id="LeftFileId">
                                {
    
    this.returnH2Fun(leftTitle)}
                                {
    
    /* {Diff.diffLines(oldStr.replace(/\r/ig, ''), isOriginFlag ? oldStr.replace(/\r/ig, '') : newStr.replace(/\r/ig, '')) */}
                                {
    
    this.returnLeftDomFun()}
                            </div>
                            <Divider type="vertical" style={
    
    {
    
     width: "1%", height: "auto", minHeight: '600px' }} />
                            <div style={
    
    {
    
     width: "49%", paddingLeft: "10px", paddingRight: "10px" }} id="RightFileId" >
                                {
    
    this.returnH2Fun(rightTitle)}
                                {
    
    /* {Diff.diffLines(isOriginFlag ? newStr.replace(/\r/ig, '') : oldStr.replace(/\r/ig, ''), newStr.replace(/\r/ig, '')) */}
                                {
    
    this.returnRightDomFun()}
                            </div>
                        </div>
                    </div>
                </Spin>
            </Modal>
        )
    }
}


worker.js

/*
 * @Descripttion: 
 * @version: 
 * @Author: ZhangJunQing
 * @Date: 2022-04-09 14:20:17
 * @LastEditors: ZhangJunQing
 * @LastEditTime: 2022-04-15 15:57:37
 */

onmessage = function (e) {
    
    //监听主进程的消息
  const {
    
     newStr, oldStr } = e.data
  console.time("处理数据花费时间")
  const DIFF = require('diff')
  let DiffLeft = DIFF.diffLines(oldStr, newStr)
  // 相同类型的字符串进行对比很快就可以完成 几乎忽略不计
  let rightSourceList = DIFF.diffLines(newStr, newStr)
  let leftSourceList = DIFF.diffLines(oldStr, oldStr)
  console.timeEnd("处理数据花费时间")
  console.log("Diffw Worker data already change finish!")
  postMessage({
    
     DiffLeft,  rightSourceList, leftSourceList })//向主进程发送消息
}

目录结构:
在这里插入图片描述
使用diff组件

 <DiffString
      isModalVisible={
    
    this.state.isModalVisible}
       contextBoxLoading={
    
    this.state.contextBoxLoading}
       rightTitle={
    
    '文件名:' + this.state.fileNameArr[0]}
       leftTitle={
    
    '文件名:' + this.state.fileNameArr[1]}
       width={
    
    "66%"}
       style={
    
    {
    
     top: "10%" }}
       oldStr={
    
    this.state.oldStr}
       newStr={
    
    this.state.newStr}
       headerHeight={
    
    2}
       changeIsModalVisibleState={
    
    this.changeIsModalVisibleState}
   />

最终样式:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43291759/article/details/125395542
今日推荐