mini-react 实现原理讲解 第二讲
source link: https://segmentfault.com/a/1190000025185299
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
相关代码请查阅 https://github.com/mcuking/bl...
v = f(props, state)
组件的渲染结果由 render,props,state 共同决定,上一讲只是讨论了 render,本讲开始讨论 props 和 state。
props
对于 props, 父组件传递过来, 不可变,由基类 Component 设置 props。
class Component { constructor(props) { this.props = props } }
state
对于 state, 在组件的生命期内是可以修改的,当调用组件的 setState 方法的时候, 其实就是重新渲染,用一个新 DOM 树替换老的 DOM:
parent.replaceChild (newdom, olddom)
因此我们需要解决两个问题:
- 组件实例必须有机制获取到 olddom
- 组件实例必须有机制获取到 parentDOM
这 2 个问题其实是一个问题。 parent = olddom.parentNode, 所以上一行代码等价于:
olddom.parentNode.replaceChild (newdom, olddom)
现在的关键就是获取到 olddom,采用的机制是:
将每个组件实例直接渲染出的组件实例 / DOM 设置 为该组件实例的 rendered 属性,形成一个__rendered 链
例如上一讲的组件嵌套案例的__rendered 链如下:
Animal --__rendered--> Pet --__rendered--> Cat --__rendered--> div
通过以下代码实现完整的__rendered 链,其中 comp 参数代表 "我是被谁渲染的":
function render (vnode, parent, comp) { let dom if(typeof vnode == "string") { const dom = ... // 创建文本节点 comp && (comp.__rendered = dom) ... // other op } else if(typeof vnode.nodeName == "string") { const dom = ... // 创建 dom 节点 comp && (comp.__rendered = dom) ... // other op } else if (typeof vnode.nodeName == "function") { const inst = ... // 创建 组件实例 comp && (comp.__rendered = inst) ... // other op } }
当第一次渲染形成了完整的__rendered 链后,再次渲染(通过 setState 等)时,即可通过当前渲染的组件实例,沿着__rendered 链向下找到实际渲染的 dom 节点,即 olddom。从而获得 parent,即 olddom.parentNode。
// 找到当前组件实例渲染的的实际的 DOM 节点 function getDOM(comp) { let rendered = comp.__rendered // 通过__render 链向下找到第一个非组件的 dom 节点 while (rendered instanceof Component) { rendered = rendered.__rendered } return rendered }
进而调用 setState,使用 dom 替换 olddom,代码如下:
function render(vnode, parent, comp, olddom) { let dom if(typeof vnode == "string") { ... if(olddom) { parent.replaceChild(dom, olddom) } else { parent.appendChild(dom) } ... } else if(typeof vnode.nodeName == "string") { ... if(olddom) { parent.replaceChild(dom, olddom) } else { parent.appendChild(dom) } ... } else if (typeof vnode.nodeName == "function") { ... render(innerVnode, parent, inst, olddom) } }
完整功能如下:
//Component class Component { constructor(props) { this.props = props } setState(state) { setTimeout(() => { this.state = state const vnode = this.render() let olddom = getDOM(this) render(vnode, olddom.parentNode, this, olddom) }, 0) } } function getDOM(comp) { let rendered = comp.__rendered while (rendered instanceof Component) { // 判断对象是否是 dom rendered = rendered.__rendered } return rendered } //render function render (vnode, parent, comp, olddom) { let dom if(typeof vnode == "string" || typeof vnode == "number") { dom = document.createTextNode(vnode) comp && (comp.__rendered = dom) parent.appendChild(dom) if(olddom) { parent.replaceChild(dom, olddom) } else { parent.appendChild(dom) } } else if(typeof vnode.nodeName == "string") { dom = document.createElement(vnode.nodeName) comp && (comp.__rendered = dom) setAttrs(dom, vnode.props) if(olddom) { parent.replaceChild(dom, olddom) } else { parent.appendChild(dom) } for(let i = 0; i < vnode.children.length; i++) { render(vnode.children[i], dom, null, null) } } else if (typeof vnode.nodeName == "function") { let func = vnode.nodeName let inst = new func(vnode.props) comp && (comp.__rendered = inst) let innerVnode = inst.render(inst) render(innerVnode, parent, inst, olddom) } }
总结
render 方法负责把 vnode 渲染到实际的 DOM, 如果组件渲染的 DOM 已经存在就替换, 并且保持一个完整的 __rendered 的引用链
Recommend
-
85
第1章 SSH服务介绍说明1.1 SSH服务介绍SSH(22端口)是Secure Shell Protocol的简写,由IETF网络工作小组(Network Working Group)制定;在进行数据传输之前,SSH先对联机数据包通过加密技术进行加密处理,加密后在进行数据传输。确保了传递的数据安全。SSH是专为...
-
99
1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/订阅(深入了解发布/订阅,可以看我的文章用发布订阅模式
-
19
GRE网络中的路由协议部署方案 考虑点:第一:两个站点设备上面用哪个接口建立OSPF邻居关系呢?第二:OSPF的hello报文是组播传递 224.0.0.5 这个组播地址不能跨跳 即TTL为1
-
33
文章首发于我的博客 https://github.com/mcuking/bl... 相关代码请查阅 https://github.com/mcu...
-
9
正则表达式匹配素数的原理讲解为什么要写这么一篇文章呢?是因为自己最近在研究和学习正则表达式,然后在RegexGolf上练习技能的时候遇到了这么一道题目,觉得很有趣。我当时虽然也解决了这个问题,但是正则表达式写的有点长,而且也只算是一种...
-
9
简介:区块链已火遍全球,行业人才急缺,岗位炙手可热,学习正当时,本次特邀登链科技首席技术官(Tiny熊),为你讲解区块链技术核心概念与原理。 ...
-
10
连载《Chrome V8 原理讲解》第四篇 V8词法分析源码讲解,Token字生成灰豆chrome v8连载,3~4天一篇,持续更新中...
-
6
连载《Chrome V8 原理讲解》第十篇 V8 Execution源码分析灰豆chrome v8连载,3~4天一篇,持续更新中...
-
6
连载《Chrome V8 原理讲解》第六篇 bytecode字节码生成灰豆chrome v8连载,3~4天一篇,持续更新中...
-
3
连载《Chrome V8 原理讲解》第七篇 V8堆栈框架 Stack Frame灰豆chrome v8连载,3~4天一篇,持续更新中...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK