前言
本文分为
- vnode树的构建
- render 的渲染
- 数据变更后的diff
- 拿到变更后的patch
项目搭建
npm i -g create-react-app
create-react-app my-app
cd my-app
npm start
并且把src中其他文件都删除,留下index.js就可以接下来了
vnode 的构建
首先要确定我们vnode对象的属性; type:标签名,attrs:属性名,children:子元素
实例:

那么根据上面我们需要的vnode结果,我们去写创建vnode的函数 createElement(type, attrs, children)
src/index.js
import { createElement } from './element'
let virtualDom = createElement('ui', { class: 'li-group' }, [
createElement('li', { class: 'item' }, ['a']),
createElement('li', { class: 'item' }, ['b'])
])
接下来我们开始具体去写createElement函数,我们先在src下新建一个element.js文件
createElement - src/element.js
src/elment.js
class Element {
constructor(type, attrs, children) {
this.type = type
this.attrs = attrs
this.children = children
}
}
function createElement(type, attrs, children) {
return new Element(type, attrs, children)
}
export { createElement }
现在我们已经可以从上面打印出virtualDom的数据结构了,那么接下来就是将virtualDom通过render函数去构建成真实的dom
render - src/elment.js
render(virtualDom, target) target:添加的目标节点
src/index.js
import { render } from './element'
let el = render(virtualDom)
src/element.js
function render(virtualDom){
const { type, attrs, children } = virtualDom
let el = document.createElement(type)
// 挂载属性
for(let key in attrs){
setAttr(el, key, attrs[key])
}
// 创建子节点
children.forEach(child=>{
if(child instanceof Element){
// 节点
el.appendChild(render(child))
}else{
// 文本
let textNode = document.createTextNode(child)
el.appendChild(textNode)
}
})
return el
}
function setAttr(node, key, value){
switch(key){
case 'value': // input textarea
if(node.tagName.toUpperCase() === 'INPUT' || node.tagName.toUpperCase() === 'TEXTAREA'){
node[key] = value
} else{
node.setAttribute(key, value)
}
break;
case 'style':
node.style.cssText = value
break;
default:
node.setAttribute(key, value)
break;
}
}
那么现在我们终于可以将Dom渲染到页面中了
renderDom - src/element.js
src/index.js
import { renderDom } from './element'
renderDom(el, document.getElementById('root'))
src/element.js
function renderDom(el, target){
target.appendChild(el)
}
上半部分已经好了,我们还是动手完成一下成果把
github: github.com/XueMary/my-…
先整理一下剩下的内容
vnode已经完成了,那么接下来就还有数据变更后的 diff 算法 和 diff 算法计算出的变更内容 patch。 数据变更后会产生新的vnode, diff会对比两份 vnode 查到其中的变更,再将这些变更通过patch函数去修改真实dom
再来打破一下大多数人对虚拟dom的理解
网上很多人说虚拟动效率高,性能好。
开始分析了:
1、什么虚拟dom经过diff算法实现了dom的最小变化,局部刷新(这句话每错),但是和性能没关系,你手动去操作dom,看看控制台是不是也只有你改变的dom元素刷新了,手工操作本身就是最小变化,框架还要经过diff,那么框架为什么要diff,因为不diff,那他就得把整个页面出现渲染了。
2、另一种说法就更唬人点了。什么js线程和渲染线程是两个部门,跨部门交流当然耗性能,js去更新 虚拟dom是js和js间的沟通,搞的好像更新了虚拟dom树后不用去操作真实dom一样了。比手工操作还多了一遍操作
那么虚拟dom到底为什么存在。
回到框架本身,现在前端是数据驱动视图,是为了让使用者只关注数据本身,对dom的增删改查就都被隐藏在了框架中,框架本身也需要这么一份vnode的描述,来说明现在的node结构是怎么的,不然他也不知道哪里被改变了,也就无法帮我们去修改真实dom了
这才是维护一份虚拟dom的作用