@@ -40,12 +40,116 @@ function normalizeClass(classValue) {
40
40
}
41
41
function unmount ( vnode ) {
42
42
var _a ;
43
+ if ( typeof vnode === "string" )
44
+ return ;
43
45
const parent = ( _a = vnode . el ) === null || _a === void 0 ? void 0 : _a . parentNode ;
44
46
if ( parent && vnode . el ) {
45
47
parent . removeChild ( vnode . el ) ;
46
48
}
47
49
}
50
+ function patchProps ( el , key , prevValue , nextValue ) {
51
+ // 匹配以 on 开头的属性,视其为事件
52
+ if ( / ^ o n / . test ( key ) ) {
53
+ // 定义 el._vei 为一个对象,存在事件名称到事件处理函数的映射
54
+ const invokers = el . _vei || ( el . _vei = { } ) ;
55
+ //根据事件名称获取 invoker
56
+ let invoker = invokers [ key ] ;
57
+ const name = key . slice ( 2 ) . toLowerCase ( ) ;
58
+ if ( nextValue ) {
59
+ if ( ! invoker ) {
60
+ // 如果没有 invoker,则将一个伪造的 invoker 缓存到 el._vei 中
61
+ // vei 是 vue event invoker 的首字母缩写
62
+ invoker = el . _vei [ key ] = ( e ) => {
63
+ // 如果 invoker.value 是数组,则遍历它并逐个调用事件处理函数
64
+ // 如果事件发生的时间早于事件处理函数绑定的时间,则不处理执行事件处理函数
65
+ if ( e . timeStamp < invoker . attached )
66
+ return ;
67
+ if ( Array . isArray ( invoker . value ) ) {
68
+ invoker . value . forEach ( ( fn ) => fn ( e ) ) ;
69
+ }
70
+ else {
71
+ // 否则直接作为函数调用
72
+ // 当伪造的事件处理函数执行时,会执行真正的事件处理函数
73
+ invoker . value ( e ) ;
74
+ }
75
+ } ;
76
+ // 将真正的事件处理函数赋值给 invoker.value
77
+ invoker . value = nextValue ;
78
+ // 添加 invoker.attached 属性,存储事件处理函数被绑定的时间
79
+ invoker . attached = performance . now ( ) ;
80
+ // 绑定 invoker 作为事件处理函数
81
+ el . addEventListener ( name , invoker ) ;
82
+ }
83
+ else {
84
+ // 如果 invoker 存在,意味着更新,并且只需要更新 invoker.value 的值即可
85
+ invoker . value = nextValue ;
86
+ }
87
+ }
88
+ else if ( invoker ) {
89
+ // 新的事件绑定函数不存在,且之前绑定的 invoker 存在,则移除绑定
90
+ el . removeEventListener ( name , invoker ) ;
91
+ }
92
+ }
93
+ else if ( key === 'class' ) {
94
+ el . className = nextValue || '' ;
95
+ }
96
+ else if ( shouldSetAsProps ( el , key , nextValue ) ) {
97
+ const type = typeof el [ key ] ;
98
+ if ( type === 'boolean' && nextValue === '' ) {
99
+ el [ key ] = true ;
100
+ }
101
+ else {
102
+ el [ key ] = nextValue ;
103
+ }
104
+ }
105
+ else {
106
+ el . setAttribute ( key , nextValue ) ;
107
+ }
108
+ }
48
109
function patchElement ( n1 , n2 ) {
110
+ const el = n2 . el = n1 . el ;
111
+ const oldProps = n1 . props ;
112
+ const newProps = n2 . props ;
113
+ // 第一步:更新 props
114
+ for ( const key in newProps ) {
115
+ if ( newProps [ key ] !== ( oldProps === null || oldProps === void 0 ? void 0 : oldProps [ key ] ) ) {
116
+ el && patchProps ( el , key , oldProps === null || oldProps === void 0 ? void 0 : oldProps [ key ] , newProps [ key ] ) ;
117
+ }
118
+ }
119
+ for ( const key in oldProps ) {
120
+ if ( newProps && ! ( key in newProps ) ) {
121
+ el && patchProps ( el , key , oldProps [ key ] , null ) ;
122
+ }
123
+ }
124
+ // 第二步:更新 children
125
+ patchChildren ( n1 , n2 , el ) ;
126
+ }
127
+ function patchChildren ( n1 , n2 , container ) {
128
+ // 判断新子节点的类型是否是文本节点
129
+ if ( typeof n2 . children === 'string' ) {
130
+ // 旧子节点的类型有三种可能:
131
+ // 只有当旧子节点为一组子节点时,才需要逐个卸载,其他情况下什么都不需要做
132
+ if ( Array . isArray ( n1 . children ) ) {
133
+ // 这里涉及到diff算法
134
+ n1 . children . forEach ( ( c ) => unmount ( c ) ) ;
135
+ }
136
+ // 最后将新的文本节点内容设置给容器元素
137
+ setElementText ( container , n2 . children ) ;
138
+ }
139
+ else if ( Array . isArray ( n2 . children ) ) {
140
+ // 说明新子节点是一组子节点
141
+ // 判断旧子节点是否也是一组子节点
142
+ if ( Array . isArray ( n1 . children ) ) {
143
+ // 代码运行到这里,则说明新旧子节点都是一组子节点,这里涉及核心的 Diff 算法
144
+ }
145
+ else {
146
+ // 此时:
147
+ // 旧子节点要么是文本子节点,要么不存在
148
+ // 但无论哪种情况,我们都只需要将容器清空,然后将新的一组子节点逐个挂载
149
+ setElementText ( container , '' ) ;
150
+ n2 . children . forEach ( c => patch ( null , c , container ) ) ;
151
+ }
152
+ }
49
153
}
50
154
function createRenderer ( options ) {
51
155
// 通过 options 得到操作 DOM 的 API
@@ -73,6 +177,8 @@ function createRenderer(options) {
73
177
insert ( el , container ) ;
74
178
}
75
179
function patch ( n1 , n2 , container ) {
180
+ if ( typeof n2 === 'string' )
181
+ return ;
76
182
// 如果 n1 存在,则对比 n1 和 n2 的类型
77
183
if ( n1 && n1 . type !== n2 . type ) {
78
184
// 如果新旧 vnode 的类型不同,则直接将旧 vnode 卸载
@@ -125,66 +231,10 @@ const renderer = createRenderer({
125
231
insert ( el , parent , anchor ) {
126
232
parent . insertBefore ( el , anchor ) ;
127
233
} ,
128
- // 将属性设置相关操作封装到 patchProps 函数中,并作为渲染器选项传递
129
234
patchProps ( el , key , prevValue , nextValue ) {
130
- // 匹配以 on 开头的属性,视其为事件
131
- if ( / ^ o n / . test ( key ) ) {
132
- // 定义 el._vei 为一个对象,存在事件名称到事件处理函数的映射
133
- const invokers = el . _vei || ( el . _vei = { } ) ;
134
- //根据事件名称获取 invoker
135
- let invoker = invokers [ key ] ;
136
- const name = key . slice ( 2 ) . toLowerCase ( ) ;
137
- if ( nextValue ) {
138
- if ( ! invoker ) {
139
- // 如果没有 invoker,则将一个伪造的 invoker 缓存到 el._vei 中
140
- // vei 是 vue event invoker 的首字母缩写
141
- invoker = el . _vei [ key ] = ( e ) => {
142
- // 如果 invoker.value 是数组,则遍历它并逐个调用事件处理函数
143
- // 如果事件发生的时间早于事件处理函数绑定的时间,则不处理执行事件处理函数
144
- if ( e . timeStamp < invoker . attached )
145
- return ;
146
- if ( Array . isArray ( invoker . value ) ) {
147
- invoker . value . forEach ( ( fn ) => fn ( e ) ) ;
148
- }
149
- else {
150
- // 否则直接作为函数调用
151
- // 当伪造的事件处理函数执行时,会执行真正的事件处理函数
152
- invoker . value ( e ) ;
153
- }
154
- } ;
155
- // 将真正的事件处理函数赋值给 invoker.value
156
- invoker . value = nextValue ;
157
- // 添加 invoker.attached 属性,存储事件处理函数被绑定的时间
158
- invoker . attached = performance . now ( ) ;
159
- // 绑定 invoker 作为事件处理函数
160
- el . addEventListener ( name , invoker ) ;
161
- }
162
- else {
163
- // 如果 invoker 存在,意味着更新,并且只需要更新 invoker.value 的值即可
164
- invoker . value = nextValue ;
165
- }
166
- }
167
- else if ( invoker ) {
168
- // 新的事件绑定函数不存在,且之前绑定的 invoker 存在,则移除绑定
169
- el . removeEventListener ( name , invoker ) ;
170
- }
171
- }
172
- else if ( key === 'class' ) {
173
- el . className = nextValue || '' ;
174
- }
175
- else if ( shouldSetAsProps ( el , key , nextValue ) ) {
176
- const type = typeof el [ key ] ;
177
- if ( type === 'boolean' && nextValue === '' ) {
178
- el [ key ] = true ;
179
- }
180
- else {
181
- el [ key ] = nextValue ;
182
- }
183
- }
184
- else {
185
- el . setAttribute ( key , nextValue ) ;
186
- }
235
+ patchProps ( el , key , prevValue , nextValue ) ;
187
236
}
237
+ // 将属性设置相关操作封装到 patchProps 函数中,并作为渲染器选项传递
188
238
} ) ;
189
239
// 不同事件名称的vnode
190
240
// const vnode = {
@@ -200,21 +250,23 @@ const renderer = createRenderer({
200
250
// children: 'text'
201
251
// }
202
252
// 相同事件名称不同函数的vnode
253
+ // 没有子节点
254
+ // vnode = {
255
+ // type: 'div',
256
+ // children: null
257
+ // }
258
+ // // 文本子节点
259
+ // vnode = {
260
+ // type: 'div',
261
+ // children: 'Some Text'
262
+ // }
263
+ // // 其他情况,子节点使用数组表示
203
264
const vnode = {
204
- type : 'p' ,
205
- props : {
206
- onClick : [
207
- // 第一个事件处理函数
208
- ( ) => {
209
- alert ( 'clicked 1' ) ;
210
- } ,
211
- // 第二个事件处理函数
212
- ( ) => {
213
- alert ( 'clicked 2' ) ;
214
- }
215
- ]
216
- } ,
217
- children : 'text'
265
+ type : 'div' ,
266
+ children : [
267
+ { type : 'p' } ,
268
+ 'Some Text'
269
+ ]
218
270
} ;
219
271
// 初次挂载
220
272
renderer . render ( vnode , document . querySelector ( '#app' ) ) ;
0 commit comments