gpower-rich-diff
v1.0.10
Published
可扩展的富文本对比框架,支持多引擎、多类型内容对比、多字段表单布局
Maintainers
Readme
gpower-rich-diff
可扩展的富文本对比框架,支持 HTML、文本、媒体资源的差异对比与可视化。
特性
- 多引擎支持:内置
htmldiff和diffdom两种对比引擎 - ShadowDOM 兼容:自动降级兼容不支持 ShadowDOM 的浏览器
- 结构化标记:提取差异标记,便于可视化展示
- LeaderLine 连线:内置连线功能,支持标记与内容的关联展示
- 自定义渲染器:可替换默认渲染模板
- 多字段对比:支持表单布局的多字段对比展示
- 自定义字段对比:支持动态字段数组的对比,包括文本、图片、视频、文件等类型
安装
npm install gpower-rich-diff使用方式
本库提供 ESM 和 UMD 两种格式,适应不同的使用场景:
方式一:ESM 格式(推荐)
适用于现代打包工具(Webpack、Vite、Rollup 等)和浏览器原生模块:
// 使用打包工具(Webpack/Vite 等)
import { render, diffHTML, diffText } from 'gpower-rich-diff'
// 浏览器原生模块
<script type="module">
import { render } from './node_modules/gpower-rich-diff/dist/rich-diff.esm.js'
render({ ... })
</script>特点:
- 支持 tree-shaking(已配置
sideEffects: true,确保 LeaderLine 正常初始化) - 更小的最终打包体积
- 现代浏览器原生支持
- 构建产物:
dist/rich-diff.esm.js(压缩版)、dist/rich-diff.dev.js(调试版)
方式二:UMD 格式
适用于传统 <script> 标签引入或旧环境:
<!-- 直接引入 UMD 文件 -->
<script src="./node_modules/gpower-rich-diff/dist/rich-diff.umd.js"></script>
<script>
// 全局变量 RichDiff
const result = RichDiff.render({ ... })
</script>特点:
- 暴露全局变量
RichDiff - 兼容 AMD、CommonJS 和浏览器全局变量
- 无需构建工具即可使用
- 构建产物:
dist/rich-diff.umd.js
CDN 引入(推荐用于演示)
<!-- 使用 unpkg CDN -->
<script src="https://unpkg.com/gpower-rich-diff/dist/rich-diff.umd.js"></script>
<script>
const result = RichDiff.render({ ... })
</script>
<!-- 或使用 ESM 模块 -->
<script type="module">
import { render } from 'https://unpkg.com/gpower-rich-diff/dist/rich-diff.esm.js'
render({ ... })
</script>注意事项
Tree-shaking 配置:本库已在
package.json中设置sideEffects: true,确保 LeaderLine 等副作用代码不被错误移除。无需额外配置。依赖内置:所有依赖(
@lionad/leader-line、diff-dom、htmldiff-js)已打包进产物,无需额外安装依赖。调试版本:开发时可使用
dist/rich-diff.dev.js(未压缩,保留调试信息):import { render } from 'gpower-rich-diff/dist/rich-diff.dev.js'
快速开始
纯文本对比
import { diffText } from 'gpower-rich-diff'
const result = diffText('旧标题', '新标题', 'title')
console.log(result.html) // '<del>旧</del><ins>新</ins>标题'
console.log(result.useShadowDOM) // true/false
console.log(result.markers) // []HTML/富文本对比
import { diffHTML } from 'gpower-rich-diff'
const oldHtml = '<p>这是<strong>旧内容</strong></p>'
const newHtml = '<p>这是<em>新内容</em></p>'
const result = diffHTML(oldHtml, newHtml, 'content')
console.log(result.html)媒体资源对比
import { diffMediaList } from 'gpower-rich-diff'
const oldMedia = [
{ fileValue: 'img1.jpg', title: '图片1' },
{ fileValue: 'img2.jpg', title: '图片2' }
]
const newMedia = [
{ fileValue: 'img1.jpg', title: '图片1已修改' },
{ fileValue: 'img3.jpg', title: '新图片' }
]
const { newList, oldList } = diffMediaList(oldMedia, newMedia, 'fileValue')
// newList 中每项带有 diffType: 'add' | 'del' | 'alter' | 'same'
newList.forEach(item => {
console.log(item.fileValue, item.diffType)
})使用渲染器
import { render } from 'gpower-rich-diff'
// 方式一:多字段对比(推荐)
// 传入数据对象和字段配置,自动渲染表单布局
const result = render({
container: document.getElementById('diffContainer'),
oldData: {
title: '旧标题',
subtitle: '旧副标题',
content: '<p>旧内容</p>'
},
newData: {
title: '新标题',
subtitle: '新副标题',
content: '<p>新内容</p>'
},
fields: [
{ label: '标题', field: 'title', sort: 1 },
{ label: '副标题', field: 'subtitle', sort: 2 },
{ label: '内容', field: 'content', sort: 3 }
]
})
// 返回值包含:
// - container: 渲染容器
// - fieldResults: 各字段的对比结果
// - markers: 所有变更标记
// - destroy: 销毁方法
// 方式二:单字段对比(兼容旧 API)
const result = render({
container: document.getElementById('diffContainer'),
oldContent: '<p>旧内容</p>',
newContent: '<p>新内容</p>',
fieldName: 'content'
})
// 不传容器返回原始数据
const rawData = render({
oldContent: '<p>旧内容</p>',
newContent: '<p>新内容</p>'
})多字段对比特性
- 表单布局:以左侧标签、右侧内容的形式展示各字段
- 自动排序:按
fields中的sort属性排序 - 智能检测:自动判断字段内容是否为 HTML(使用
diffHTML或diffText) - 标记合并:相邻的 add/del 标记自动合并为 modify 类型
- 精准定位:LeaderLine 连线指向具体的
<ins>/<del>节点,而非字段容器 - 交互反馈:悬停标记时自动高亮对应字段并显示连线
自定义字段对比
支持动态字段数组的对比,适用于内容管理系统、表单系统等场景:
import { render } from 'gpower-rich-diff'
const fields = [
{ field: 'title', type: 'text', label: '标题' },
{ field: 'customFieldMap', type: 'dynamic', label: '自定义字段' }
]
const oldData = {
title: '标题',
customFieldMap: [
{ type: 'text', label: '摘要', value: '<p>旧摘要</p>' },
{ type: 'image', label: '封面图', value: 'https://example.com/old.jpg' },
{ type: 'file', label: '附件', value: ['file1.rar', 'file2.pdf'] }
]
}
const newData = {
title: '新标题',
customFieldMap: [
{ type: 'text', label: '摘要', value: '<p>新摘要</p>' },
{ type: 'image', label: '封面图', value: 'https://example.com/new.jpg' },
{ type: 'file', label: '附件', value: ['file3.png', 'file2.pdf'] }
]
}
render({
container: document.getElementById('diffContainer'),
oldData,
newData,
fields
})支持的字段类型:
| 类型 | 说明 | 渲染效果 |
|------|------|---------|
| text | 文本内容 | 使用 diffText/diffHTML 对比 |
| image | 图片 URL | 缩略图,点击预览大图 |
| video | 视频 URL | 文件名 + 图标,点击播放 |
| file | 文件 URL | 文件名 + 下载链接 |
特性:
- 按
label字段匹配新旧数据项 - 数组值自动逐项对比,标记新增和删除
- 图片/视频支持点击预览
- 文件支持点击下载
详细文档请参考 自定义字段对比功能
提取变更标记
import { diffHTML, generateMarkers } from 'gpower-rich-diff'
const result = diffHTML(oldHtml, newHtml, 'content')
const markers = generateMarkers({
diffed: { content: result.html },
current: { content: newHtml },
last: { content: oldHtml }
})
markers.forEach(marker => {
console.log(marker.field) // 字段名
console.log(marker.changeType) // 'add' | 'del' | 'modify'
console.log(marker.oldValue) // 旧值
console.log(marker.newValue) // 新值
})LeaderLine 连线
import { createConnector, clearConnectors } from 'gpower-rich-diff'
// 创建连线
const connector = createConnector(startElement, endElement, {
color: '#1878E8',
size: 2,
path: 'fluid'
})
// 清除所有连线
clearConnectors()API 文档
核心方法
| 方法 | 说明 | 参数 | 返回值 |
|------|------|------|--------|
| diffText(oldText, newText, fieldName) | 纯文本对比 | oldText: 旧文本newText: 新文本fieldName: 字段名 | { html, useShadowDOM, markers } |
| diffHTML(oldHtml, newHtml, fieldName) | HTML 对比 | oldHtml: 旧 HTMLnewHtml: 新 HTMLfieldName: 字段名 | { html, useShadowDOM, markers } |
| diffMediaList(oldList, newList, idField) | 媒体对比 | oldList: 旧列表newList: 新列表idField: 标识字段 | { newList, oldList } |
| generateMarkers(options) | 提取标记 | options.diffed: 差异结果options.current: 当前数据options.last: 旧数据 | Marker[] |
引擎控制
| 方法 | 说明 | 参数 |
|------|------|------|
| setEngine(engine) | 切换引擎 | 'htmldiff' 或 'diffdom' |
| getEngineName() | 获取当前引擎名 | - |
| getAvailableEngines() | 获取可用引擎列表 | - |
渲染器
| 方法 | 说明 | 参数 |
|------|------|------|
| render(options) | 渲染差异 | container: 容器元素oldContent: 旧内容newContent: 新内容fieldName: 字段名 |
| setRenderer(renderer) | 设置自定义渲染器 | 渲染器对象 |
| getRenderer() | 获取当前渲染器 | - |
LeaderLine
| 方法 | 说明 |
|------|------|
| createConnector(start, end, options) | 创建连线 |
| removeConnector(id) | 移除指定连线 |
| clearConnectors() | 清除所有连线 |
| updateConnector(id) | 更新连线位置 |
| updateAllConnectors() | 更新所有连线位置 |
| getConnectorCount() | 获取连线数量 |
| isLeaderLineAvailable() | 检查 LeaderLine 是否可用 |
自定义渲染器
import { setRenderer } from 'gpower-rich-diff'
const myRenderer = {
render({ container, oldContent, newContent, diffResult, config }) {
// 自定义渲染逻辑
container.innerHTML = diffResult.html
return { success: true }
}
}
setRenderer(myRenderer)样式对比
基本用法
import { diffStyle } from 'gpower-rich-diff'
const oldHtml = '<p style="color:red;font-size:14px">内容</p>'
const newHtml = '<p style="color:blue;font-size:16px">内容</p>'
const result = diffStyle(oldHtml, newHtml)
console.log(result.hasStyleDiff) // true
console.log(result.styleChanges) // [...]结合 render 使用
import { render } from 'gpower-rich-diff'
render({
container: document.getElementById('diffContainer'),
oldData: { content: oldHtml },
newData: { content: newHtml },
fields: [{ label: '内容', field: 'content', sort: 1 }],
detectStyle: true, // 启用样式检测
styleDiffConfig: {
matchStrategy: 'auto' // 元素匹配策略
}
})样式配置选项
{
detectStyle: true, // 是否检测样式变更
styleDiffConfig: {
matchStrategy: 'auto', // 'id' | 'path' | 'auto'
ignoreProperties: ['opacity'] // 忽略的属性
}
}弹窗交互
当鼠标悬浮在有样式变更的 marker 上时,会自动显示弹窗:
- 左侧展示旧版本的 HTML 渲染效果
- 右侧展示新版本的 HTML 渲染效果
- 底部列出具体的属性变更
样式对比 API
| 方法 | 说明 | 参数 | 返回值 |
|------|------|------|--------|
| diffStyle(oldHtml, newHtml, options) | 对比 HTML 样式差异 | oldHtml: 旧 HTMLnewHtml: 新 HTMLoptions: 配置选项 | { hasStyleDiff, styleChanges } |
| parseStyle(styleString) | 解析样式字符串 | styleString: 样式字符串 | { property: value } |
| compareStyles(oldStyle, newStyle) | 对比两个样式对象 | oldStyle: 旧样式newStyle: 新样式 | { added, removed, changed } |
自定义对比引擎
import { setEngine } from 'gpower-rich-diff'
// 使用内置引擎
setEngine('htmldiff') // 默认,基于 htmldiff-js
setEngine('diffdom') // 基于 diff-dom
// 或注册自定义引擎(需实现 Engine 接口)浏览器支持
- Chrome 60+
- Firefox 63+
- Safari 13+
- Edge 79+
不支持 ShadowDOM 的浏览器会自动降级为 <difftext> 标签。
依赖说明
htmldiff-js- HTML 对比引擎diff-dom- DOM 对比引擎@lionad/leader-line- SVG 连线库
开发
# 安装依赖
npm install
# 构建
npm run build
# 构建产物
# - dist/rich-diff.esm.js (ESM 格式)
# - dist/rich-diff.umd.js (UMD 格式)
# - dist/rich-diff.dev.js (未压缩,调试用)License
MIT
