学习目标
1. vue是个啥
2. this.message为啥就可以访问到message的值
vue源码文件
vue是什么
在 core 文件的index中可以看出 Vue是在 instance/index 引入的
看到文件里面发现 vue 是一个 function 里面就只有执行了一个init方法
这个init 做了什么呢? 是在哪里定义的呢?
我们可以看到在定义vue之后 紧接着就 init了好几个方法
这个init的操作其实就是在 其中 initMixin 里面做的 (从名字就应该可以看出来)
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el) 做挂载的方法
}
}
}
在上面代码中我们可以看出 initState() 方法 这个方法里面 对我们直接用this访问data做了处理
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
这里面 initProps initMethods 然后 initData()
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
里面对数据做了proxy和observe操作
大致的简易版本如下
function Vue (options) {
this._init(options)
}
initMixin(Vue)
initState(Vue)
initData(Vue)
function initMixin(Vue) {
Vue.prototype._init = function (options) {
let vm = this
// options 参数处理
initState(vm)
}
}
function initState(Vue) {
initData(vm)
}
function initData(vm) {
let data = vm.$options.data
observe(data)
}
function observe(data) {
// 后续介绍
}
new Vue({
el: '#root'
})
所以第一个问题的结论vue是什么 其实就是一个函数
this.message是怎么访问到message里面的值的
在前面 initData方法中 proxy(vm, _data
, key) 有一个这样的方法,这里的 proxy是这样定义的
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
proxy(vm, `_data`, key)
在initData中 首先把vm.$option.data 里面的data 赋值给 vm._data 然后去对比props, methods里面有没有相同定义,如果有就不能定义相同名称的data属性。然后遍历data里面的属性值 对其进行 proxy 操作 修改这个值的get和set方法
const defineObject = {
enumerable: true,
configurable: true,
get: null,
set: null
}
function proxy (target, sourceKey, key) {
defineObject.get = function proxyGetter () {
return this[sourceKey][key]
}
defineObject.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, defineObject)
}
let Vue = {
'_data': {
a: 1,
b: 2,
c: 3
}
}
const keys = Object.keys(Vue['_data'])
let i = keys.length
while(i--) {
const key = keys[i]
proxy(Vue, '_data' , key)
}
console.log(Vue)
console.log(Vue.a)
console.log(Vue.b)
console.log(Vue.c)
执行结果:
从结果我们就可以看到 重写了data的set和get方法。
所以结论: 当我们获取this.message的时候其实获取的是 this._data.message的值