「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」
createElm函数
路径:core/vdom/patch.js
createElm函数的作用是把VNode节点转换成对应的DOM元素,把DOM元素存储在对象的 Element 属性中,但是并没有把创建的DOM元素挂载到对应的节点上,那么什么时候进行渲染呢?
这个函数内部可以分成三个过程
过程1
执行用户设置的 init 钩子函数
代码片段
// 执行用户设置的 init 钩子函数
let i: any
let data = vnode.data
if (data !== undefined) {
const init = data.hook?.init
if (isDef(init)) {
init(vnode)
data = vnode.data
}
}
解析
-
vnode.data这个就是我们使用h函数创建vnode的时候,传入的参数- 像这样:
vnode = h('div#container.xxx', 'Hello WorldAAAAAA') - 或者这样:
h('p', {on: {click: eventHandler }}, 'World')关于h函数,大家可以看一下我之前写的 h函数的解析
- 像这样:
-
data.hook?.init这块也是我们用户进行传递的内容hook?.init的?是 ES 链式判断运算符 ?
data.hook?.init //等价于 data.hook && data.hook.init; -
isDef(init)这就是很简单,判断用户是否有定义 init 的内容 -
init(vnode)init 钩子函数是在创建真实DOM之前,让用户可以对vnode可以进行修改,比如可以更改样式或者其他属性
过程2
把 vnode 转换成真实 DOM 对象(没有渲染到页面上,而是把创建的对象挂载到 vnode的element属性上)
代码片断
const children = vnode.children
const sel = vnode.sel
if (sel === '!') {
if (isUndef(vnode.text)) {
vnode.text = ''
}
vnode.elm = api.createComment(vnode.text!)
} else if (sel !== undefined) {
...
中间省略
} else {
vnode.elm = api.createTextNode(vnode.text!)
}
解析
children变量就是 vnode的子节点sel是选择器sel === '!'这里的判断旧比较奇怪了- 首先我们先看一下我们在html中的注释格式
<!-- 1 --> - 这样就很明确了,如果
sel === '!'那么接下来就是注释节点的创建了
- 首先我们先看一下我们在html中的注释格式
sel !== undefined会创建对应的DOM元素else中就很简单了:创建文本节点
过程3
过程3最简单,直接返回vnode.elem也就是返回新创建的 DOM
总结
-
如果当前元素是注释节点,会调用
createComment来创建一个注释节点,然后挂载到vnode.elm -
如果不存在选择器,只是单纯的文本,调用
createTextNode来创建文本,然后挂载到vnode.elm -
如果存在选择器,会对这个选择器做解析,得到
tag、id和class,-
调用
createElement或createElementNS来生成节点,并挂载到vnode.elm。 -
接着调用
module上的create hook,如果存在children,遍历所有子节点并递归调用createElm创建dom,通过appendChild挂载到当前的elm上,不存在children但存在text,便使用createTextNode来创建文本。 -
最后调用调用元素上的
create hook和保存存在insert hook的vnode,因为insert hook需要等dom真正挂载到document上才会调用,这里用个数组来保存可以避免真正需要调用时需要对vnode树做遍历。
-