介绍
使用vue时候,要先new操作符调用,说明Vue是一个构造函数,所以我们首先要把vue的构造函数搞清楚
Vue构造函数的原型
npm run dev
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
调用scripts/config.js
// 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
}
由此可见入口文件是web/entry-runtime-with-compiler.js,输出为dist/vue.js,是一个umd模块,但是在入口文件中web是哪个文件?
这其实是一个别名配置,script/alias.js文件。
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
compiler: resolve('src/compiler'),
core: resolve('src/core'),
shared: resolve('src/shared'),
web: resolve('src/platforms/web'),
weex: resolve('src/platforms/weex'),
server: resolve('src/server'),
entries: resolve('src/entries'),
sfc: resolve('src/sfc')
}
由此可见web为 resolve(‘src/platforms/web’);
在src/platforms/web/entry-runtime-with-compiler.js文件中,看到这样一句
import Vue from './runtime/index'
然后打开runtime文件看到
import Vue from 'core/index'
看到core来自于scripts/alias.js配置,core指向src/core,打开之后发现
import Vue from './instance/index'
打开./instance/index.js文件
// 从五个文件导入五个方法(不包括 warn)
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'
// 定义 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)
}
// 将 Vue 作为参数传递给导入的五个方法
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
// 导出 Vue
export default Vue
由此才发现这是vue的真正来源
首先重init等五个文件导入方法,然后再定义Vue的构造函数,其中还提醒你使用new来操作,最后导出Vue。
打开./init.js,找到initMixin方法,
export function initMixin(Vue: class<Component>){
Vue.prototype._init = function(option?:Object){
//..._init 方法内容
}
}
发现在vue原型上添加了_init方法,在Vue的构造函数中也使用了这个方法,打开./state.js文件,stateMixin(数据)方法
export function stateMixin (Vue: Class<Component>) {
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
if (process.env.NODE_ENV !== 'production') {
dataDef.set = function () {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.',
this
)
}
propsDef.set = function () {
warn(`$props is readonly.`, this)
}
}
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
cb.call(vm, watcher.value)
}
return function unwatchFn () {
watcher.teardown()
}
}
}
在这基础上分析,使用Obejct.defineProperty在Vue.prototype上定义两个属性,
这就是
d
a
t
a
和
data和
data和props,两个属性分别写在dataDef和propsDef,仔细分析:
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
可以看出$data实际上是代理_data这个实例属性,在不是生产环境中,设置set,使其只读。
接着又在stateMixin在Vue原型上定义了三个方法:
s
e
t
,
set,
set,delete,$watch
在分析eventsMixin(事件),eventsMixin在Vue原型上添加是四个个方法:
Vue.prototype.$on = function (event:string | Array<string>,fn:Function):Component{};
Vue.prototype.$once = function(event:string,fn:Function):Component{}
Vue.prototype.$off= function(event?:string | Array<string>,fn?:Function):Component{};
Vue.prototype.$emit = function(event:string):Compoent{};
下一个分析lifecycle(生命周期),lifecycle在Vue原型上添加三个方法:
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}
最后一个renderMixin,在render.js中一开始就以Vue.prototype为参数调用了installRenderHelpers函数
export function installRenderHelpers (target: any) {
target._o = markOnce
target._n = toNumber
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
}
这个函数的作用就是为Vue.prototype添加一些方法。之后又为Vue.prototype添加两个方法 $nextTick和_render,最后经过renderMixin之后,Vue.prototype添加如下方法
Vue.prototype._o = markOnce
Vue.prototype._n = toNumber
Vue.prototype._s = toString
Vue.prototype._l = renderList
Vue.prototype._t = renderSlot
Vue.prototype._q = looseEqual
Vue.prototype._i = looseIndexOf
Vue.prototype._m = renderStatic
Vue.prototype._f = resolveFilter
Vue.prototype._k = checkKeyCodes
Vue.prototype._b = bindObjectProps
Vue.prototype._v = createTextVNode
Vue.prototype._e = createEmptyVNode
Vue.prototype._u = resolveScopedSlots
Vue.prototype._g = bindObjectListeners
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
至此instance/index.js代码运行完成,了解了*Mixin作用就是包装Vue.prototype,