拆箱vue3,从compont到element

786 阅读2分钟

此系列的文章感谢 阿崔cxrmini-vue

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