1.从package.json文件开始
由上一节的学习,我们应该已经基本明白了Vue源码的整个构建的基本过程,下面我们来学习和了解Vue的入口文件
1.1 npm run dev
还是要看根目录下的package.json文件。这里我们只看第一个,也就是npm run dev所执行的命令。
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
- 由这行命令我们看到,用到的构建工具是rollup,我们在上一节已经讲到了,我们只需要知道它是一个类似于webpack的打包工具就行了,
- 接着 scripts/config.js,就知道了需要运行scripts/config.js了
- web-full-dev 是向scripts/config.js文件传递的参数
1.2 scripts/config.js
这个文件我们上一节也讲到了,他会根据传入参数的不同,而根据不同版本的Vue.js形成构建工具rollup的不同配置
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。
2.入口文件
2.1 寻找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 Vue from './instance/index'
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
至此我们找到了Vue对象的所在。
2.2分析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)
也许有同学会问,为何没有采用ES6的class,而是采用的ES5的new构造函数的方法,那是因为vue源码中需要向vue的prototype添加许多的方法,ES6操作起来就没有那么容易了,ES5的话写起来要简单许多,而且可以将不同的原型的方法写在不同的js中,方便管理。
2.3 initGlobalAPI
我们按照刚才所提到的文件引入顺序一步步来看。src/core/instance/index.js执行之后,是src/core/index.js文件
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Vue.version = '__VERSION__'
该文件首先调用了initGlobalAPI,引自src/core/global-api/index.js。
Vue.prototype[$isServer]属性,用于判断是不是服务端渲染。
2.3.1 扩展全局的静态方法:
src/core/global-api/index.js中定义了给 Vue 这个对象本身扩展全局的静态方法
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)
}
上面引入了全局的config,在src/core/config.js中
这里就是在 Vue 上扩展的一些全局方法的定义,Vue 官网中关于全局 API 都可以在这里找到,Vue.util 暴露的方法最好不要依赖,因为它可能经常会发生变化,是不稳定的。
3.总结
- 我们找到了vue构造函数,并知道了向vue原型中添加了很多实例方法
- 知道了通过 initGlobalAPI 向vue中添加了很多的静态方法