JavaScript的diff库详解(示例:vue项目实现两段字符串比对标黄功能)

diff库介绍

diff 库是基于 Myers 差分算法 实现的 JavaScript 文本差异库。
Myers 差分算法 是由 Eugene Myers 在 1986 年发表的一篇经典算法论文 "An O(ND) Difference Algorithm and its Variations" 中描述的一种高效算法,用于计算两个序列(通常是字符串)之间的差异。

该算法的时间复杂度为 O(ND),其中:

  • N 是两个序列中较短序列的长度。
  • D 是两个序列之间的最小编辑距离,即从一个序列变换为另一个序列所需的最少操作次数(插入、删除或替换)。

通过该算法,diff 库可以高效地分析文本之间的差异,广泛应用于文本比对、版本管理和实时内容编辑等场景。


在线演示文档

diff 库提供了一个在线演示网站,方便用户了解其功能:https://kpdecker.github.io/jsdiff

基于在线演示网站,我们可以看到diff 库支持字符级词级行级unified diff等等的差异比较。

前面三个都好理解,unified diff则可能需要了解下相应概念。

Unified Diff 和 Patch 的概念

Unified Diff

Unified Diff 是一种标准化的差异格式,用于描述两个文本文件之间的变化,广泛使用于版本控制系统(如 Git)。它通过行号和上下文信息展示新增、删除或修改的内容,是 diff 工具生成的输出格式之一。

一个 Unified Diff 的典型结构如下:

diff 复制代码
--- oldFile.txt
+++ newFile.txt
@@ -1,4 +1,4 @@
 Line 1
-Line 2
+Line 2 updated
 Line 3
 Line 4

解释:

clike 复制代码
--- oldFile.txt 和 +++ newFile.txt:分别表示旧文件和新文件的文件名。
@@ -1,4 +1,4 @@:上下文范围的描述。
-1,4 表示旧文件从第 1 行开始的 4 行。
+1,4 表示新文件从第 1 行开始的 4 行。
- 表示从旧文件中移除的内容。
+ 表示添加到新文件中的内容。

Patch

Patch 是应用这些差异的一种工具,通常配合 Unified Diff 使用。patch 工具可以读取 Unified Diff 格式的文件,并将其应用到目标文件上,以实现对文件的更新。

diff 库中的 createPatch 方法生成的就是一个 Unified Diff 格式的输出。可以用这个输出作为输入,再使用 applyPatch 方法将这些差异应用到目标文本中。


diff库比对的基本流程

diff 库的所有diff函数都用于比较两个文本,并执行以下三个步骤:

1. 将文本分割为 "tokens"

  • Token 的定义 :Token 是文本中的最小单位,其定义根据所使用的 diff 方法而变化:
    • diffChars 方法中,每个字符是一个token。
    • diffWords 方法中,每个单词是一个token。
    • diffLines 方法中,每一行是一个token。

通过这种分割方式,diff 库能够灵活地比较文本的不同层次(如字符、单词或行)。


2. 找到最小的操作集合

  • 目标:通过最少的插入和删除操作,将第一个 token 数组转换为第二个 token 数组。
  • 相等的定义
    • 默认情况下,两个 token 是否相等由 === 运算符决定。
    • 某些 diff 方法支持自定义"相等"定义。例如:
      • 默认比较中,diffChars("Foo", "FOOD") 会认为 oO 不相等:
        • 结果:删除两个 o,插入两个 O 和一个 D
      • 设置选项 { ignoreCase: true } 后,oO 会被视为相等:
        • 结果:仅需要插入一个 D

3. 返回变换结果

  • 返回值 :一个数组,表示从旧文本到新文本的转换过程。
    • 数组结构 :包含一系列 change objects
    • 顺序:从输入的起始位置到结束位置按顺序排列。
    • change objects 的含义
      • 插入 :在新文本中添加一个或多个 token(added: true)。
      • 删除 :从旧文本中删除一个或多个 token(removed: true)。
      • 保留 :保持一个或多个 token 不变(无 addedremoved 标记)。

示例代码

以下是 diffChars 的一个简单示例:

javascript 复制代码
import { diffChars } from 'diff';

const oldText = "Foo";
const newText = "FOOD";

// 默认比较(区分大小写)
const result = diffChars(oldText, newText);
console.log(result);
/* 数据格式
[
  { value: 'F', count: 1 },
  { removed: true, value: 'o' },
  { removed: true, value: 'o' },
  { added: true, value: 'O' },
  { added: true, value: 'O' },
  { added: true, value: 'D' }
]
*/

// 忽略大小写
const resultIgnoreCase = diffChars(oldText, newText, { ignoreCase: true });
console.log(resultIgnoreCase);
/* 数据格式
[
  { value: 'Foo', count: 3 },
  { added: true, value: 'D' }
]
*/

diff 库安装与使用

1. 安装库

通过 npm 安装:

bash 复制代码
npm install --save diff

2. 在项目中导入

在 Vue 项目中,可以通过以下方式引入库中所需的功能:

javascript 复制代码
import { diffWords } from 'diff';

3. 在 Vue 项目中使用

在 Vue 项目中,可以将比对函数与 v-html 指令结合,动态渲染高亮比对的结果。

组件模板
html 复制代码
<template>
  <div v-html="getYellowDiffText(tableName1, tableName2)"></div>
</template>
组件逻辑
js 复制代码
<script>
import { diffWords } from 'diff';

export default {
  data() {
    return {
      tableName1: 'Hello world!',
      tableName2: 'Hello my friend!',
    };
  },
  methods: {
    /** 比较两个字符串,标记差异部分为黄色,diff库比对结果状态只有added和removed,新增部分即存在差异部分 */
    getYellowDiffText(tableName1, tableName2) {
      let htmltext = '';
      let diffs = [];

      if (!tableName2) {
        diffs = [{ value: tableName1 }];
      } else {
        diffs = diffWords(tableName1, tableName2);
      }

      diffs.forEach((item) => {
        htmltext += item.added
          ? `<span style="background-color: yellow;">${item.value}</span>`
          : item.removed
          ? '' // 个人项目需求不需要比对删除情况,有需求的可以自行处理
          : item.value;
      });

      return htmltext;
    },
  },
};
</script>

diff库配置

  • 可配置忽略空白字符、大小写等比较选项。
相关推荐
|晴 天|9 分钟前
我如何用Vue 3打造一个现代化个人博客系统(性能提升52%)
前端·javascript·vue.js
风止何安啊17 分钟前
网页都知道要双向握手才加载!从 URL 到页面渲染,单向喜欢连 DNS 都解析不通
前端·javascript·面试
太极OS23 分钟前
给 AI Skill 做 CI/CD:GitHub + ClawHub + Xiaping 同步发布实战
前端
你_好23 分钟前
Chrome 内置了 AI 工具协议?WebMCP 抢先体验 + 开源 DevTools 全解析
前端·mcp
GISer_Jing23 分钟前
LangChain.js + LangGraph.js 前端AI开发实战指南
前端·javascript·langchain
正在发育ing__27 分钟前
从源码看vue的key和状态错乱的patch
前端
木心术129 分钟前
TypeScript实战进阶:从基础类型到高级类型编程
javascript·ubuntu·typescript
Hello--_--World1 小时前
浏览器同源策略与跨域问题
javascript
yuqifang1 小时前
vue3+typescript+vite封装自己的UI组件库并上传至npm
vue.js·arkui
黄林晴1 小时前
第一次听到 Tauri 这个词,去学习一下
前端