Vue源码学习(二)之 找到Vue

寻找 Vue 的入口

  • src/platforms/web/entry-runtime-with-compiler 文件,它虽然export default Vue ,但是先从./runtime/index import 了 Vue
import Vue from './runtime/index'
  • runtime/indexcore/index中 import 了 Vue
import Vue from 'core/index'
  • core/indexinstance/index中 import Vue ,并初始化全局 API ,给 Vue 这个对象本身扩展全局的静态方法
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue)
  • instance/index 中我们会发现, Vue实际上是一个 Function 实现的类,我们只能通过 new Vue 去实例化它,为什么不用 class 来定义 Vue 呢

因为有很多 xxxMixin 的函数调用,把 Vue 当参数传入,它们的功能都是给 Vue 的 prototype 上扩展一些方法,Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。这么做的好处是非常方便代码的维护和管理

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
  • 这样一来我们就找到了 Vue 的入口
  • 那么一系列的 mixin 函数又做了什么呢,我在下面做了个总结
// initMixin(Vue)    src/core/instance/init.js **************************************************
Vue.prototype._init = function (options?: Object) {}

// stateMixin(Vue)    src/core/instance/state.js **************************************************
Vue.prototype.$data
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function(){}

// renderMixin(Vue)    src/core/instance/render.js **************************************************
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
Vue.prototype._s = _toString
Vue.prototype._v = createTextVNode
Vue.prototype._n = toNumber
Vue.prototype._e = createEmptyVNode
Vue.prototype._q = looseEqual
Vue.prototype._i = looseIndexOf
Vue.prototype._m = function(){}
Vue.prototype._o = function(){}
Vue.prototype._f = function resolveFilter (id) {}
Vue.prototype._l = function(){}
Vue.prototype._t = function(){}
Vue.prototype._b = function(){}
Vue.prototype._k = function(){}

// eventsMixin(Vue)    src/core/instance/events.js **************************************************
Vue.prototype.$on = function (event: string, fn: Function): Component {}
Vue.prototype.$once = function (event: string, fn: Function): Component {}
Vue.prototype.$off = function (event?: string, fn?: Function): Component {}
Vue.prototype.$emit = function (event: string): Component {}

// lifecycleMixin(Vue)    src/core/instance/lifecycle.js **************************************************
Vue.prototype._mount = function(){}
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype._updateFromParent = function(){}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}
  • 知道了 Vue 的构造函数以及一系列的 mixin 做了什么之后 ,我们在回溯到上一个,及core/index.js
initGlobalAPI(Vue)
  • initGlobalAPI 的作用是在 Vue 构造函数上挂载静态属性和方法,经历过initGlobalAPI 后 Vue 如下
Vue.config
Vue.util = util
Vue.set = set
Vue.delete = del
Vue.nextTick = util.nextTick
Vue.observable
Vue.options = {
    components: {
        KeepAlive
    },
    directives: {},
    filters: {},
    _base: Vue
}
Vue.use
Vue.mixin
Vue.cid = 0
Vue.extend
Vue.component = function(){}
Vue.directive = function(){}
Vue.filter = function(){}
  • 继续回溯,在platforms/web/runtime.js中主要做了三件事

一、覆盖 Vue.config 的属性,将其设置为平台特有的一些方法

二、Vue.options.directives 和 Vue.options.components 安装平台特有的指令和组件

三、在 Vue.prototype 上定义 _patch_ 和 $mount

  • 这个时候的 Vue 又增加了新的属性
// 安装平台特定的utils
Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.getTagNamespace = getTagNamespace
Vue.config.mustUseProp = mustUseProp
// 安装平台特定的 指令 和 组件
Vue.options = {
    components: {
        KeepAlive,
        Transition,
        TransitionGroup
    },
    directives: {
        model,
        show
    },
    filters: {},
    _base: Vue
}
Vue.prototype.__patch__
Vue.prototype.$mount
  • 最后我们回溯到了``entry-runtime-with-compiler````文件中,这个文件重要做了两件事

1、缓存来自 web-runtime.js 文件的 $mount 函数, const mount = Vue.prototype.$mount然后覆盖覆盖了 Vue.prototype.$mount

2、在 Vue 上挂载 compile Vue.compile = compileToFunctions,
compileToFunctions 函数的作用,就是将模板 template 编译为 render 函数

总结

  • 至此我们将 Vue 构造函数做了简单的一轮梳理

Vue.prototype 下的属性和方法的挂载主要是在 src/core/instance 目录中的代码处理的

Vue 下的静态属性和方法的挂载主要是在src/core/global-api 目录下的代码处理的

platforms/web/runtime/index.js 主要是添加 web 平台特有的配置、组件和指令,entry-runtime-with-compiler.js 给 Vue 的 $mount 方法添加 compiler 编译器,支持 template。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容