数据劫持
初始化数据
检测属性是否被重复声明,并对属性进行观测
export function initState(vm: Component) {
vm._watchers = []
const opts = vm.$options
// vue 的数据来源:属性、方法、数据、计算属性、watch(按顺序解析)
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)
}
}
// 数据初始化工作
function initData(vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
// 1.数据不是对象则发生异常
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
)
}
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
// 2.校验数据是否在 method 中已经声明过
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
)
}
}
// 3.校验数据是否在属性中已经声明过
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)) {
// 4.将 _data 代理到实例上
proxy(vm, `_data`, key)
}
}
// 5.观测数据
observe(data, true /* asRootData */)
}
数据观测
export function observe(value: any, asRootData: ?boolean): Observer | void {
// 1.如果不是对象直接 return
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 2.如果已经观测过则直接返回上次观测的实例
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { //
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
// 3.如果可以观测就进行观测
ob = new Observer(value)
}
// 4.如果是根数据 vmCount 标注为 1
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
// 只观测对象数据类型,已经观测的不在进行观测,不能扩展的属性不进行观测
export class Observer {
constructor(value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
// 1.数组:重写数组原型方法
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 2.观测数组中是对象类型的数据
this.observeArray(value)
} else {
// 3.对象的话使用 defineProperty 重新定义属性
this.walk(value)
}
}
walk(obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray(items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
对象的观测
对象的观测就是将所有属性使用 defineProperty 进行重新定义,想减少观测可以使用 Object.freeze 冻结对象;

export function defineReactive(
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 1.如果对象不可配置则直接退出
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// 2.获取 getter 和 setter
const getter = property && property.get
const setter = property && property.set
// 3.重新定义 set 和 get 方法
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
return value
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
}
})
}
数组的观测
通过重写原型方法来实现的

let oldArrayProtoMethods = Array.prototype;
export let arrayMethods = Object.create(oldArrayProtoMethods);
let methods = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
];
methods.forEach(method => {
arrayMethods[method] = function (...args) {
const result = oldArrayProtoMethods[method].apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2)
default:
break;
}
if (inserted) ob.observeArray(inserted); // 对新增的每一项进行观测
return result
}
})
依赖收集
图解

vue 的渲染过程是通过渲染 watcher 来实现的
创建 watcher 时,会对变量进行取值;
export function mountComponent( vm: Component, el: ?Element, hydrating?: boolean): Component {
vm.$el = el
let updateComponent = () => { // 执行时会去实例上取值
vm._update(vm._render(), hydrating)
}
// 调用 updateComponent 这个方法
new Watcher(vm, updateComponent, noop, {}, true /* isRenderWatcher */)
hydrating = false
return vm
}
对象依赖收集
对于对象而言,取值就会触发 get 方法,在 defineProperty 的 get 中进行依赖收集,在 set 中通知watcher 进行更新操作;
class Watcher {
constructor(
vm: Component,
expOrFn: string | Function, // updateComponent
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) vm._watcher = this
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString()
: ''
// 将 updateComponent 放到 this.getter 上
this.getter = expOrFn
this.value = this.lazy ? undefined : this.get() // 执行 get 方法
}
get() {
pushTarget(this) // Dep.target = 渲染 watcher
let value
const vm = this.vm
try {
// 开始取值,那么在 get 方法中就可以获取到这个全局变量 Dep.target
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
if (this.deep) {
traverse(value)
}
popTarget() // 结束后进行清理操作
this.cleanupDeps()
}
return value
}
}
// Observer
// 渲染 watcher 默认会调用 Observer 中属性的 get 方法也就是传入的 updateComponent 方法,在调用此方法前先将 watcher 存到全局中,这样再取值时可以获取到这个 watcher
const dep = new Dep()
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if (Dep.target) { // 如果有 watcher,将 dep 和 watcher 对应起来
dep.depend()
}
return value
}
set: function reactiveSetter(newVal) {
dep.notify(); // 当属性更新时通知 dep 中的所有 watcher 进行更新操作
}
数组的依赖收集
watcher 和 dep 的关系是多对多的关系,一个属性一个 dep,每个 dep 里存放着多个 watcher,同时 watcher 也会记住对应的 dep
// Observer
let childOb = !shallow && observe(val)
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) { // 如果值也是个对象的话,对这个值进行依赖收集
childOb.dep.depend()
if (Array.isArray(value)) { // 如果是数组对数组中的内容继续进行依赖收集
dependArray(value)
}
}
}
return value
}
// 调用数组方法时进行 watcher 的更新操作
methodsToPatch.forEach(function (method) {
ob.dep.notify()
})
export default class Dep {
constructor() {
this.id = uid++
this.subs = []
}
addSub(sub: Watcher) {
this.subs.push(sub)
}
depend() {
if (Dep.target) {
Dep.target.addDep(this) // 让 watcher 记住自己
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 让存储的 watcher 依次调用更新方法
}
}
}
class Watcher {
constructor(
vm: Component,
expOrFn: string | Function, // updateComponent
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString()
: ''
// 1.将 updateComponent 放到 this.getter 上
this.getter = expOrFn
this.value = this.lazy ? undefined : this.get() // 2.执行 get 方法
}
addDep(dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
// watcher 中会进行虑重操作,实现 watcher 和 dep 互相记忆
if (!this.depIds.has(id)) {
// 3.让 dep 记录 watcher
dep.addSub(this)
}
}
}
update() {
queueWatcher(this)
}
}
异步更新
为了防止多次更改同一个属性(他们依赖的 watcher 相同)或者多次修改不同属性会导致频繁更新渲染;
export function queueWatcher(watcher: Watcher) {
const id = watcher.id
// 1.判断 watcher 是否已经存放了,对相同 watcher 进行过滤操作
if (has[id] == null) {
has[id] = true
// 2.将 watcher 存放到队列中
queue.push(watcher)
if (!waiting) { // 当同步的更改状态完毕时再去更新 watcher
waiting = true
nextTick(flushSchedulerQueue) // 在下一队列中清空 queue
}
}
}
初始化流程
上一篇