@@ -105,16 +105,16 @@ ReactDOM.render(element, container);
105
105
如果用 ` TypeScript ` 来表示这个对象,可以写为:
106
106
107
107
``` ts
108
- export interface IFiber {
108
+ export interface IPropsFiber {
109
109
type: string ;
110
110
props: {
111
- children? : IFiber [];
111
+ children? : IPropsFiber [];
112
112
[props : string ]: any
113
113
};
114
114
}
115
115
```
116
116
117
- 注意到这里以 ` fiber ` 为类型名,这里先不展开讲 ` fiber ` , 我们后续会更详细地介绍它 。
117
+ 注意到我们的类型定义里有 ` fiber ` 这个单词,这一个重要的概念,但是这里先不展开讲,我们后续会更详细介绍 。
118
118
119
119
言归正传,` createElement ` 要做的事情很简单,就是根据入参转换出一个树结构出来,例如:
120
120
@@ -186,7 +186,7 @@ const element = {
186
186
187
187
``` ts
188
188
createElement (type , props , ... children ){
189
- // TODO
189
+ // ...
190
190
}
191
191
```
192
192
@@ -220,11 +220,149 @@ function createTextElement(text: string) {
220
220
}
221
221
```
222
222
223
- 运行 ` yarn demo01 ` 可以查看效果。
223
+ 运行 ` yarn demo02 ` 可以查看效果:
224
+
225
+ ![ ] ( ./images/02.png )
226
+
227
+ 可看到一个树状的数据结构。
228
+
229
+ 细心的同学会在 demo02 中看到 ` @jsx ` 的注释,如下:
230
+
231
+ ``` jsx
232
+ /** @jsx createElement */
233
+ const element = (< div title= ' foo' >
234
+ < span id= ' a' > hello< / span>
235
+ < span id= ' b' > world< / span>
236
+ < / div> );
237
+ ```
238
+
239
+ 解释一下,加上这行注释 parcel 就会用 babel 相应的[ 插件] ( https://www.babeljs.cn/docs/babel-plugin-transform-react-jsx ) 来解析 JSX 语法。
240
+
241
+ 总结一下:** 在第二步中,我们借助了 babel 的能力解析了 JSX 语法,然后通过我们自定义的 ` createElement ` 实现了树状结构。**
224
242
225
243
### 第三步:实现 render 方法
226
244
227
- TODO
245
+ 实现 render 方法其实非常简单:
246
+
247
+ ``` ts
248
+ function render(element , container ) {
249
+ const dom = document .createElement (element .type )
250
+ container .appendChild (dom )
251
+ }
252
+ ```
253
+
254
+ 是的,就是这个可用的 ` render ` 方法。
255
+
256
+ 但是,我们需要考虑递归的问题,因为 render 的第2个参数是一个树状的结构。
257
+
258
+ 于是我们优化如下:
259
+
260
+ ``` diff
261
+ function render(element, container) {
262
+ const dom = document.createElement(element.type)
263
+ + element.props.children.forEach(child => render(child, dom))
264
+ container.appendChild(dom)
265
+ }
266
+ ```
267
+
268
+ 考虑到 element 可能是原始数据类型,我们还需要继续优化:
269
+
270
+ ``` diff
271
+ function render(element, container) {
272
+ - const dom = document.createElement(element.type)
273
+ + const dom =
274
+ + element.type === 'TEXT_ELEMENT'
275
+ + ? document.createTextNode('')
276
+ + : document.createElement(element.type)
277
+ element.props.children.forEach(child => render(child, dom))
278
+ container.appendChild(dom)
279
+ }
280
+ ```
281
+
282
+ 另外,我们还要将树中属性写到 Dom 上:
283
+
284
+ ``` diff
285
+ function render(element, container) {
286
+ const dom =
287
+ element.type === 'TEXT_ELEMENT'
288
+ ? document.createTextNode('')
289
+ : document.createElement(element.type)
290
+
291
+ + const isProperty = key => key !== "children"
292
+ + Object.keys(element.props)
293
+ + .filter(isProperty)
294
+ + .forEach(name => {
295
+ + dom[name] = element.props[name]
296
+ + })
297
+
298
+ element.props.children.forEach(child => render(child, dom))
299
+ container.appendChild(dom)
300
+ }
301
+ ```
302
+
303
+ 注意到这里过滤了 ` children ` 属性,因为在 react 中 ` children ` 表示子元素。
304
+
305
+ 先阶段完整的 ` react ` 代码如下:
306
+
307
+ ``` ts
308
+ function createElement(type : string , props , ... children ) {
309
+ return {
310
+ type ,
311
+ props: {
312
+ ... props ,
313
+ children: children .map (child =>
314
+ typeof child === ' object'
315
+ ? child
316
+ : createTextElement (child )
317
+ )
318
+ }
319
+ };
320
+ }
321
+
322
+ function createTextElement(text : string ) {
323
+ return {
324
+ type: ' TEXT_ELEMENT' ,
325
+ props: {
326
+ nodeValue: text ,
327
+ children: []
328
+ }
329
+ };
330
+ }
331
+
332
+
333
+ function render(element , container ) {
334
+ const dom =
335
+ element .type === ' TEXT_ELEMENT'
336
+ ? document .createTextNode (' ' )
337
+ : document .createElement (element .type );
338
+
339
+ const isProperty = key => key !== ' children' ;
340
+ Object .keys (element .props )
341
+ .filter (isProperty )
342
+ .forEach (name => {
343
+ dom [name ] = element .props [name ];
344
+ });
345
+
346
+ element .props .children .forEach (child => render (child , dom ));
347
+ container .appendChild (dom );
348
+ }
349
+
350
+ /** 以下是业务代码 **/
351
+
352
+ /** @jsx createElement */
353
+ const element = (<div title = ' foo' >
354
+ <span id = ' a' >hello</span >
355
+ <span id = ' b' >world</span >
356
+ </div >);
357
+
358
+ render (element , document .getElementById (' root' ));
359
+ ` ` `
360
+
361
+ 我们可以运行 ` yarn demo03 ` 查看它的运行效果,和 react 是一样的效果。
362
+
363
+ ### 第四步
364
+
365
+ <!-- TODO -->
228
366
229
367
### 相关链接
230
368
0 commit comments