jsdiff + diff2html【jQuery】实现文件对比功能

发布于:2025-08-12 ⋅ 阅读:(20) ⋅ 点赞:(0)

页面效果:
在这里插入图片描述

jsdiff 是一个 JavaScript 库,用于实现文本差异比较。这个库提供了多种方法来计算和展示两个文本之间的差异,可以用于多种文本差异比较的场景 - 标准 Diff 格式数据
轻量级、算法精度较强、需自行实现可视化的渲染、大文件受限

createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)
createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options)

oldFileName: 旧文件的文件名。
newFileName: 新文件的文件名。
oldStr: 原始字符串值
newStr: 新的字符串值
oldHeader: 可选,允许在旧文件的头部添加额外的信息。
newHeader: 可选,用于在新文件的头部添加额外的信息。
options: 一个包含选项的对象,可以用来自定义补丁的生成方式:
    context: 描述应该包含多少行上下文。设置为 Number.MAX_SAFE_INTEGERInfinity 可以包含整个文件内容在一个块(hunk)中。
    ignoreWhitespace: 与 diffLines 中的用法相同,用于忽略空白字符差异。默认为 false。
    stripTrailingCr: 与 diffLines 中的用法相同,用于在执行差异比较之前去除所有尾随的回车符( )。这有助于在比较 UNIX 和 Windows 文本文件时得到有用的差异结果。默认为 false

diff2html 是一个用于将差异(diff)结果转换成 HTML 格式的工具,它通常用于在网页上展示文件或文本内容之间的差异。这个库提供了一种方便的方式来生成美观的差异比较视图,使得用户可以轻松地查看和理解两个版本之间的变化。

相关 js和css引入
引入文件依赖源码:可以找一个vue项目npm下载,然后在node_modules文件夹中找到对应文件夹 复制对应js以及css文件引入

npm install diff --save
npm install diff2html --save

<link rel="stylesheet" type="text/css" href="./css/diff2html.min.css"/>
<script src="./js/diff2html-ui.min.js"></script>
<script src="./js/diff2html.min.js"></script>
<script src="./js/diff.min.js"></script>

diff2html使用

// 比对文本函数
function compareText() {
  // 生成标准diff格式
  const gitDiffContent = Diff.createPatch(
    'SQL脚本', //文件名称
    leftContent, //对比左侧内容
    rightContent, //对比右侧内容
    '',
    '',
    {
      context: Infinity //保留无差异内容
    }
  );
  
  // 使用Diff2Html 传入生成的标准diff格式数据 生成可视化的html
  const diffHTML = Diff2Html.html(gitDiffContent, {
    inputFormat: 'diff',
    outputFormat: 'side-by-side', //左右分列的形式
    matching: 'none',
    drawFileList: false,
    highlight: true,
    showFiles: true,
    renderNothingWhenEmpty: false
  });
  $('#diff-container').html(diffHTML); //内容区域id: diff-container
  //回显当前锁定左右侧区域
  var index = currentSide == 'left' ? 0 : 1
  $(".d2h-file-side-diff").eq(index).addClass('side_active')
  // 同步滚动
  syncScroll();
}
// 同步滚动函数
function syncScroll() {
    const leftContent = $('.d2h-file-side-diff')[0];
    const rightContent = $('.d2h-file-side-diff')[1];
    
    leftContent.addEventListener('scroll', function() {
        rightContent.scrollTop = leftContent.scrollTop;
        rightContent.scrollLeft = leftContent.scrollLeft;
    });
    
    rightContent.addEventListener('scroll', function() {
        leftContent.scrollTop = rightContent.scrollTop;
        leftContent.scrollLeft = rightContent.scrollLeft;
    });
}

注意事项:

  1. 若外层有flex样式会有冲突,显示异常,添加以下样式即可
.d2h-files-diff {
  height: calc(100% - 35px);
}
.d2h-file-side-diff {
  overflow-y: scroll;
}
/* 强制行号容器同步滚动 */
.d2h-code-side-linenumber {
  position: relative !important; /* 覆盖sticky定位 */
  float: left;
  width: 40px; /* 固定行号列宽度 */
}
/* 主滚动容器修正 */
.d2h-file-side-diff {
  overflow-x: auto !important;
  -webkit-overflow-scrolling: touch; /* iOS滚动优化 */
}
/* 隐藏原生滚动条 */
.d2h-code-side-line::-webkit-scrollbar {
  display: none;
}
  1. 无任何差异文件数据展示

使用 jsdiff 生成的diff数据结果渲染出来会有一个问题,当传入的两个字符串没有任何差异的时候,不显示文件内容,仅显示一行“without any changes”,如果修改要显示全部的文件数据,可以修改diff.js 中的源码

function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
    if (typeof options === 'function') {
        options = { callback: options };
    }
    if (!(options === null || options === void 0 ? void 0 : options.callback)) {
        const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
        if (!patchObj) {
            return;
        }
        //判断是否有差异的逻辑
        if(patchObj.hunks.length > 0) {
          return formatPatch(patchObj);
        } else {
          return createFullPatch(oldFileName, newFileName, oldStr)
        }
    }
    else {
        const { callback } = options;
        structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: patchObj => {
                if (!patchObj) {
                    callback(undefined);
                }
                else {
                  console.log(patchObj);
                    callback(formatPatch(patchObj));
                }
            } }));
    }
}

//无任何差异调用的方法
function createFullPatch(oldFile, newFile, content) {
  const lines = content.split('\n').length;
  return `--- ${oldFile}\n+++ ${newFile}\n@@ -1,${lines} +1,${lines} @@\n` +
    content.split('\n').map(line => ` ${line}`).join('\n');
}