飞雪连天射白鹿,笑书神侠倚碧鸳

0%

33行实现react

一种简易的类react架构的实现,了解react,
全文添加中文注释,方便理解

  • 取得状态并返回虚拟dom
  • 状态改变,返回新的虚拟dom
  • 将虚拟dom渲染为真实dom
  • 更新真实dom

前置知识点

  • Mithril 现代化的 JS 轻量框架,内置了路由和 XHR 工具
    • m() 函数可以描述任何 HTML 结构
    • m.render(root, ‘’) 挂载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
const m = (...args)=>{
// 结构args => tag, attrs, classes
let [attrs, [head, ...tail]] = [{}, args]
let [tag, ...classes] = head.split('.')
if (tail.length && !m.isRenderable(tail[0])) [attrs, ...tail] = tail
// 合并class
if (attrs.class) classes = [...classes, ...attrs.class]
// 把已经使用的attrs做浅拷贝,删除class的值
attrs = {...attrs}; delete attrs.class
// 创建子数组,递归地将嵌套项拍平到该数组中,并忽略空值
const children = []
const addChildren = v=>v === null? null : Array.isArray(v)? v.map(addChildren) : children.push(v)
addChildren(tail)
return {__m: true, tag: tag || 'div', attrs, classes, children}
}

// 判断是否可以渲染null,字符串,数字,虚拟节点,数组
m.isRenderable = v =>v === null || ['string', 'number'].includes(typeof v) || v.__m || Array.isArray(v)

// 接收真实dom和虚拟dom
// 把所需的虚拟dom的属性和类更新到真实dom
m.update = (el, v)=>{
// 如果是文本元素,需要设置数据
if (!v.__m) return el.data === `${v}` || (el.data = v)
// 设置添加class
for (const name of v.classes) {
if (!el.classList.contains(name)) el.classList.add(name)
}
for (const name of el.classList) {
if (!v.classes.includes(name)) el.classList.remove(name)
}
// 设置添加attributes
for (const name of Object.keys(v.attrs)){
if (el[name] !== v.attrs[name]) el[name] = v.attrs[name]
}
for (const {name} of el.attributes) {
if (!Object.keys(v.attrs).includes(name) && name !== 'class') el.removeAttribute(name)
}
}

// 给定一个虚拟DOM节点,生成一个真实dom节点,否则生成一个真实textNode值。
m.makeEl = v=>v.__m? document.createElement(v.tag) : document.createTextNode(v)

// 渲染,真实Dom和虚拟Dom
// a) 获取新老节点的子项
// b) 清除多余的旧节点
// c) 遍历每个新虚拟节点
// 1、通过index获取匹配的旧节点,如果没有则新增节点
// 2、如果没有匹配的旧元素,创建一个新元素加入父级
// 3、如果不匹配(标签名/节点),就替换父级上的匹配项为新元素
// 4、更新节点的属性/类
// 5、递归子节点
// 高效追加到元素列表,耗时O(n)
m.render = (parent, v)=>{
const olds = parent.childNodes || []
const news = v.children || []
// 遍历次数是新旧节点的属性长度之差
for (const _ of Array(Math.max(0, olds.length - news.length))) {
parent.removeChild(parent.lastChild)
}
// entries返回可枚举属性的键值对数组
for (const [i, child] of news.entries()){
let el = olds[i] || m.makeEl(child)
// 旧的属性的值存在
if (!olds[i]){
parent.appendChild(el)
}
const mismatch = (el.tagName || '') !== (child.tag || '').toUpperCase()
if (mismatch) {
(el = m.makeEl(child)) && parent.replaceChild(el, olds[i])
}
m.update(el, child)
m.render(el, child)
}
}

原文:https://leontrolski.github.io/33-line-react.html

听说,打赏我的人最后都找到了真爱
↘ 此处应有打赏 ↙
// 用户脚本