Skip to content

Latest commit

 

History

History
283 lines (210 loc) · 6.4 KB

初始化流程.md

File metadata and controls

283 lines (210 loc) · 6.4 KB

初始化流程

首先看一个简单的例子

<script src="../dist/vue.global.js"></script>

<div id="app">
  <h1>{{title}}</h1>
</div>

<script>
const {createApp, ref} = Vue
createApp({
  setup() {
    const title = ref('vue3')
    return {
      title
    }
  }
})
.mount('#app')
</script>

在createApp打上断点,进入断点到了下面的路径

packages/runtime-dom/src/index.ts
const app = ensureRenderer().createApp(...args)

ensureRenderer

感觉ensureRenderer返回一个对象,对象当中包含createApp

function ensureRenderer() {
  return (
    renderer ||
    // 创建渲染器实例同时传入当前平台特有的节点操作
    (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
  )
}

createRenderer

ensureRenderer返回renderer,renderercreateRenderer返回

packages/runtime-core/src/renderer.ts
export function createRenderer<
  HostNode = RendererNode,
  HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
  return baseCreateRenderer<HostNode, HostElement>(options)
}


function baseCreateRenderer(
  options: RendererOptions,
  createHydrationFns?: typeof createHydrationFunctions
): any {
    ···2000多行代码
   return {
    render, // 把接收到的vnode转换成dom对象,并追加到宿主元素
    hydrate, // SSR 服务端将一个vnode生成为html
    createApp: createAppAPI(render, hydrate) // 创建app实例
  }
 }

最后可以确定ensureRenderercreateAppbaseCreateRenderer返回,createApp实际上是createAppAPI的返回值

createAppAPI

packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI<HostElement>(
  render: RootRenderFunction,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
  // 用户创建的App实例是这个函数生成的
  return function createApp(rootComponent, rootProps = null) {
    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,

      version,

      get config() {
    
      },

      set config(v) {
        
      },

      use(plugin: Plugin, ...options: any[]) {
        
      },

      mixin(mixin: ComponentOptions) {
       
      },

      component(name: string, component?: Component): any {
        
      },

      directive(name: string, directive?: Directive) {
       
      },

      mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
        if (!isMounted) {
          const vnode = createVNode(
            rootComponent as ConcreteComponent,
            rootProps
          )
          // store app context on the root VNode.
          // this will be set on the root instance on initial mount.
          vnode.appContext = context

          // HMR root reload
          if (__DEV__) {
            context.reload = () => {
              render(cloneVNode(vnode), rootContainer, isSVG)
            }
          }

          if (isHydrate && hydrate) {
            hydrate(vnode as VNode<Node, Element>, rootContainer as any)
          } else {
            render(vnode, rootContainer, isSVG)
          }
          isMounted = true
          app._container = rootContainer
          // for devtools and telemetry
          ;(rootContainer as any).__vue_app__ = app

          if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
            app._instance = vnode.component
            devtoolsInitApp(app, version)
          }

          return getExposeProxy(vnode.component!) || vnode.component!.proxy
        } else if (__DEV__) {
          warn(
            `App has already been mounted.\n` +
              `If you want to remount the same app, move your app creation logic ` +
              `into a factory function and create fresh app instances for each ` +
              `mount - e.g. \`const createMyApp = () => createApp(App)\``
          )
        }
      },

      unmount() {
        
      },

      provide(key, value) {
       
      }
    })

    return app
  }
}

createAppAPI返回一个函数createApp,createApp返回一个实例对象

综上所述,刚开始看到的例子 createApp执行返回一个app实例,这个实例当中包含了各种实例的配置,mount就是在这个实例中声明的。

mount

mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
     
          const vnode = createVNode(
            rootComponent as ConcreteComponent,
            rootProps
          )
        
          render(vnode, rootContainer, isSVG)
       
      },

获取vnode执行render,

const render: RootRenderFunction = (vnode, container, isSVG) => {
    patch(container._vnode || null, vnode, container, null, null, null, isSVG)
 }

执行patch

第一次传入的是根组件执行processComponent ,挂载组件mountComponent执行三步

  1. 获取组件实例 instance 这时是一个空的
  2. setupComponent 初始化
  3. setupRenderEffect 建立render的副作用 更新机制

setupComponent

setupComponent中执行setupStatefulComponent返回一个有状态的组件

setupStatefulComponent中判断setup是否存在,如果存在则执行得到setupResult,handleSetupResultsetupResult添加在instance.setupState

最后执行finishComponentSetup,这里如果没有render就会生成一个 添加到instance.render中,

兼容vue2

setCurrentInstance(instance)
pauseTracking()
applyOptions(instance)
resetTracking()
unsetCurrentInstance()

setupRenderEffect

  1. 创建更新函数componetUpdateFn
  2. 创建componentUpdateFn 的副作用 new ReactiveEffect
  3. update

update

执行update才会去执行componentUpdateFn

如果componentUpdateFn执行的过程中有响应式数据发生变化,则按照参数2的方式执行参数1

componetUpdateFn中会先判断是否已经挂载,如果已经挂载说明不是第一次,初始化就是第一次

if (!instance.isMounted) {
  ···
} else {
  ···
}

执行当前组件实例的render函数获取vnode

const subTree = (instance.subTree = renderComponentRoot(instance))

执行patch

 patch(
   null,
   subTree,
   container,
   anchor,
   instance,
   parentSuspense,
   isSVG
 )

这次的patch是首次,将创建子元素