const {createVNode,render,h} = VueRuntimeDOM
render(h('div',{style:{color:'red'}},
[
h('p',{key:'a'},'a'),
h('p',{key:'b'},'b'),
h('p',{key:'c'},'c'),
h('p',{key:'d'},'d'),
h('p',{key:'e'},'e'),
h('p',{key:'q'},'q'),
h('p',{key:'f'},'f'),
h('p',{key:'g'},'g')
]
),app)
//vue内置的渲染器,也可以通过createRenderer创建一个渲染器
export function render(vnode,container){
let {render} = createRenderer(renderOptions)
return render(vnode,container)
}
函数接收一个dom操作对象,在函数内部会对改对象解构,获取各种dom和属性操作方法
返回一个render函数,该函数接收两个参数,分别是虚拟节点vnode,和容器container,改函数在节点更新删除都可以使用,因为都要将虚拟节点渲染成新节点,因为只需要比对传入的新的虚拟节点和原来的旧的虚拟节点,就可以判断出是更新节点还是删除节点
在render函数内部,判断vnode
patch函数,传入四个参数;旧虚拟节点vnode1,新虚拟节点vnode2,节点容器container,以及anchor = null
processElement函数中,对vnode进行判断,若为null,说明不存在旧节点,则直接根据新的vnode创建真实节点
在patchElement函数中,因为两个元素的元素名相同,因此只需要比对属性和孩子
let {
createElement: hostCreateElement,
createTextNode: hostCreateTextNode,
insert: hostInsert,
remove: hostRemove,
querySelector: hostQuerySelector,
parentNode: hostParentNode,
nextSibling: hostNextSibling,
setText: hostSetText,
setElementText: hostSetElementText,
patchProp: hostPatchProp
} = options
function render(vnode, container) {
if (vnode == null) {
//卸载元素
if (container._vnode) {
umount(container._vnode)
}
} else {
//更新元素
patch(container._vnode || null, vnode, container)
}
//将节点保留在容器上
container._vnode = vnode
}
return {
render
}
function umount(vnode) {
hostRemove(vnode.el)
}
function patch(n1, n2, container, anchor = null) {
//判断n1和n2是否相同
if (n1 && !isSameVNode(n1, n2)) {
//之前存在节点,但和更新的不同,则删除之前的节点
umount(n1)
n1 = null
}
const { type, shapeFlags } = n2
switch (type) {
case Text:
processText(n1, n2, container)
break
case Fragment:
processFragment(n1,n2,container)
default:
if (shapeFlags & ShapeFlags.ELEMENT) {
processElement(n1, n2, container, anchor)
}
}
}
//判断是否为同一类型节点
export function isSameVNode(n1,n2){
return n1.type=== n2.type && n1.key === n2.key
}
function processElement(n1: any, n2: any, container: any, anchor: any) {
if (n1 == null) {
//创建节点
mountElement(n2, container, anchor)
} else {
//更新节点
patchElement(n1, n2)
}
}
function mountElement(vnode, container, anchor) {
let { type, shapeFlags, props, children } = vnode
let el = vnode.el = hostCreateElement(type)
if (props) {
//存在属性则更新属性
patchProps(null, props, el)
}
//此时渲染children,其不是文本就是数组
if (shapeFlags & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(el, children)
}
if (shapeFlags & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(children, el)
}
hostInsert(el, container, anchor)
}
function patchProps(oldProps, newProps, el) {
if (oldProps == null) {
oldProps = {}
}
if (newProps == null) {
newProps = {}
}
for (let key in newProps) {
hostPatchProp(el, key, oldProps[key], newProps[key])
}
for (let key in oldProps) {
if (newProps[key] == null) {
hostPatchProp(el, key, oldProps[key], null)
}
}
}
function patchChildren(n1, n2, el) {
let c1 = n1.children
let c2 = n2.children
const prevShapeFlag = n1.shapeFlags
const shapeFlag = n2.shapeFlags
//如果新孩子是文本
if (shapeFlag && shapeFlag & ShapeFlags.TEXT_CHILDREN) {
//之前是数组
if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(c1)
} if (c1 !== c2) {
//之前要么文本,要么是空
hostSetElementText(el, c2)
}
} else {
//最新的要么是数组,要么就是空
if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
//前后都是数组.
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
//diff算法
patchKeyedChildren(c1, c2, el)
} else {
//说明是空
unmountChildren(c1)
}
} else {
if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(el, '')
}
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(c2, el)
}
}
}
}
function unmountChildren(children) {
children.forEach(child => {
umount(child)
})
}
function normalize(children, i) {
if (isString(children[i]) || isNumber(children[i])) {
children[i] = createVNode(Text, null, children[i])
}
return children[i]
}
function mountChildren(children, container) {
for (let i = 0; i < children.length; ++i) {
//如果数组为字符串或者数字,则是文本节点数组,其它的则是元素节点数组
let child = normalize(children, i)
patch(null, child, container)
}
}
const {createVNode,render,h} = VueRuntimeDOM
render(h('div',{style:{color:'red'}},'Hello'),app)
setTimeout( function(){
render(h('div',{style:{color:'black'}},'World'),app)
},1000)