Foreword
By learning to understand Everest course Responsive principle Vue source.
Responsive principle
1. The core point: Object.defineProperty
2. Default Vue during initialization data using the attribute data will Object.defineProperty redefine all attributes, when the page corresponding to the acquired attribute. Collected will be dependent on (current collector assembly Watcher) If the property will notify dependency changed update operation.
What is dependent on the collection?
By Object.defineProperty
the time to re-define data attributes, intercept, then the actual rendering; that before the actual rendering process series is dependent on the logical collection 上边有说,会在依赖收集的时候为每一个属性创建一个watcher,如果属性发生变化,则通知对应的 watcher 更新视图
.
To look at the source
1, the first constructor initializes looks, src/core/instance/index.js
because we share the main principles of responsive data, that is, how the data is initialized Vue render and build listening, mainly to see stateMixin module
2, into the stateMixin
module, we look directly into the initData
function
function initData (vm: Component) { // 初始化data
let data = vm.$options.data // 获取到用户传入的data数据
data = vm._data = typeof data === 'function' // 模板语法与标准语法区分获取data数据
? 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) // es6 proxy 代理
}
}
/*
中间我就跳过了,看意思是非生产环境下,对 props , methods 的一些定义,声明做的判断,不允许重复声明
另外就是添加了 proxy , es6 新增代理属性 , 包含所有 Object.defineProperty 的功能, 重要的一点是解决 了不能对象监听的问题等。
*/
// observe data
observe(data, true /* asRootData */) // 重点在这儿,为每一个data属性创建一个watcher
}
重点 : observe(data, true /* asRootData */) // 重点在这儿,为每一个data属性创建一个wathcer
Next we went into the observer 类
3, src/core/observer/index.js
line 37
is below the Observer class, specifically it done, look at the code
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor(value: any) { // 构造函数
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
/*
观测呢分为两种,一种是数组,一种是对象
*/
if (Array.isArray(value)) { // 是数组
if (hasProto) {
protoAugment(value, arrayMethods) // 改写数组原型方法
} else {
copyAugment(value, arrayMethods, arrayKeys) // 复制数组已有方法
}
this.observeArray(value) // 深度观察数组中的每一项 , 下边方法
} else {
this.walk(value) // 重新定义对象类型数据 下边方法
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) { // 遍历对象
defineReactive(obj, keys[i]); // 定义响应式数据,这里可以看到 defineReactive 方法
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) { // 遍历数组
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]) // 观测数组中的每一项
}
}
}
By the code above, we see the main thing to do, is to distinguish between arrays and objects, and the data and objects traverse, create observer watcher
=>defineReactive 方法
There will come on top when it comes to the array observeArray
method, which is to traverse the call observer
, some types of data and determine whether the monitor had not been listening to the callback go back to create an observation.
src/core/observer/index.js
113 line
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
/*
不是对象不进行观测,如:不管是模板语法还是标准语法data均是一个对象
data () { 模板语法返回一个对象
return {}
}
new Vue ({ 标准语法 data 也是一个对象
data:{}
})
*/
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { // 已经被监听的,不会重复监听
ob = value.__b__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) && // 是否可扩展
!value._isVue
) {
ob = new Observer(value) //重点,重点,重点, 回调回去,观测对象类型
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
If it is not an array, then go directly to defineReactive
Method
src/core/observer/index.js
148 lines defineReactive 响应式数据绑定关键方法
where we use the word that is the Object.defineProperty () application; all initialization data will come here.
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val) // 是数组则递归观测
Object.defineProperty(obj, key, { // 重点
enumerable: true,
configurable: true,
get: function reactiveGetter () { // 数据的取值
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend() // 收集依赖 watcher
if (childOb) {
childOb.dep.depend() // 收集依赖
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) { // 数据的设置值
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify() // 触发数据对应的依赖进行更新 , 重点,重点,往下看
}
})
}
All collections observer Dep, which is a collection of wathcer, when creating the observed data is created for each attribute the watcher
observer ( Object.defineProperty方法的 get 里边
), then the trigger data update set
method calls dep.notify()
, look at the code
src/core/observer/dep.js
, line 13, Dep like to see saw constructor, add watcher, delete watcher and so on, then I think the point of view update methodnotify()
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () { // 通知存储的依赖更新
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 依赖中对应修改属性的update方法
}
}
}
update ()
Methods src/core/observer/wachter.js
166 rows, in fact, this is the definition of wachter
the observer class, inside various operation wachter
methods of the observer, such as: add, modify, remove the like.
to sum up
Well, the analysis here, in fact, has been very clear for data responsive principle, the overall process is: