前言
本文主要记一下看Vue源码的步骤,以 Vue 2.6 为例,研究一下 Vue 到底对 data 做了什么
第一步
第二步
点击 dev 输入自己想要看的分支版本,切换到这个分支
选择 src 这个目录
进入后 选择 core
想研究哪个就去看哪个目录
这里去看一下 Vue 的实例
第三步
点开 index.js
import { initMixin } from './init' // 4. initMixin 从这里来的,于是去找当前目录的 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) { // 1. 构造了一个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) // 2. 初始化了 options,那这个 _init, 做了啥?
}
initMixin(Vue) // 3. 看名字 init,利用排除法 感觉是它做的,然后去看 initMixin 哪来的( cmd/ctrl + f 搜索一下)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
第四步
来到当前目录的 init.js
在 Vue 原型上加了 init属性,接收一个 options,那它到底做了啥? 于是就往下面找
感觉是这个
initState(vm) 因为找不到 data,退而求其次看它,其他的看的不像
于是去看initState(vm)它是从哪里来的
去找当前目录下的 state 目录
第五步
全局查找 initState
if (opts.data) {
initData(vm) // 如果你传了data 它就会初始化 data
} else {
observe(vm._data = {}, true /* asRootData */)
}
全局查找 initData
function initData (vm: Component) {
let data = vm.$options.data // 拿到 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) // 对 vm 进行代理,用 _data 进行存储那个 key ,这段代码在源码147行
}
}
// observe data
observe(data, true /* asRootData */) // 监听原始对象
}
去看看代理咋写的
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) // 最核心的一行,它会对vm 进行代理
}
以上代码使用了 get set 然后把它传给 Object.defineProperty,这个 key 有 get set 属性
通过代理可以访问到原始的data
vm 当了 _data 这个代理之后,调用 observe(data)
第六步
全局查找 observe 看它从哪里来的
进入 ../observer/index.js 文件夹瞧瞧
找observe 的函数
以上代码的 value 表示, 它会接收一个值(这个值就是那个 data)
在 124 行,来看看这个观察者做了啥
如果是个数组就观察,如果不是就 walk
来看看这个 walk 做了啥
它对这个对象的每一个属性,都会 defineReactive
在看看这个 defineReactive 是啥
这个函数会得到一个对象和对应的 key,这里的 val 是最开始的那个值 ,这个 key 做啥?
它对原来的属性进行了一个覆盖,对它进行 get 操作它就会先去读它的 val(161行)
当我对右边这个 data 进行写的时候,会去尝试读(174行),写的时候又回到 val (188行)了
总结
不要随便的去看源码,得知道原理才可以去看源码,连原理都不知道怎么通过源码学习呢?基础知识都不牢固,学***,所以通过源码学习得要有个前提,那就是你是高手, 我也看不懂= =,小菜鸟一个,本文只是简单记录一哈,看源码学习不可取,先把基础打牢,有些东西不需要看源码也能知道大概的是怎么写的。\
对 Object.defineProperty 不了解的可以看看这篇文章