Vue源码分析-CreateApp

官网解释

创建一个应用实例

  • 类型

    1
    function createApp(rootComponent: Component, rootProps?: object): App
  • 详细信息

    第一个参数是根组件。第二个参数可选,它是要传递给根组件的 props。

  • 示例

    可以直接内联根组件:

    1
    2
    3
    4
    5
    import { createApp } from 'vue'

    const app = createApp({
    /* root component options */
    })

    也可以使用从别处导入的组件:

    1
    2
    3
    4
    import { createApp } from 'vue'
    import App from './App.vue'

    const app = createApp(App)
  • 源码位置

    packages/runtime-core/src/apiCreateApp.ts

  • 函数名

    createAppAPI

  • mini-vue中的实现(简化版vue实现)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    export function createAppAPI(render) {
    return function createApp(rootComponent) {
    const app = {
    _component: rootComponent,
    mount(rootContainer) {
    console.log("基于根组件创建 vnode");
    const vnode = createVNode(rootComponent);
    console.log("调用 render,基于 vnode 进行开箱");
    render(vnode, rootContainer);
    },
    };

    return app;
    };
    }

createApp源码内函数处理

  • 部分源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    export function createAppAPI<HostElement>(
    render: RootRenderFunction<HostElement>,
    hydrate?: RootHydrateFunction
    ): CreateAppFunction<HostElement> {
    if (!isFunction(rootComponent)) {
    rootComponent = extend({}, rootComponent)
    }

    if (rootProps != null && !isObject(rootProps)) {
    __DEV__ && warn(`root props passed to app.mount() must be an object.`)
    rootProps = null
    }

    const context = createAppContext()

    // ...

    const app: App = (context.app = {
    // ...
    })

    if (__COMPAT__) {
    installAppCompatProperties(app, context, render)
    }

    return app
    }
  • 创建context源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // createApp经过一些列判断之后使用createAppContext,创建了一个空的根组件对象
    export function createAppContext(): AppContext {
    return {
    app: null as any,
    config: {
    isNativeTag: NO,
    performance: false,
    globalProperties: {},
    optionMergeStrategies: {},
    errorHandler: undefined,
    warnHandler: undefined,
    compilerOptions: {}
    },
    mixins: [],
    components: {},
    directives: {},
    provides: Object.create(null),
    optionsCache: new WeakMap(),
    propsCache: new WeakMap(),
    emitsCache: new WeakMap()
    }
    }

    创建完context后,给context.app赋值, 其中包含的属性及含义:

    • _uid

    • _component(createApp传入的rootComponent处理来的)

    • _props(createApp传入的rootProps处理来的)

    • _container mount传入的rootContainer

    • _context(context实例)

    • _instance (mount时创建的vnode的component)

    • version(Vue版本)

    • config

      特殊处理设置getter、setter

      getter 返回 context.config

      setter 开发环境提示不让直接更改该属性

    • use 对应app.use(plugin: Plugin, …options: any[])

    • mixin 方法

      如果支持选项式API则将传入的mixin存放到context.mixins
      内部有去重处理

    • component 同app.component()

    • directive 同 app.directive()

    • mount

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      mount(
      rootContainer: HostElement,
      isHydrate?: boolean,
      isSVG?: boolean
      ): any {
      // ...
      // 创建vnode
      const vnode = createVNode(rootComponent, rootProps)
      // store app context on the root VNode.
      // this will be set on the root instance on initial mount.
      // 将当前的上下文给vnode挂上
      vnode.appContext = context
      // ...
      // 判断用那个函数渲染
      if (isHydrate && hydrate) {
      hydrate(vnode as VNode<Node, Element>, rootContainer as any)
      } else {
      render(vnode, rootContainer, isSVG)
      }
      app._container = rootContainer // 将mount传入的rootContainer赋值给_container

      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
      // 将vnode的component赋值给app的_instance
      app._instance = vnode.component
      devtoolsInitApp(app, version)
      }
      // 返回一个根组件实例
      return getExposeProxy(vnode.component!) || vnode.component!.proxy
      // ...
      }
    • unmount

      1
      2
      3
      4
      5
      6
      7
      8
      unmount() {
      // ...
      render(null, app._container) // 卸载应用
      // ...
      app._instance = null // 将_instance赋空
      // ...
      delete app._container.__vue_app__ // 删除__vue_app__标记
      }
    • provide

      1
      2
      3
      4
      5
      6
      7
      provide(key, value) {
      // ...
      // 提供一个值,可以在应用中的所有后代组件中注入使用
      context.provides[key as string | symbol] = value
      // 返回当前实例可以链式调用
      return app
      }
    • runWithContext 3.3+

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      runWithContext(fn) {
      // 当前应用
      currentApp = app
      try {
      // 执行回调函数
      return fn()
      } finally {
      currentApp = null
      }
      }

    app属性参考,官网地址: https://cn.vuejs.org/api/application.html