前言
初始化组件流程中的最后提到:
因为对根组件拆箱后只是得到了下一个层元素类型的
vnode
,没有实现对元素类型vnode
的渲染逻辑
因此我们来实现这一部分
拆箱
runtime-core/renderer.js
diff
function patch(vnode, container){
+ if(typeof vnode.type === 'string') {
+ processElement(vnode, container)
+ } else if(typeof vnode.type === 'object' && vnode.type !== null) {
processComponent(vnode, container)
+ }
}
元素类型的vnode
的不需要挂载、初始化等一系列操作,和初始化组件流程不同需要走不同的逻辑
处理元素类型
runtime-core/renderer.js
js
function processElement(vnode, container) {
mountElement(vnode, container)
}
和[[初始化组件流程#处理组件|处理组件]]一样有两种情况:
- 视图初始化
- 视图更新
先关注初始化
初始化元素
runtime-core/renderer.js
js
function mountElement(vnode, container) {
const el = document.createElement('div')
el.textContent = 'Hello, World'
el.setAttribute('id', 'root')
document.body.append(el)
}
这里虽然写死了渲染逻辑,但原理都是这个原理
- document.createElement:根据类型创建元素
- textContent:设置元素子节点
- setAttribute:设置元素属性
- append:插入元素
创建元素
runtime-core/renderer.js
diff
function mountElement(vnode, container) {
+ const el = document.createElement(vnode.type)
el.textContent = 'Hello, World'
el.setAttribute('id', 'root')
document.body.append(el)
}
设置子节点
runtime-core/renderer.js
diff
function mountElement(vnode, container) {
const el = document.createElement(vnode.type)
+ const { children } = vnode
+ if (typeof children === 'string') {
+ el.textContent = children
+ } else if (Array.isArray(children)) {
+ children.forEach(v => {
+ patch(v, el)
+ })
+ }
el.setAttribute('id', 'root')
document.body.append(el)
}
- 子节点为字符串:直接覆盖
- 子节点为数组:因此数组的成员必须由
vnode
组成,所以需要遍历递归[[初始化元素流程#拆箱|拆箱]],注意此时容器应该是当前元素
设置属性
runtime-core/renderer.js
diff
function mountElement(vnode, container) {
const el = document.createElement(vnode.type)
const { children } = vnode
if (typeof children === 'string') {
el.textContent = children
} else if (Array.isArray(children)) {
children.forEach(v => {
patch(v, el)
})
}
+ const { props } = vnode
+ for(const key in props){
+ el.setAttribute(key, props[key])
+ }
document.body.append(el)
}
props
只能是对象,遍历设值即可
插入元素
runtime-core/renderer.js
diff
function mountElement(vnode, container) {
const el = document.createElement(vnode.type)
const { children } = vnode
if (typeof children === 'string') {
el.textContent = children
} else if (Array.isArray(children)) {
children.forEach(v => {
patch(v, el)
})
}
const { props } = vnode
for(const key in props){
el.setAttribute(key, props[key])
}
+ container.append(el)
}
要注意插入到那个容器元素中
总结
初始化元素的流程和初始化组件流程的不同只是在拆箱时多了一条分支
到这里视图的初始化流程基本上没问题了,后续会慢慢补充一些细节
预告
前面的 demo 中我们打算在根组件 的render
中使用this.msg
获取setup
状态,显然还没有实现这部分逻辑,看不到效果,只有undefined
,this
是什么?它是怎么获取状态的?留到下一节解答