vue3+diff简单实现对比差异工具组件

结果图

背景

某天要给管理后台的数据配置添加一个审核功能,为了更好的审核,就需要看到数据差异,于是就要开发一款对比差异的页面

调研

  1. 调研有没有现成的组件
  2. 评估自研成本(有AI的加入,成本大幅度降低)

现成的组件感觉有点一般,于是我打算自研,用的是diffdiffChars功能,因为要精确到字符级 ,如果是行级,可以用diffLines,更简单

diffChars需要自己还原原来的数据,最重要的还是他的精度高,可以做字符级的高亮,他的数据结构参考

json 复制代码
[
    {
        "count": 17,
        "added": false,
        "removed": false,
        "value": "{\n  \"name\": \"Alic"
    },
    ....
]

代码

技术栈:vue3、unocss

子组件--Diff.vue

html 复制代码
<script lang="ts" setup>
import type { Change } from 'diff'
import { diffChars } from 'diff'

const props = defineProps<{
  oldData: unknown
  newData: unknown
}>()

const oldStr = computed(() => `${JSON.stringify(props.oldData, null, 2)}\n`)
const newStr = computed(() => `${JSON.stringify(props.newData, null, 2)}\n`)

const diffCharList = computed<Change[]>(() => diffChars(oldStr.value, newStr.value))

interface Segment {
  text: string
  added?: boolean
  removed?: boolean
}

function buildLines() {
  const oldLines: { segments: Segment[]; changed: boolean }[] = []
  const newLines: { segments: Segment[]; changed: boolean }[] = []

  const oldCurrent: Segment[] = []
  const newCurrent: Segment[] = []

  const pushLine = () => {
    oldLines.push({
      segments: [...oldCurrent],
      changed: oldCurrent.some((s) => s.removed),
    })
    newLines.push({
      segments: [...newCurrent],
      changed: newCurrent.some((s) => s.added),
    })
    oldCurrent.length = 0
    newCurrent.length = 0
  }

  diffCharList.value.forEach((part) => {
    const lines = part.value.split(/(?<=\n)/)
    lines.forEach((line, i) => {
      const isNewLine = line.endsWith('\n')

      if (part.added) {
        newCurrent.push({ text: line, added: true })
        if (isNewLine) pushLine()
      } else if (part.removed) {
        oldCurrent.push({ text: line, removed: true })
        if (isNewLine) pushLine()
      } else {
        oldCurrent.push({ text: line })
        newCurrent.push({ text: line })
        if (isNewLine) pushLine()
      }
    })
  })

  if (oldCurrent.length || newCurrent.length) pushLine()

  return { oldLines, newLines }
}

const { oldLines, newLines } = computed(() => buildLines()).value
</script>

<template>
  <div class="font-mono text-14 lh-32 flex overflow-auto b b-gray-300 bg-white">
    <!-- 旧数据 -->
    <div class="flex-1 p-4 b-r b-gray-300 overflow-x-auto">
      <div
        v-for="(line, index) in oldLines"
        :key="'old-' + index"
        :class="{
          'bg-red-100': line.changed,
        }"
        class="whitespace-pre-wrap"
      >
        <template v-for="(segment, i) in line.segments" :key="i">
          <span
            :class="{
              'bg-red-300 line-through': segment.removed,
            }"
          >
            {{ segment.text }}
          </span>
        </template>
      </div>
    </div>
    <!-- 新数据 -->
    <div class="flex-1 p-4 overflow-x-auto">
      <div
        v-for="(line, index) in newLines"
        :key="'new-' + index"
        :class="{
          'bg-green-100': line.changed,
        }"
        class="whitespace-pre-wrap"
      >
        <template v-for="(segment, i) in line.segments" :key="i">
          <span :class="segment.added ? 'bg-green-300' : ''">
            {{ segment.text }}
          </span>
        </template>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped></style>

父组件

html 复制代码
<Diff :old-data="diffObj.oldData" :new-data="diffObj.newData" />

  diffObj.value.oldData = {
    name: 'Alice',
    age: 25,
    address: {
      city: 'New York',
      zip: '10001',
    },
    tags: ['developer', 'frontend'],
    text: 'hello world fsdkafjs撒放假就疯狂拉萨大家疯狂了发啥地方撒的',
    text2: 'hello world fsdkafjs撒放假就疯狂拉萨大家疯狂的发啥地方撒的',
  }
  diffObj.value.newData = {
    name: 'Alicia', // 修改
    age: 26, // 修改
    address: {
      city: 'San Francisco', // 修改
      zip: '94105', // 修改
    },
    tags: ['developer', 'fullstack'], // 修改数组项
    text: 'hello world fsdkafjs撒放假就疯狂拉萨大家疯狂了发啥地方撒的',
    text2: 'hello world fsdkafjs撒放假就疯狂拉萨大家疯狂了发啥地方撒的',
    active: true, // 新增字段
  }
相关推荐
YGY Webgis糕手之路2 分钟前
Cesium 快速入门(一)快速搭建项目
前端·经验分享·笔记·vue·web
im_AMBER3 分钟前
Web 开发 08
前端·javascript
前端amanda4 分钟前
uniapp中uview组件中u-input格式化后赋值踩坑
前端·javascript·uni-app
golitter.4 分钟前
python中高效构建提示词
前端·数据库·python
前端工作日常5 分钟前
我学习到的“维护推广阶段的核心目标”
前端
gyratesky5 分钟前
如何实现高性能的网格渲染和单体拾取
前端·数据可视化
冰菓Neko28 分钟前
CSS 常用属性汇总
前端·css
Moment31 分钟前
调试代码,是每个前端逃不过的必修课 😏😏😏
前端·javascript·node.js
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | 3dBackgroundBoxes(3D背景盒子组件)
前端·javascript·vue.js·3d·vue
Fantastic_sj4 小时前
CSS-in-JS 动态主题切换与首屏渲染优化
前端·javascript·css