1. 首页

跟着尤大大一起Vue3源码解读(一)-createApp

前言

距离上一篇文章源码解读预热过去了两周多的时间了,度过了国庆和中秋节,迟到的中秋国庆快乐送给大家,祝大家no bug,线上不报警,天天早下班。

Vue3系列文章将会围绕文件夹来进行讲解,主线流程走通后,再进行部分的讲解,会按照这个原则进行Vue3源码的逐一阅读和分析。

正文

正文从这里开始,Vue3源码分析之路从这里开始,

createApp

Vue3中创建应用的方式是通过createApp来进行的,较之前new Vue的方式在使用上基本无差别。


@file packages/runtime-dom/src/index.ts export const createApp = ((...args) => { const app = ensureRenderer().createApp(...args) if (__DEV__) { injectNativeTagCheck(app) } const { mount } = app app.mount = (containerOrSelector: Element | string): any => { const container = normalizeContainer(containerOrSelector) if (!container) return const component = app._component if (!isFunction(component) && !component.render && !component.template) { component.template = container.innerHTML } container.innerHTML = '' const proxy = mount(container) container.removeAttribute('v-cloak') container.setAttribute('data-v-app', '') return proxy } return app }) as CreateAppFunction<Element> // Js中文网 -前端Vue3源码解析 https://www.javascriptc.com/

上面代码createApp就是咱们在创建应用时调用的方法

  • ensureRenderer是一个单例模式的函数,会返回一个renderer,如果无renderer则会调用createRenderer进行获取renderer,获得了一个app实例;
  • dev环境下注册一个方法:isNativeTag,挂载到app.config下面;
  • 获取到实例的mount方法,并保存下来;
  • 重写实例的mount方法;
    • 调用normalizeContainer获取根元素容器;
    • 判断template,获取需要渲染的模板;
    • 把容器的innerHTML置空;
    • 调用上面实例的mount方法;
    • 删除v-cloak属性,添加data-v-app属性;
    • 返回mount后的代理;
  • 返回实例;

// 上面代码中所使用到的两个简单的函数源码 // @file packages/runtime-dom/src/index.ts // 获取renderer let renderer: Renderer<Element> | HydrationRenderer function ensureRenderer() { return renderer || (renderer = createRenderer<Node, Element>(rendererOptions)) } // normalizeContainer,获取元素 function normalizeContainer(container: Element | string): Element | null { if (isString(container)) { return document.querySelector(container) } return container } // Js中文网 -前端Vue3源码解析 https://www.javascriptc.com/

至此,createAPP方法讲解完成。其中的核心包括两部分,第一部分就是调用createRenderer的实现;第二部分就是实例的mount方法的实现。

createRenderer

上面说到createAPP里面调用此方法获取renderer对象,来看下这个的实现。createRenderer接受一个对象参数rendererOptions, const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps)

nodeOps和Vue2的时候的node-ops是差不多的功能,不过在Vue3中进行了优化,把patch和clone增加到了nodeOps里面,去掉了部分功能;


// @file packages/runtime-core/src/renderer.ts export function createRenderer<HostNode = RendererNode, HostElement = RendererElement>(options: RendererOptions<HostNode, HostElement>) { return baseCreateRenderer<HostNode, HostElement>(options) } // Js中文网 -前端Vue3源码解析 https://www.javascriptc.com/

baseCreateRenderer的实现如下:


// @file packages/runtime-core/src/renderer.ts function baseCreateRenderer( options: RendererOptions, createHydrationFns?: typeof createHydrationFunctions ): any { // balabala,一堆函数的声明 return { render, hydrate, createApp: createAppAPI(render, hydrate) } } // Js中文网 -前端Vue3源码解析 https://www.javascriptc.com/

可以看到上面的代码,最后返回的是render函数,hydrate函数和createApp函数,createApp函数是通过调用createAppAPI来获取到的。

// @file packages/runtime-core/src/apiCreateApp.ts
export function createAppContext(): AppContext {
  return {
    app: null as any,
    config: {
      isNativeTag: NO,
      performance: false,
      globalProperties: {},
      optionMergeStrategies: {},
      isCustomElement: NO,
      errorHandler: undefined,
      warnHandler: undefined
    },
    mixins: [],
    components: {},
    directives: {},
    provides: Object.create(null)
  }
}

export function createAppAPI<HostElement>(
  render: RootRenderFunction,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    if (rootProps != null && !isObject(rootProps)) {
      __DEV__ && warn(`root props passed to app.mount() must be an object.`)
      rootProps = null
    }

    const context = createAppContext()
    const installedPlugins = new Set()

    let isMounted = false
    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,

      version,

      get config() {
        return context.config
      },

      set config(v) {
        if (__DEV__) {
          warn(
            `app.config cannot be replaced. Modify individual options instead.`
          )
        }
      },
      use() {},
      mixin() {},
      component() {},
      directive() {},
      mount() {},
      unmount() {},
      provide() {}
    })
    return app
  }
}

如上,就是createAppAPI的源码,直接返回了一个函数createApp。createApp:

  • 对传递进来的第二个参数,也就是root props进行校验;
  • 调用createAppContext创建appContext对象,赋值给context;
  • 创建变量installedPlugins,Set类型,存储已经安装过的插件;
  • isMounted设为false;
  • 创建app,挂载属性和函数;
  • 返回app;

此时的app属于Vue的一个准备阶段,为后面的mount等操作准备好了所需要使用到的函数。 跟着尤大大一起Vue3源码解读(一)-createApp

mount

最核心(繁琐)的操作都在mount里面,这里面包括Vnode,render,patch等等所有的核心功能。咱们先理一下mount的调用流程。

  • 第一步:调用runtime-dom下面index.ts里面的mount函数;
  • 第二步:调用runtime-core下面apiCreateApp里面的mount函数;

前面这两步骤和Vue2中的调用是相似的,先调用平台相关的mount,再调用核心相关的mount;

  • 第三步:直接调用createVNode;与Vue2不同的是,2并不会直接调用,而是建立一个watcher,使用watcher调用get的时候来调用,详情可见Vue2源码解读(七)-mount
  • 第四步:调用render,判断是进行卸载还是进行渲染;
  • 第五步:调用patch,进行dom diff,渲染页面。 跟着尤大大一起Vue3源码解读(一)-createApp

结语

本篇文章算是简单的开了个小头,接下来会对mount部分进行详细的分析和解读。

路漫漫其修远兮,吾将上下而求索

听说最近react出了个recoil的官方状态管理库,更加简单的管理state了,期待Vue后续的跟进。

作者:德莱问
链接:https://juejin.im/post/6881910894473773069

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:跟着尤大大一起Vue3源码解读(一)-createApp

链接:https://www.javascriptc.com/4166.html

« Node.js异步编程进化论
Vue3源码分析——数据侦测»
Flutter 中文教程资源

相关推荐

QR code