npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gpower-rich-diff

v1.0.10

Published

可扩展的富文本对比框架,支持多引擎、多类型内容对比、多字段表单布局

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>

注意事项

  1. Tree-shaking 配置:本库已在 package.json 中设置 sideEffects: true,确保 LeaderLine 等副作用代码不被错误移除。无需额外配置。

  2. 依赖内置:所有依赖(@lionad/leader-line、diff-dom、htmldiff-js)已打包进产物,无需额外安装依赖。

  3. 调试版本:开发时可使用 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