欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

代码文本对比-前端工具

程序员文章站 2022-07-13 21:51:49
...

代码文本对比

很多时候,我们可能有那种前端代码对比、文本对比的功能

方式一(纯js实现)

  1. 效果图

代码文本对比-前端工具

  1. 主要html

如果想要在线编辑对比可使用textare标签,并自定义监听函数去对比渲染

<style>
       pre {
           width: 50%;
           float: left;
       }
       legend {
           margin-bottom: 0;
       }
</style>
<body>
<fieldset>
   <legend>升级<span style="color: red">前</span><span style="color: blue">后</span>对比效果</legend>
   <pre id="pre"></pre>
   <pre id="pre2"></pre>
</fieldset>
</body>
  1. 主要js
// data.appBefore--变更前数据
const bf = data.appBefore.replace(new RegExp("<", "gm"), "&lt;").replace(new RegExp(">", "gm"), "&gt;");
// data.appAfter--变更后数据
// 这里replace是把数据中的<、>替换成&lt;、&gt; 如果想要pre标签中展示html、xml类型代码数据的话
const af = data.appAfter.replace(new RegExp("<", "gm"), "&lt;").replace(new RegExp(">", "gm"), "&gt;");
const result = _eq({value1: bf || bf, value2: af || af});

// 由于这个对比页面是个弹窗,用到了layui,故此方式渲染数据至html标签中
// result.value1 变更前数据
// result.value2 变更后数据
body.find("#pre").html(result.value1); 
body.find("#pre2").html(result.value2);

// 核心方法
function _eq(op) {
   if(!op){
       return op;
   }
   if(!op.value1_style){
       op.value1_style="background-color:#f9e9e9;text-decoration: line-through;color:red";
   }
   if(!op.value2_style){
       op.value2_style="background-color:#ddeeff;";
   }
   if(!op.eq_min){
       op.eq_min=3;
   }
   if(!op.eq_index){
       op.eq_index=5;
   }
   if (!op.value1 || !op.value2) {
       return op;
   }
   var ps = {
       v1_i: 0,
       v1_new_value: "",
       v2_i: 0,
       v2_new_value: ""
   };
   while (ps.v1_i < op.value1.length && ps.v2_i < op.value2.length) {
       if (op.value1[ps.v1_i] === op.value2[ps.v2_i]) {
           ps.v1_new_value += op.value1[ps.v1_i].replace(/</g,"<").replace(">",">");
           ps.v2_new_value += op.value2[ps.v2_i].replace(/</g,"<").replace(">",">");
           ps.v1_i += 1;
           ps.v2_i += 1;
           if (ps.v1_i >= op.value1.length) {
               ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
               break;
           }
           if (ps.v2_i >= op.value2.length) {
               ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
               break;
           }
       } else {
           ps.v1_index = ps.v1_i + 1;
           ps.v1_eq_length = 0;
           ps.v1_eq_max = 0;
           ps.v1_start = ps.v1_i + 1;
           while (ps.v1_index < op.value1.length) {
               if (op.value1[ps.v1_index] === op.value2[ps.v2_i + ps.v1_eq_length]) {
                   ps.v1_eq_length += 1;
               } else if (ps.v1_eq_length > 0) {
                   if (ps.v1_eq_max < ps.v1_eq_length) {
                       ps.v1_eq_max = ps.v1_eq_length;
                       ps.v1_start = ps.v1_index - ps.v1_eq_length;
                   }
                   ps.v1_eq_length = 0;
                   break;//只寻找最近的
               }
               ps.v1_index += 1;
           }
           if (ps.v1_eq_max < ps.v1_eq_length) {
               ps.v1_eq_max = ps.v1_eq_length;
               ps.v1_start = ps.v1_index - ps.v1_eq_length;
           }

           ps.v2_index = ps.v2_i + 1;
           ps.v2_eq_length = 0;
           ps.v2_eq_max = 0;
           ps.v2_start = ps.v2_i + 1;
           while (ps.v2_index < op.value2.length) {
               if (op.value2[ps.v2_index] === op.value1[ps.v1_i + ps.v2_eq_length]) {
                   ps.v2_eq_length += 1;
               } else if (ps.v2_eq_length > 0) {
                   if (ps.v2_eq_max < ps.v2_eq_length) {
                       ps.v2_eq_max = ps.v2_eq_length;
                       ps.v2_start = ps.v2_index - ps.v2_eq_length;
                   }
                   ps.v1_eq_length = 0;
                   break;//只寻找最近的
               }
               ps.v2_index += 1;
           }
           if (ps.v2_eq_max < ps.v2_eq_length) {
               ps.v2_eq_max = ps.v2_eq_length;
               ps.v2_start = ps.v2_index - ps.v2_eq_length;
           }
           if (ps.v1_eq_max < op.eq_min && ps.v1_start - ps.v1_i > op.eq_index) {
               ps.v1_eq_max = 0;
           }
           if (ps.v2_eq_max < op.eq_min && ps.v2_start - ps.v2_i > op.eq_index) {
               ps.v2_eq_max = 0;
           }
           if ((ps.v1_eq_max === 0 && ps.v2_eq_max === 0)) {
               ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1[ps.v1_i].replace(/</g,"<").replace(">",">") + "</span>";
               ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2[ps.v2_i].replace(/</g,"<").replace(">",">") + "</span>";
               ps.v1_i += 1;
               ps.v2_i += 1;

               if (ps.v1_i >= op.value1.length) {
                   ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
                   break;
               }
               if (ps.v2_i >= op.value2.length) {
                   ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
                   break;
               }
           } else if (ps.v1_eq_max > ps.v2_eq_max) {
               ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i, ps.v1_start - ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
               ps.v1_i = ps.v1_start;
           } else {
               ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i, ps.v2_start - ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
               ps.v2_i = ps.v2_start;
           }
       }
   }
   op.value1 = ps.v1_new_value;
   op.value2 = ps.v2_new_value;
   return op;
}

