1 解析App得到一个 vnode
,下面的操作全部基于vnode
// main.js
createApp(App).mount(rootContainer)
function createApp(rootComponent) {
return {
// 这里先写一个容器,因为vue3传递的是选择器,现在先用容器
// 为了把App即系rootComponent的渲染到这个根容器里面
mount(rootContainer) {
// 先把rootComponent变成一个虚拟节点 vnode (rootComponent->vnode)
// 后续所有逻辑都会基于这个虚拟节点进行处理
const vnode = createdVnode(rootComponent)
//接受一个vnode和虚拟节点
render(vnode, rootContainer)
}
}
}
// h函数也是调用createdVnode
function createdVnode(type,props?,children?) {
const vnode = {
type,
props,
children
}
return vnode
}
// 第一层拆箱,拆解App
// vnode = {
// children: undefined
// props: undefined
// type: {render: ƒ, setup: ƒ}
// }
2 接着调用render函数,把vnode
和容器#root
传过去,内部原封不动调用patch
方法
// 调用path,进行递归处理
function render(vnode, container) {
patch(vnode, container)
}
function path(vnode,container) {
if(vnode.type === 'string') {
processElement(vnode,container)
}else if(isObject(vnode.type)) {
processComponent(vnode,container)
}
}
3 处理组件类型
// 在这先得出结论(为了得到一个实例,拆箱直到type是element)
// component类型最后要得出一个instance实例是
instance = {
render: ƒ render(),
setupState: {msg: 'mini-vue'},
type: {render: f, setup: ƒ},
vnode: {
type: {
render: ƒ render()
setup: ƒ setup()
},
props: undefined,
children: undefined
}
}
// 处理组件
function processComponent(vnode,container) {
mountComponent(vnode,container)
}
// 初始化组件
function mountComponent(vnode,container) {
// 得到一个实例instance
const instance = createComponentInstance(vnode)
// 补全instance属性
setupComponent(instance)
setupRenderEffect(instance,container)
}
// 创建一个组件实例
function createComponentInstance(vnode) {
const component = {
vnode,
// 便于后面使用
type:vnode.type
}
}
function setupComponent(instance) {
//TODO: initProps
//TODO: initSlots
// 初始化有状态的组件
setupStatefulComponent(vnode)
}
// 解构setup,并执行
function setupStatefulComponent(vnode) {
const Component = vnode.type
const { setup } = Component
// 一种情况,用户可能不写setup
if(setup) {
// 执行setup
const setupResult = setup()
handlerSetupResult(instance,setupResult)
}
}
function handlerSetupResult(instance,setupResult) {
// 只处理setup是object的情况,function先不处理
if(typeof setupResult === 'object') {
instance.setupState = setupResult
}
finishComponentSetup(instance)
}
function finishComponentSetup(instance) {
const Component = instance.type
// 假设用户一定要写render
instance.render = Component.render
}
function setupRenderEffect(vnode,container) {
// 调用render的h方法,h又调用createdVnode
const subTree = vnode.render()
// 继续调用path方法,继续拆箱
path(subTree,container)
}
4 处理element类型
// 处理element类型
function processElement(vnode,container) {
mountElement(vnode,container)
}
// 初始化element
function mountElement(vnode,container) {
// vnode 是h函数返回的数据(type,props,children)
// 创建元素
const el = document.createElement(vnode.type)
// 判断children是不是数组,如果是数组继续拆箱,遍历调用path方法
const { children } = vnode
// 不是数组那就是string类型,调用el.textContent
if(typeof children === 'string') {
el.textContent = children
}else if(Array.isArray(children)){
mountChildren(vnode, el)
}
// 生成props
const { props } = vnode
for(const key in props) {
const val = props[key]
el.setAttribute(key, val)
}
// 插入container
container.appendChildren(el)
}
function mountChildren(vnode,container) {
vnode.children.forEach(v=>{
patch(v,container)
})
}
本文仓库地址 github