@kaitify/core 0.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/examples/App.vue +342 -0
  4. package/examples/content.js +1 -0
  5. package/examples/main.ts +4 -0
  6. package/examples/test.html +23 -0
  7. package/lib/extensions/Extension.d.ts +172 -0
  8. package/lib/extensions/align/index.d.ts +10 -0
  9. package/lib/extensions/attachment/index.d.ts +29 -0
  10. package/lib/extensions/back-color/index.d.ts +9 -0
  11. package/lib/extensions/blockquote/index.d.ts +12 -0
  12. package/lib/extensions/bold/index.d.ts +9 -0
  13. package/lib/extensions/code/index.d.ts +12 -0
  14. package/lib/extensions/code-block/hljs.d.ts +12 -0
  15. package/lib/extensions/code-block/index.d.ts +15 -0
  16. package/lib/extensions/color/index.d.ts +9 -0
  17. package/lib/extensions/font-family/index.d.ts +9 -0
  18. package/lib/extensions/font-size/index.d.ts +9 -0
  19. package/lib/extensions/heading/index.d.ts +13 -0
  20. package/lib/extensions/history/index.d.ts +10 -0
  21. package/lib/extensions/horizontal/index.d.ts +7 -0
  22. package/lib/extensions/image/index.d.ts +26 -0
  23. package/lib/extensions/indent/index.d.ts +8 -0
  24. package/lib/extensions/index.d.ts +29 -0
  25. package/lib/extensions/italic/index.d.ts +9 -0
  26. package/lib/extensions/line-height/index.d.ts +9 -0
  27. package/lib/extensions/link/index.d.ts +27 -0
  28. package/lib/extensions/list/index.d.ts +18 -0
  29. package/lib/extensions/math/index.d.ts +11 -0
  30. package/lib/extensions/strikethrough/index.d.ts +9 -0
  31. package/lib/extensions/subscript/index.d.ts +9 -0
  32. package/lib/extensions/superscript/index.d.ts +9 -0
  33. package/lib/extensions/table/index.d.ts +21 -0
  34. package/lib/extensions/task/index.d.ts +12 -0
  35. package/lib/extensions/text/index.d.ts +14 -0
  36. package/lib/extensions/underline/index.d.ts +9 -0
  37. package/lib/extensions/video/index.d.ts +27 -0
  38. package/lib/index.d.ts +3 -0
  39. package/lib/kaitify-core.es.js +38337 -0
  40. package/lib/kaitify-core.umd.js +2 -0
  41. package/lib/model/Editor.d.ts +504 -0
  42. package/lib/model/History.d.ts +42 -0
  43. package/lib/model/KNode.d.ts +258 -0
  44. package/lib/model/Selection.d.ts +29 -0
  45. package/lib/model/config/dom-observe.d.ts +10 -0
  46. package/lib/model/config/event-handler.d.ts +33 -0
  47. package/lib/model/config/format-patch.d.ts +25 -0
  48. package/lib/model/config/format-rules.d.ts +37 -0
  49. package/lib/model/config/function.d.ts +84 -0
  50. package/lib/model/index.d.ts +6 -0
  51. package/lib/tools/index.d.ts +49 -0
  52. package/lib/view/index.d.ts +21 -0
  53. package/lib/view/js-render/dom-patch.d.ts +65 -0
  54. package/lib/view/js-render/index.d.ts +5 -0
  55. package/package.json +52 -0
  56. package/src/css/style.less +56 -0
  57. package/src/css/var.less +45 -0
  58. package/src/extensions/Extension.ts +200 -0
  59. package/src/extensions/align/index.ts +115 -0
  60. package/src/extensions/attachment/icon.svg +1 -0
  61. package/src/extensions/attachment/index.ts +293 -0
  62. package/src/extensions/attachment/style.less +25 -0
  63. package/src/extensions/back-color/index.ts +56 -0
  64. package/src/extensions/blockquote/index.ts +144 -0
  65. package/src/extensions/blockquote/style.less +16 -0
  66. package/src/extensions/bold/index.ts +77 -0
  67. package/src/extensions/code/index.ts +295 -0
  68. package/src/extensions/code/style.less +14 -0
  69. package/src/extensions/code-block/hljs.less +183 -0
  70. package/src/extensions/code-block/hljs.ts +95 -0
  71. package/src/extensions/code-block/index.ts +308 -0
  72. package/src/extensions/code-block/style.less +20 -0
  73. package/src/extensions/color/index.ts +56 -0
  74. package/src/extensions/font-family/index.ts +80 -0
  75. package/src/extensions/font-size/index.ts +56 -0
  76. package/src/extensions/heading/index.ts +164 -0
  77. package/src/extensions/heading/style.less +42 -0
  78. package/src/extensions/history/index.ts +96 -0
  79. package/src/extensions/horizontal/index.ts +45 -0
  80. package/src/extensions/horizontal/style.less +13 -0
  81. package/src/extensions/image/index.ts +242 -0
  82. package/src/extensions/image/style.less +8 -0
  83. package/src/extensions/indent/index.ts +98 -0
  84. package/src/extensions/index.ts +29 -0
  85. package/src/extensions/italic/index.ts +77 -0
  86. package/src/extensions/line-height/index.ts +113 -0
  87. package/src/extensions/link/index.ts +184 -0
  88. package/src/extensions/link/style.less +19 -0
  89. package/src/extensions/list/index.ts +410 -0
  90. package/src/extensions/list/style.less +19 -0
  91. package/src/extensions/math/index.ts +233 -0
  92. package/src/extensions/math/style.less +21 -0
  93. package/src/extensions/strikethrough/index.ts +78 -0
  94. package/src/extensions/subscript/index.ts +77 -0
  95. package/src/extensions/superscript/index.ts +77 -0
  96. package/src/extensions/table/index.ts +1148 -0
  97. package/src/extensions/table/style.less +71 -0
  98. package/src/extensions/task/index.ts +243 -0
  99. package/src/extensions/task/style.less +59 -0
  100. package/src/extensions/text/index.ts +359 -0
  101. package/src/extensions/underline/index.ts +78 -0
  102. package/src/extensions/video/index.ts +273 -0
  103. package/src/extensions/video/style.less +8 -0
  104. package/src/index.ts +9 -0
  105. package/src/model/Editor.ts +1963 -0
  106. package/src/model/History.ts +115 -0
  107. package/src/model/KNode.ts +677 -0
  108. package/src/model/Selection.ts +39 -0
  109. package/src/model/config/dom-observe.ts +184 -0
  110. package/src/model/config/event-handler.ts +237 -0
  111. package/src/model/config/format-patch.ts +215 -0
  112. package/src/model/config/format-rules.ts +218 -0
  113. package/src/model/config/function.ts +1018 -0
  114. package/src/model/index.ts +6 -0
  115. package/src/tools/index.ts +156 -0
  116. package/src/view/index.ts +46 -0
  117. package/src/view/js-render/dom-patch.ts +324 -0
  118. package/src/view/js-render/index.ts +210 -0
  119. package/vite-env.d.ts +2 -0