方式二(使用CodeMirror)

效果图

代码文本对比-前端工具

codemirror使用过程
下载项目

github:https://github.com/codemirror/CodeMirror
官网:https://codemirror.net/

打开找到demo html->merge.html

代码文本对比-前端工具

检查是否有codemirror.js

代码文本对比-前端工具
如果没有可以在一个新目录下运行npm install codemirror
把新安装的codemirror工程下的lib的codemirror.js复制过来

打开页面

代码文本对比-前端工具
代码文本对比-前端工具
本地调整后测试的merge.html代码:

<!DOCTYPE html>
<html lang="UTF-8">

<title>CodeMirror: merge view demo</title>
<meta charset="utf-8"/>
<!--<link rel=stylesheet href="../doc/docs.css">-->

<link rel=stylesheet href="../lib/codemirror.css">
<link rel=stylesheet href="../addon/merge/merge.css">
<link rel="stylesheet" href="../theme/darcula.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/css/css.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<script src="../addon/merge/merge.js"></script>
<style>
  .CodeMirror {
    line-height: 1.2;
  }

  @media screen and (min-width: 1300px) {
    article {
      max-width: 1000px;
    }

    #nav {
      border-right: 499px solid transparent;
    }
  }

  span.clicky {
    cursor: pointer;
    background: #d70;
    color: white;
    padding: 0 3px;
    border-radius: 3px;
  }
</style>

<article>
  <h2>Compared view</h2>


  <div id=view></div>

  <script>
      var value, orig1, orig2, dv, panes = 2, highlight = true, connect = "align", collapse = false;

      function initUI() {
          if (value == null) return;
          var target = document.getElementById("view");
          target.innerHTML = "";
          dv = CodeMirror.MergeView(target, {
              value: value,
              origLeft: panes === 3 ? orig1 : null,
              orig: orig2,
              lineNumbers: true,
              mode: "text/html",
              theme: 'darcula',
              revertButtons: false,
              highlightDifferences: highlight,
              connect: connect,
              collapseIdentical: collapse
          });
      }

      function toggleDifferences() {
          dv.setShowDifferences(highlight = !highlight);
      }

      window.onload = function () {
          value = 'logging:\n' +
              '  level:\n' +
              '    root: info\n' +
              '    org.apache.ibatis: ${LOG_LEVEL:info}\n' +
              '    io.choerodon: ${LOG_LEVEL:info}\n' +
              '    org.hzero: ${LOG_LEVEL:info}';
          // orig1 = "<!doctype html>\n\n" + value.replace(/\.\.\//g, "codemirror/").replace("yellow", "orange");
          orig2 = 'logging:\n' +
              '  level:\n' +
              '    root: ${LOG_LEVEL:info}\n' +
              '    org.apache.ibatis: ${LOG_LEVEL:info}\n' +
              '    io.choerodon: ${LOG_LEVEL:info}\n' +
              '    org.hzero: ${LOG_LEVEL:info}\n' +
              '\n' +
              '# 新加测试配置\n' +
              'test:\n' +
              '  data: test';
          initUI();
          let d = document.createElement("div");
          d.style.cssText = "width: 50px; margin: 7px; height: 14px";
          dv.editor().addLineWidget(57, d)
      };

      function mergeViewHeight(mergeView) {
          function editorHeight(editor) {
              if (!editor) return 0;
              return editor.getScrollInfo().height;
          }

          return Math.max(editorHeight(mergeView.leftOriginal()),
              editorHeight(mergeView.editor()),
              editorHeight(mergeView.rightOriginal()));
      }

      function resize(mergeView) {
          var height = mergeViewHeight(mergeView);
          for (; ;) {
              if (mergeView.leftOriginal())
                  mergeView.leftOriginal().setSize(null, height);
              mergeView.editor().setSize(null, height);
              if (mergeView.rightOriginal())
                  mergeView.rightOriginal().setSize(null, height);

              var newHeight = mergeViewHeight(mergeView);
              if (newHeight >= height) break;
              else height = newHeight;
          }
          mergeView.wrap.style.height = height + "px";
      }
  </script>
