Runtime Only VS Runtime+Compiler
通常我们利用vue_cli去初始化项目的时候会询问我们用Runtime Only版本还是Runtime+Compiler版本。下面我们来对比这两个版本。
- Runtime Only
我们在使用Runtime Only版本的Vue.js的时候,通常需要借助如webpack的vue-loader工具把.vue文件编译成JavaScript,因为是在编译阶段做的,所以它只包含运行时的Vue.js代码,因此代码体积也会更轻量。
- Runtime+Compiler
如果我们没有对代码做预编译,但又使用了Vue的template属性并传入一个字符串,则需要在客户端编译模版,如下所示:
// Runtime+Compiler
new Vue({
template: '<div>{{hi}}</div>'
})
// Runtime Only
new Vue({
render (h){
return h('div', this.hi)
}
})
在Vue.js 2.0中,最终渲染都是通过render函数,如果写template属性,则需要编译成render函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本。
很显然,这个编译过程对性能会有一定损耗,所以通常我们更推荐使用Runtime-Only 的Vue.js。
入口文件
我们通过Runtime+Compiler版本来追踪一下Vue的定义。找到以下目录文件
src/platforms/web/entry-runtime-with-compiler.js
。
/* @flow */
import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'
import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'
...
Vue.compile = compileToFunctions
export default Vue
当我们的项目里执行 import Vue from 'vue'
的时候,就是从这个入口执行代码来初始化Vue,我们从这个文件追踪下去。
Vue初始化定义
在上面的文件中我们看到import Vue from './runtime/index'
这一行,打开文件
// 1.src/platforms/web/runtime/index.js
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
...
export default Vue
这个文件也只是扩展了vue的属性和一些方法,继续往下走,根据import Vue from 'core/index'
,找到src/core/index.js
文件
// 2.src/core/index.js
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)
...
Vue.version = '__VERSION__'
export default Vue
接着往下追踪,import Vue from './instance/index'
// 3.src/core/instance/index.js
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
Congratulations!我们终于走到了终点,Vue原来是一个function,也就是说vue本质是使用一个构造函数进行实例化。
当我们使用new Vue()的时候,是调用了this._init(options)
,而这个方法是由initMixin(Vue)
挂载在Vue的原型prototype上。为什么不使用ES6的Class去实现而使用这个方式呢?主要是可以把功能扩展分散到多个模块中去实现,方便维护和管理;而不是在一个Class模块中实现所有。
initGlobalAPI
在初始化过程中,除了在prototype上扩展外,还给Vue这个对象本身扩展了全局的静态方法
// src/core/global-api/index.js
/* @flow */
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
...
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
...
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
这里就是Vue上扩展的一些全局方法的定义,比如Vue.util.Vue.util暴露的方法最好不要依赖,因为它可能会经常发生变化,是不稳定的。
总结
Vue本质上就是一个用Function实现的Class,然后它的原型prototype和它本身都扩展了一系列的方法和属性。