@@ -0,0 +1,6 @@
1
+ export type * from './config/format-patch'
2
+ export type * from './config/format-rules'
3
+ export * from './Editor'
4
+ export * from './History'
5
+ export * from './KNode'
6
+ export * from './Selection'
@@ -0,0 +1,156 @@
1
+ import { data as DapData, element as DapElement } from 'dap-util'
2
+ import { KNodeMarksType, KNodeStylesType } from '@/model'
3
+ import { NODE_MARK } from '@/view'
4
+
5
+ /**
6
+ * 用于KNode生成唯一的key
7
+ */
8
+ export const createUniqueKey = (): number => {
9
+ let key = DapData.get(window, 'kaitify-node-key') || 0
10
+ key++
11
+ DapData.set(window, 'kaitify-node-key', key)
12
+ return key
13
+ }
14
+
15
+ /**
16
+ * 用于编辑器生成唯一的guid
17
+ */
18
+ export const createGuid = function (): number {
19
+ //获取唯一id
20
+ let key = DapData.get(window, 'kaitify-guid') || 0
21
+ key++
22
+ DapData.set(window, 'kaitify-guid', key)
23
+ return key
24
+ }
25
+
26
+ /**
27
+ * 判断字符串是否零宽度无断空白字符
28
+ */
29
+ export const isZeroWidthText = (val: string) => {
30
+ return /^[\uFEFF]+$/g.test(val)
31
+ }
32
+
33
+ /**
34
+ * 获取一个零宽度无断空白字符
35
+ */
36
+ export const getZeroWidthText = () => {
37
+ return '\uFEFF'
38
+ }
39
+
40
+ /**
41
+ * 驼峰转中划线
42
+ */
43
+ export const camelToKebab = (val: string) => {
44
+ return val.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
45
+ }
46
+
47
+ /**
48
+ * 中划线转驼峰
49
+ */
50
+ export const kebabToCamel = (val: string) => {
51
+ return val.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
52
+ }
53
+
54
+ /**
55
+ * 获取dom元素的属性集合
56
+ */
57
+ export const getDomAttributes = (dom: HTMLElement) => {
58
+ let o: KNodeMarksType = {}
59
+ const length = dom.attributes.length
60
+ for (let i = 0; i < length; i++) {
61
+ const attribute = dom.attributes[i]
62
+ const regExp = new RegExp(`(^on)|(^style$)|(^${NODE_MARK}$)`, 'g')
63
+ //匹配事件、样式和face外的属性
64
+ if (!regExp.test(attribute.nodeName)) {
65
+ o[attribute.nodeName] = attribute.nodeValue || ''
66
+ }
67
+ }
68
+ return o
69
+ }
70
+
71
+ /**
72
+ * 获取dom元素的样式集合
73
+ */
74
+ export const getDomStyles = (dom: HTMLElement) => {
75
+ let o: KNodeStylesType = {}
76
+ const styles = dom.getAttribute('style')
77
+ if (styles) {
78
+ let i = 0
79
+ let start = 0
80
+ let splitStyles = []
81
+ while (i < styles.length) {
82
+ if (styles[i] == ';' && styles.substring(i + 1, i + 8) != 'base64,') {
83
+ splitStyles.push(styles.substring(start, i))
84
+ start = i + 1
85
+ }
86
+ //到最后了,并且最后没有分号
87
+ if (i == styles.length - 1 && start < i + 1) {
88
+ splitStyles.push(styles.substring(start, i + 1))
89
+ }
90
+ i++
91
+ }
92
+ splitStyles.forEach(style => {
93
+ const index = style.indexOf(':')
94
+ const property = style.substring(0, index).trim()
95
+ const value = style.substring(index + 1).trim()
96
+ o[kebabToCamel(property)] = value
97
+ })
98
+ }
99
+ return o
100
+ }
101
+
102
+ /**
103
+ * 初始化编辑器dom
104
+ */
105
+ export const initEditorDom = (dom: HTMLElement | string) => {
106
+ //判断是否字符串,如果是字符串按照选择器来寻找元素
107
+ if (typeof dom == 'string' && dom) {
108
+ dom = document.body.querySelector(dom) as HTMLElement
109
+ }
110
+ dom = dom as HTMLElement
111
+ //如何node不是元素则抛出异常
112
+ if (!DapElement.isElement(dom)) {
113
+ throw new Error('You must specify a dom container to initialize the editor')
114
+ }
115
+ //如果已经初始化过了则抛出异常
116
+ if (DapData.get(dom, 'kaitify-init')) {
117
+ throw new Error('The element node has been initialized to the editor')
118
+ }
119
+ //添加初始化的标记
120
+ DapData.set(dom, 'kaitify-init', true)
121
+ return dom
122
+ }
123
+
124
+ /**
125
+ * 判断某个dom是否包含另一个dom
126
+ */
127
+ export const isContains = (parent: Node, child: Node) => {
128
+ if (child.nodeType == 3) {
129
+ return DapElement.isContains(parent as HTMLElement, child.parentNode as HTMLElement)
130
+ }
131
+ return DapElement.isContains(parent as HTMLElement, child as HTMLElement)
132
+ }
133
+
134
+ /**
135
+ * 延迟指定时间
136
+ */
137
+ export const delay = (num: number | undefined = 0) => {
138
+ return new Promise<void>(resolve => {
139
+ setTimeout(() => {
140
+ resolve()
141
+ }, num)
142
+ })
143
+ }
144
+
145
+ /**
146
+ * 删除对象的某个属性
147
+ */
148
+ export const deleteProperty = <T>(val: any, propertyName: string) => {
149
+ const newObj: any = {}
150
+ Object.keys(val).forEach(key => {
151
+ if (key != propertyName) {
152
+ newObj[key] = val[key]
153
+ }
154
+ })
155
+ return newObj as T
156
+ }
@@ -0,0 +1,46 @@
1
+ import { common as DapCommon } from 'dap-util'
2
+ import { Editor, KNode, KNodeMarksType, KNodeStylesType } from '@/model'
3
+
4
+ /**
5
+ * 渲染参数类型
6
+ */
7
+ export type KNodeRenderOptionType = {
8
+ key: number
9
+ tag: string
10
+ attrs: KNodeMarksType
11
+ styles: KNodeStylesType
12
+ namespace?: string
13
+ textContent?: string
14
+ children?: KNodeRenderOptionType[]
15
+ }
16
+
17
+ /**
18
+ * 节点渲染成dom后在dom上生成的一个特殊标记名称,它的值是节点的key值
19
+ */
20
+ export const NODE_MARK = 'kaitify-node'
21
+
22
+ /**
23
+ * 获取节点的渲染参数
24
+ */
25
+ export const getNodeRenderOptions = (editor: Editor, node: KNode): KNodeRenderOptionType => {
26
+ //文本节点
27
+ if (node.isText()) {
28
+ return {
29
+ key: node.key,
30
+ tag: editor.textRenderTag,
31
+ namespace: node.namespace,
32
+ attrs: node.hasMarks() ? DapCommon.clone({ ...node.marks, [NODE_MARK]: node.key }) : { [NODE_MARK]: node.key },
33
+ styles: node.hasStyles() ? DapCommon.clone(node.styles) : {},
34
+ textContent: node.textContent
35
+ }
36
+ }
37
+ //其他节点
38
+ return {
39
+ key: node.key,
40
+ tag: node.tag!,
41
+ namespace: node.namespace,
42
+ attrs: node.hasMarks() ? DapCommon.clone({ ...node.marks, [NODE_MARK]: node.key }) : { [NODE_MARK]: node.key },
43
+ styles: node.hasStyles() ? DapCommon.clone(node.styles) : {},
44
+ children: node.hasChildren() ? node.children!.map(item => getNodeRenderOptions(editor, item)) : []
45
+ }
46
+ }
@@ -0,0 +1,324 @@
1
+ import { KNode, KNodeMarksType, KNodeStylesType } from '@/model'
2
+
3
+ /**
4
+ * 节点数组比对结果类型
5
+ */
6
+ type NodePatchResultType = {
7
+ /**
8
+ * 差异类型:insert:插入节点;remove:移除节点;update:节点更新;replace:节点被替换;move:节点同级位置移动
9
+ */
10
+ type: 'insert' | 'remove' | 'update' | 'replace' | 'move'
11
+ /**
12
+ * 新节点
13
+ */
14
+ newNode: KNode | null
15
+ /**
16
+ * 旧节点
17
+ */
18
+ oldNode: KNode | null
19
+ /**
20
+ * 更新的字段
21
+ */
22
+ update?: 'textContent' | 'styles' | 'marks'
23
+ }
24
+
25
+ /**
26
+ * mark比对结果类型
27
+ */
28
+ export type MarkPatchResultType = {
29
+ /**
30
+ * 新增和更新的标记
31
+ */
32
+ addMarks: KNodeMarksType
33
+ /**
34
+ * 移除的标记
35
+ */
36
+ removeMarks: KNodeMarksType
37
+ }
38
+
39
+ /**
40
+ * style比对结果类型
41
+ */
42
+ export type StylePatchResultType = {
43
+ /**
44
+ * 新增和更新的样式
45
+ */
46
+ addStyles: KNodeStylesType
47
+ /**
48
+ * 移除的样式
49
+ */
50
+ removeStyles: KNodeStylesType
51
+ }
52
+
53
+ /**
54
+ * 获取两个节点上不相同的marks
55
+ */
56
+ export const getDifferentMarks = (newNode: KNode, oldNode: KNode): MarkPatchResultType => {
57
+ //新增的标记
58
+ const addMarks: KNodeMarksType = {}
59
+ //移除的标记
60
+ const removeMarks: KNodeMarksType = {}
61
+
62
+ if (newNode.hasMarks()) {
63
+ Object.keys(newNode.marks!).forEach(mark => {
64
+ if (oldNode.hasMarks() && oldNode.marks!.hasOwnProperty(mark) && oldNode.marks![mark] === newNode.marks![mark]) {
65
+ return
66
+ }
67
+ addMarks[mark] = newNode.marks![mark]
68
+ })
69
+ }
70
+ if (oldNode.hasMarks()) {
71
+ Object.keys(oldNode.marks!).forEach(mark => {
72
+ if (newNode.hasMarks() && newNode.marks!.hasOwnProperty(mark)) {
73
+ return
74
+ }
75
+ removeMarks[mark] = oldNode.marks![mark]
76
+ })
77
+ }
78
+ return { addMarks, removeMarks }
79
+ }
80
+
81
+ /**
82
+ * 获取两个节点上不相同的styles
83
+ */
84
+ export const getDifferentStyles = (newNode: KNode, oldNode: KNode): StylePatchResultType => {
85
+ //新增的样式
86
+ const addStyles: KNodeStylesType = {}
87
+ //移除的样式
88
+ const removeStyles: KNodeStylesType = {}
89
+
90
+ if (newNode.hasStyles()) {
91
+ Object.keys(newNode.styles!).forEach(style => {
92
+ if (oldNode.hasStyles() && oldNode.styles!.hasOwnProperty(style) && oldNode.styles![style] === newNode.styles![style]) {
93
+ return
94
+ }
95
+ addStyles[style] = newNode.styles![style]
96
+ })
97
+ }
98
+ if (oldNode.hasStyles()) {
99
+ Object.keys(oldNode.styles!).forEach(style => {
100
+ if (newNode.hasStyles() && newNode.styles!.hasOwnProperty(style)) {
101
+ return
102
+ }
103
+ removeStyles[style] = oldNode.styles![style]
104
+ })
105
+ }
106
+ return { addStyles, removeStyles }
107
+ }
108
+
109
+ /**
110
+ * 对新旧两个节点数组进行比对
111
+ */
112
+ export const patchNodes = (newNodes: KNode[], oldNodes: (KNode | null)[]) => {
113
+ //两个数组都为空,无需操作
114
+ if (newNodes.length == 0 && oldNodes.length == 0) {
115
+ return []
116
+ }
117
+ //旧节点全部移除
118
+ if (newNodes.length === 0) {
119
+ return oldNodes
120
+ .filter(node => !node)
121
+ .map(oldNode => ({
122
+ type: 'remove',
123
+ oldNode,
124
+ newNode: null
125
+ })) as NodePatchResultType[]
126
+ }
127
+ //新节点全部插入
128
+ if (oldNodes.length === 0) {
129
+ return newNodes.map(newNode => ({
130
+ type: 'insert',
131
+ newNode,
132
+ oldNode: null
133
+ })) as NodePatchResultType[]
134
+ }
135
+ //比对结果数组
136
+ const result: NodePatchResultType[] = []
137
+ // 创建一个 Map 存储旧节点 key 对应的索引,提升查找效率
138
+ const oldKeyMap = new Map<number, number>()
139
+ oldNodes.forEach((node, index) => {
140
+ if (node) {
141
+ oldKeyMap.set(node.key, index)
142
+ }
143
+ })
144
+ // 双端遍历
145
+ let newStartIndex = 0
146
+ let oldStartIndex = 0
147
+ let newEndIndex = newNodes.length - 1
148
+ let oldEndIndex = oldNodes.length - 1
149
+ while (newStartIndex <= newEndIndex && oldStartIndex <= oldEndIndex) {
150
+ const newStartNode = newNodes[newStartIndex]
151
+ const oldStartNode = oldNodes[oldStartIndex]
152
+ const newEndNode = newNodes[newEndIndex]
153
+ const oldEndNode = oldNodes[oldEndIndex]
154
+ //跳过已被处理的旧节点
155
+ if (!oldStartNode) {
156
+ oldStartIndex++
157
+ }
158
+ //跳过已被处理的旧节点
159
+ else if (!oldEndNode) {
160
+ oldEndIndex--
161
+ }
162
+ //起始节点 key 匹配,进行比对
163
+ else if (newStartNode.key == oldStartNode!.key) {
164
+ result.push(...patchNode(newStartNode, oldStartNode!))
165
+ newStartIndex++
166
+ oldStartIndex++
167
+ }
168
+ //终点节点 key 匹配,进行比对
169
+ else if (newEndNode.key == oldEndNode!.key) {
170
+ result.push(...patchNode(newEndNode, oldEndNode!))
171
+ newEndIndex--
172
+ oldEndIndex--
173
+ }
174
+ //新起点和旧终点匹配,说明节点被移动
175
+ else if (newStartNode.key == oldEndNode!.key) {
176
+ result.push(
177
+ {
178
+ type: 'move',
179
+ newNode: newStartNode,
180
+ oldNode: oldEndNode
181
+ },
182
+ ...patchNode(newStartNode, oldEndNode!)
183
+ )
184
+ newStartIndex++
185
+ oldEndIndex--
186
+ }
187
+ //新终点和旧起点匹配,说明节点被移动
188
+ else if (newEndNode.key == oldStartNode!.key) {
189
+ result.push(
190
+ {
191
+ type: 'move',
192
+ newNode: newEndNode,
193
+ oldNode: oldStartNode
194
+ },
195
+ ...patchNode(newEndNode, oldStartNode!)
196
+ )
197
+ newEndIndex--
198
+ oldStartIndex++
199
+ }
200
+ //其他情况
201
+ else {
202
+ //查找新起点节点在旧节点数组中的位置
203
+ const idxInOld = oldKeyMap.get(newStartNode.key)
204
+ if (idxInOld !== undefined) {
205
+ //说明找到了同key节点,进行移动
206
+ result.push(
207
+ {
208
+ type: 'move',
209
+ newNode: newStartNode,
210
+ oldNode: oldNodes[idxInOld]
211
+ },
212
+ ...patchNode(newStartNode, oldNodes[idxInOld]!)
213
+ )
214
+ //标记节点已处理
215
+ oldNodes[idxInOld] = null
216
+ } else {
217
+ //没有找到相同 key,则是新插入的节点
218
+ result.push({
219
+ type: 'insert',
220
+ newNode: newStartNode,
221
+ oldNode: null
222
+ })
223
+ }
224
+ newStartIndex++
225
+ }
226
+ }
227
+ //处理剩余的新节点(全部为插入)
228
+ while (newStartIndex <= newEndIndex) {
229
+ result.push({
230
+ type: 'insert',
231
+ newNode: newNodes[newStartIndex],
232
+ oldNode: null
233
+ })
234
+ newStartIndex++
235
+ }
236
+ //处理剩余的旧节点(全部为移除)
237
+ while (oldStartIndex <= oldEndIndex) {
238
+ if (oldNodes[oldStartIndex]) {
239
+ result.push({
240
+ type: 'remove',
241
+ oldNode: oldNodes[oldStartIndex]!,
242
+ newNode: null
243
+ })
244
+ }
245
+ oldStartIndex++
246
+ }
247
+ return result
248
+ }
249
+
250
+ /**
251
+ * 对新旧两个节点进行比对
252
+ */
253
+ export const patchNode = (newNode: KNode, oldNode: KNode) => {
254
+ //比对结果数组
255
+ const result: NodePatchResultType[] = []
256
+ //namespace变更
257
+ if (newNode.namespace != oldNode.namespace) {
258
+ result.push({
259
+ type: 'replace',
260
+ newNode,
261
+ oldNode
262
+ })
263
+ }
264
+ //非文本节点的tag变化
265
+ else if (!newNode.isText() && newNode.tag != oldNode.tag) {
266
+ result.push({
267
+ type: 'replace',
268
+ newNode,
269
+ oldNode
270
+ })
271
+ }
272
+ //新节点有子节点而旧节点没有
273
+ else if (newNode.hasChildren() && !oldNode.hasChildren()) {
274
+ result.push({
275
+ type: 'replace',
276
+ newNode,
277
+ oldNode
278
+ })
279
+ }
280
+ //旧节点有子节点而新节点没有
281
+ else if (oldNode.hasChildren() && !newNode.hasChildren()) {
282
+ result.push({
283
+ type: 'replace',
284
+ newNode,
285
+ oldNode
286
+ })
287
+ }
288
+ //以下是更新节点的情况
289
+ else {
290
+ //文本节点的textContent变更
291
+ if (newNode.isText() && newNode.textContent != oldNode.textContent) {
292
+ result.push({
293
+ type: 'update',
294
+ oldNode,
295
+ newNode,
296
+ update: 'textContent'
297
+ })
298
+ }
299
+ //节点的styles变更
300
+ if (!newNode.isEqualStyles(oldNode)) {
301
+ result.push({
302
+ type: 'update',
303
+ newNode,
304
+ oldNode,
305
+ update: 'styles'
306
+ })
307
+ }
308
+ //节点的marks变更
309
+ if (!newNode.isEqualMarks(oldNode)) {
310
+ result.push({
311
+ type: 'update',
312
+ newNode,
313
+ oldNode,
314
+ update: 'marks'
315
+ })
316
+ }
317
+ //子节点
318
+ if (newNode.hasChildren() && oldNode.hasChildren()) {
319
+ result.push(...patchNodes(newNode.children!, oldNode.children!))
320
+ }
321
+ }
322
+
323
+ return result
324
+ }