</article>
</html>

关于merge.html中参数和函数说明自行查看官网https://codemirror.net/demo/merge.html

集成应用

前面是在源项目中测试,html中js和css都是相对源项目的相对路径

集成在自己项目中,需要改造迁移html、js、css,把这些文件迁过来
代码文本对比-前端工具
代码文本对比-前端工具
html换了个名字compares.html,代码:

<!DOCTYPE html>
<html lang="UTF-8">
<head>
    <title>详情</title>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <link rel="stylesheet" href="codeMirror/codemirror.css">
    <link rel="stylesheet" href="codeMirror/merge.css">
    <link rel="stylesheet" href="codeMirror/darcula.css">
    <link rel="stylesheet" href="codeMirror/base16-light.css">
    <style>
        .CodeMirror {
            line-height: 1.2;
        }

        @media screen and (min-width: 1300px) {
            article {
                max-width: 1000px;
            }

            #nav {
                border-right: 499px solid transparent;
            }
        }

        span.clicky {
            cursor: pointer;
            background: #d70;
            color: white;
            padding: 0 3px;
            border-radius: 3px;
        }

        h2 {
            color: yellowgreen;
        }
    </style>

</head>

<body>
<article>
    <h2>Compared view</h2>
    <div id=view></div>

    <!-- 引入组件库 -->
    <script src="libs/jquery.min.js"></script>
    <script src="codeMirror/codemirror.js"></script>
<!--    <script src="src/edit/CodeMirror.js"></script>-->
    <script src="codeMirror/diff_match_patch.js"></script>
    <script src="codeMirror/merge.js"></script>
    <script src="codeMirror/xml.js"></script>
    <script src="codeMirror/javascript.js"></script>
    <script src="codeMirror/htmlmixed.js"></script>

    <script type="text/javascript">
        let value, orig1, orig2, dv, panes = 2, highlight = true, connect = null, collapse = false;

        let mainUrl, sv, tv;
        mainUrl = getQueryString("mainUrl");
        sv = getQueryString("sv");
        tv = getQueryString("tv");

        function getQueryString(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return decodeURI(r[2]);
            return null;
        }

        function initUI() {
            if (value == null) return;
            var target = document.getElementById("view");
            target.innerHTML = "";
            dv = CodeMirror.MergeView(target, {
                value: value,
                origLeft: panes === 3 ? orig1 : null,
                orig: orig2,
                lineNumbers: true,
                mode: "text/html",
                theme: 'base16-light', // darcula
                revertButtons: false,
                highlightDifferences: highlight,
                connect: connect,
                collapseIdentical: collapse
            });
        }

        function toggleDifferences() {
            dv.setShowDifferences(highlight = !highlight);
        }

        window.onload = function () {

            $.ajax({
                type: "GET",
                url: mainUrl + "&sourceVersion=" + sv + "&targetVersion=" + tv,
                contentType: "text/plain",
                success: function (data) {
                    value = data.appBefore || data.pomBefore;
                    orig2 = data.appAfter || data.pomAfter;
                    initUI();
                },
                error: function (e) {
                    alert(e.responseText);
                }
            });
        };

        function mergeViewHeight(mergeView) {
            function editorHeight(editor) {
                if (!editor) return 0;
                return editor.getScrollInfo().height;
            }

            return Math.max(editorHeight(mergeView.leftOriginal()),
                editorHeight(mergeView.editor()),
                editorHeight(mergeView.rightOriginal()));
        }

        function resize(mergeView) {
            var height = mergeViewHeight(mergeView);
            for (; ;) {
                if (mergeView.leftOriginal())
                    mergeView.leftOriginal().setSize(null, height);
                mergeView.editor().setSize(null, height);
                if (mergeView.rightOriginal())
                    mergeView.rightOriginal().setSize(null, height);

                var newHeight = mergeViewHeight(mergeView);
                if (newHeight >= height) break;
                else height = newHeight;
            }
            mergeView.wrap.style.height = height + "px";
        }
    </script>
</article>
</body>
</html>

这里对比的数据是从后端接口查的,可以一开始写成常量查看效果

<link rel="stylesheet" href="codeMirror/darcula.css">
<link rel="stylesheet" href="codeMirror/base16-light.css">

上面表示的是主题,想要更多的主题去源码工程迁移过来

<script src="codeMirror/diff_match_patch.js"></script>

diff_match_patch.js可以使用demo中的外部路径,我这里是下载下来了

codeMirror中的js和css文件不要落迁移了
其他问题注意自己看控制台检查
代码文本对比-前端工具