手摸手学习vue3源码一:渲染过程

202 阅读2分钟

optionsApi 选项式函数

createApp()

  • 文件目录:packages/runtime-dom/src/index.ts
funtion createApp((...args)) {
    const app = ensureRenderer().createApp(...args)
}

// 重写 app 的mount
app.mount = () => {
    // 判断 app._component 是否是函数、是否存在render、是否存在template
    // 否:将innerHTML赋值,执行原来的 createAppAPI.mount(),执行 createVnode(),执行render(vnode, rootContainer, namespace="svg")
    // render方法是packages\runtime-core\src\renderer.ts的baseCreateRenderer的render方法的传参
}
  • 文件目录是packages/runtime-core/src/renderer.ts
funtion createRenderer(options) {
    return baseCreateRenderer(options)
}

function baseCreateRenderer() {
   return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate),
  }
}
// 渲染comonent

const render = (vnode, container, namespace) => {
    if (vnode == null) {
        if (container._vnode) {
            unmount(container._vnode, null, null, true)
        }
    } else {
        patch(container._vnode || null, vnode, container, null, null, null, namespace)
    }
}

....
执行顺序:
patch()
processComponent()
mountComponent() // 初始化html
setupComponent() // 挂在实例
  • 通过createAppAPI()返回 文件目录:packages/runtime-core/src/apiCreateApp.ts

export function createAPI(params) {
    // params = {data:xxx, methods:xx}
   const app = {
       // 常用的方法
       use() {},
       mixin() {},
       component() {},
       directive() {},
       mount() {},
       onUnmount() {},
       unmount() {},
       provide() {},
       runWithContext(fn){}
   }
   return app
}
  • 文件目录:packages/runtime-core/src/component.ts
function setupComponent(instance,isSSR,optimized) {
    // 处理 data methods
    // 把html模版编译为可执行render函数
}

function setupRenderEffect() {
    // 把render函数执行生成vnode
    // data收集 componentUpdateFn, 生成vnode,把vnode参数传给patch函数渲染正式dom
}

整理渲染过程

  • 执行 app = Vue.createdApp(),初始化操作 ensureRenderer(),闭包保存所有把 vnode 渲染为正式 dom 的函数,并且把整个实例保存到 createAppContext(),把 args 保存到 context.app_component ,再执行 app.mount('#App')
  • 执行栈:mount->patch->processComponent->mountComponent->setupComponent(渲染正式dom)

composition Api 组合式函数

  • 在setupComponent中执行了setStatefullComponent(instance, isSSR),判断是否设置 setup 属性
function setupComponent(instance, isSSR) {
    const { setup } = component
    if (setup) {
        const setupResult = callWithErrorHandling(
          setup,
          instance,
          ErrorCodes.SETUP_FUNCTION,
          [
            __DEV__ ? shallowReadonly(instance.props) : instance.props,
            setupContext,
          ],
        )
    }
}
function handleSetupResult(instance, setupResult, isSSR) {
    // 将返回的值存储到vues实例中
    instance.setupState = proxyRefs(setupResult)
}
function finishComponentSetup(instance,isSSR,skipOptions) {
    // 将实例完成proxy
    installWithProxy = i => {
        i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers)
    }
}
  • 因为在 setup 里面属性添加了 proxy,在生成 vnode 的时候,会触发 get 方法,然后就可以收集渲染函数了,在set的时候,就会触发渲染函数,实现更新