-
介绍
版本:2.5.17
-
从package.json文件开始
根目录下的package.json文件。在 web 应用下,我们来分析 Runtime + Compiler 构建出来的 Vue.js,它的入口是
src/platforms/web/entry-runtime-with-compiler.js,因此
这里我们只看第一个,也就是npm run dev所执行的命令。-
npm run dev
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
-
scripts/config.js
-
它会根据传入参数的不同,而根据不同版本的Vue.js形成构建工具rollup的不同配置 。config.js文章介绍请看(vue的源码学习之三——源码构建)
const builds = {
...
...
...
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
...
...
...
}
function genConfig (opts) {
...
}
if (process.env.TARGET) {
module.exports = genConfig(builds[process.env.TARGET])
} else {
exports.getBuild = name => genConfig(builds[name])
exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name]))
}
我们看到它调用了getConfig(builds[process.env.TARGET]),getConfig用于生成rollup的配置文件。builds是一个对象,获取它的process.env.TARGET值,在package.json中,我们看到dev中有TARGET:web-full-dev参数。
根据web-full-dev这个参数,我们到找到该版本的Vue.js的相关配置,配合alias.js这样入口文件我们就找到了,也就是/src/platforms/web/entry-runtime-with-compiler.js。
-
入口文件
-
寻找Vue对象的所在
入口文件:/src/platforms/web/entry-runtime-with-compiler.js。
在文件中我们看到:import Vue from './runtime/index'
src/platforms/web/runtime/index.js:
import Vue from 'core/index'
src/core/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) } // // 每个Mixin就是往vue定的原型方法 Vue.prototype.xx = yy 用function方便代码的管理,组装,添加原型方法各个文件各个模板,用class,需要一个主文件,添加主属性等。所以vue使用function initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default Vue
import Vue from './instance/index'
src/core/instance/index.js:
扫描二维码关注公众号,回复: 4810642 查看本文章至此我们找到了Vue对象的所在。
-
分析Vue对象
在src/core/instance/index.js中我们找到了定义Vue对象的所在之处。它的构造函数及其简单:
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) }
首先判断如果是不是生产环境,且不是通过new关键字来创建对象的话,就在控制台打印一个warning,之后调用了this._init(options)函数。
下面的几个函数,分别在Vue.prototype原型上绑定了一些实例方法,Vue的实例方法// _init initMixin(Vue) // $set、$delete、$watch stateMixin(Vue) // $on、$once、$off、$emit eventsMixin(Vue) // _update、$forceUpdate、$destroy lifecycleMixin(Vue) // $nextTick、_render、以及多个内部调用的方法 renderMixin(Vue)
我们终于看到了 Vue 的庐山真面目,它实际上就是一个用 Function 实现的类,我们只能通过
new Vue
去实例化它。为何 Vue 不用 ES6 的 Class 去实现呢?我们往后看这里有很多
xxxMixin
的函数调用,并把Vue
当参数传入,它们的功能都是给 Vue 的 prototype 上扩展一些方法,Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。这么做的好处是非常方便代码的维护和管理,这种编程技巧也非常值得我们去学习。-
initGlobalAPI
我们按照刚才所提到的文件引入顺序一步步来看。src/core/instance/index.js执行之后,是src/core/index.js文件
import { initGlobalAPI } from './global-api/index' import { isServerRendering } from 'core/util/env' import { FunctionalRenderContext } from 'core/vdom/create-functional-component' // 全局方法 initGlobalAPI(Vue) Object.defineProperty(Vue.prototype, '$isServer', { get: isServerRendering }) Object.defineProperty(Vue.prototype, '$ssrContext', { get () { /* istanbul ignore next */ return this.$vnode && this.$vnode.ssrContext } }) // expose FunctionalRenderContext for ssr runtime helper installation Object.defineProperty(Vue, 'FunctionalRenderContext', { value: FunctionalRenderContext }) Vue.version = '__VERSION__' export default Vue
该文件首先调用了initGlobalAPI,引自src/core/global-api/index.js。
- 扩展全局的静态方法
-
src/core/global-api/index.js中定义了给 Vue 这个对象本身扩展全局的静态方法
/* @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 {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
这里就是在 Vue 上扩展的一些全局方法的定义,Vue 官网中关于全局 API 都可以在这里找到,这里不会介绍细节,会在之后的章节我们具体介绍到某个 API 的时候会详细介绍。有一点要注意的是,Vue.util
暴露的方法最好不要依赖,因为它可能经常会发生变化,是不稳定的
-
总结
- 我们找到了vue构造函数,并知道了向vue原型中添加了很多实例方法
- 知道了通过 initGlobalAPI 向vue中添加了很多的静态方法
学习文档:https://ustbhuangyi.github.io/vue-analysis/components/lifecycle.html