[TOC]
******
## 4 [\core\observer\] 數據綁定目錄
>注:本節機制見基礎原理的數據綁定
### 4-1 目錄文件
~~~
\core\observer\
index.js ;數據監控器observer
dep.js ;消息訂閱器
watcher.js ;消息訂閱者
batcher.js ;刷新
array.js ;數組監控
~~~
### 4-2 index.js(數據監控器)
>[info] import
~~~
;(導入) 消息訂閱器,數組監控器,基礎工具
import Dep from './dep'
import { arrayMethods } from './array'
import {
def,
remove,
isArray,
isObject,
isPlainObject,
hasProto,
hasOwn,
isReserved
} from '../util/index'
~~~
>[info] module
~~~
;數據監控方法鍵名
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
;是否需要遞歸轉換響應屬性
export const observerState = {
shouldConvert: true
}
;創建數據監視器
export function Observer (value) {
this.value = value
this.dep = new Dep()
def(value, '__ob__', this)
;數組類型值監視
if (isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
;普通類型值監視
this.walk(value)
}
}
;普通數據遍歷轉換
Observer.prototype.walk = function (obj) {
for (const key in obj) {
this.convert(key, obj[key])
}
}
;數組數據遍歷轉換
Observer.prototype.observeArray = function (items) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
;數據轉換為響應屬性
Observer.prototype.convert = function (key, val) {
defineReactive(this.value, key, val)
}
;添加數據監視器與vm關聯
Observer.prototype.addVm = function (vm) {
(this.vms || (this.vms = [])).push(vm)
}
;注銷數據監視器與vm關聯
Observer.prototype.removeVm = function (vm) {
remove(this.vms, vm)
}
;數組類型值參數proto實現
function protoAugment (target, src) {
/* eslint-disable no-proto */
target.__proto__ = src
/* eslint-enable no-proto */
}
;數組類型值參數copy實現
function copyAugment (target, src, keys) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key])
}
}
;創建數據的監視器并關聯vm
export function observe (value, vm) {
if (!isObject(value)) {
return
}
let ob
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
observerState.shouldConvert &&
(isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (ob && vm) {
ob.addVm(vm)
}
return ob
}
;創建對象的屬性位響應,這里實現數據雙向綁定
export function defineReactive (obj, key, val) {
;set操作消息訂閱數組
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
let childOb = 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()
if (childOb) {
childOb.dep.depend()
}
if (isArray(value)) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i]
e && e.__ob__ && e.__ob__.dep.depend()
}
}
}
return value
},
set: function reactiveSetter (newVal) {
;獲取值,并比較新舊值
const value = getter ? getter.call(obj) : val
if (newVal === value) {
return
}
;值不同進行修改
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
;創建位數據監視器
childOb = observe(newVal)
;廣播到消息訂閱數組中
dep.notify()
}
})
}
;對象屬性修改快捷方式
export function set (obj, key, val) {
;數組類型值屬性修改
if (isArray(obj)) {
return obj.splice(key, 1, val)
}
;普通對象屬性值修改
if (hasOwn(obj, key)) {
obj[key] = val
return
}
;Vue對象屬性值修改obj._data
if (obj._isVue) {
set(obj._data, key, val)
return
}
;響應數據值__ob__修改
const ob = obj.__ob__
if (!ob) {
obj[key] = val
return
}
ob.convert(key, val)
ob.dep.notify()
;vms相應修改
if (ob.vms) {
let i = ob.vms.length
while (i--) {
const vm = ob.vms[i]
proxy(vm, key)
vm.$forceUpdate()
}
}
;返回修改后的值
return val
}
;刪除對象屬性快捷方法
export function del (obj, key) {
;普通對象
if (!hasOwn(obj, key)) {
return
}
delete obj[key]
;響應對象
const ob = obj.__ob__
if (!ob) {
if (obj._isVue) {
delete obj._data[key]
obj.$forceUpdate()
}
return
}
ob.dep.notify()
if (ob.vms) {
let i = ob.vms.length
while (i--) {
const vm = ob.vms[i]
unproxy(vm, key)
vm.$forceUpdate()
}
}
}
;$與_數據代理模擬生成vm._data
export function proxy (vm, key) {
if (!isReserved(key)) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return vm._data[key]
},
set: function proxySetter (val) {
vm._data[key] = val
}
})
}
}
;數據代理注銷模擬
export function unproxy (vm, key) {
if (!isReserved(key)) {
delete vm[key]
}
}
~~~
>[info] export
~~~
;(導出)數據監控器
export function Observer (value) {}
;(導出)數據監控注冊到vm
export function observe (value, vm) {}
;(導出)生成響應屬性
export function defineReactive (obj, key, val) {}
;(導出)修改對象屬性
export function set (obj, key, val) {}
;(導出)刪除對象屬性
export function del (obj, key) {}
;(導出)屬性代理模擬
export function proxy (vm, key) {}
;(導出)屬性代理注銷模擬
export function unproxy (vm, key) {}
~~~

### 4-3 dep.js(消息訂閱器)
>[info] import
~~~
;(導入)刪除數組項
import { remove } from '../util/index'
~~~
>[info] module
~~~
;dep的id
let uid = 0
;Dep結構組織id和subs數組
export default function Dep () {
this.id = uid++
this.subs = []
}
;消息訂閱數組狀態標志;用于綁定數據監視器
Dep.target = null
;消息訂閱數組添加消息訂閱者
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}
;消息訂閱數組刪除消息訂閱者
Dep.prototype.removeSub = function (sub) {
remove(this.subs, sub)
}
;消息訂閱者添加到消息訂閱數組
Dep.prototype.depend = function () {
Dep.target.addDep(this)
}
;廣播消息訂閱數組用于刷新
Dep.prototype.notify = function () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
~~~
>[info] export
~~~
;(導出)消息訂閱數組
export default function Dep () {}
~~~

### 4-4 watcher.js(消息訂閱者)
>[info] import
>[info] module
>[info] export

### 4-5 batcher.js(刷新)
>[info] import
~~~
;(導入)消息訂閱數組,刷新模塊,基礎工具
import Dep from './dep'
import { pushWatcher } from './batcher'
import {
warn,
remove,
extend,
isArray,
isObject,
parsePath,
_Set as Set
} from '../util/index'
~~~
>[info] module
~~~
;uid,前一個Watcher
let uid = 0
let prevTarget
;消息訂閱者
export default function Watcher (vm, expOrFn, cb, options) {
;初始化參數
if (options) {
extend(this, options)
}
;待解析表達式
const isFn = typeof expOrFn === 'function'
;vm與vm._watchers
this.vm = vm
vm._watchers.push(this)
;解析表達式
this.expression = expOrFn
this.cb = cb
;uid
this.id = ++uid // uid for batching
;狀態控制
this.active = true
this.dirty = this.lazy // for lazy watchers
;Dep管理
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
;表達式解析為getter
if (isFn) {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = function () {}
process.env.NODE_ENV !== 'production' && warn(
'Failed watching path: ' + expOrFn +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
)
}
}
;值獲取
this.value = this.lazy
? undefined
: this.get()
this.queued = this.shallow = false
}
;Watcher.get():watcher值獲取
Watcher.prototype.get = function () {
;get前回調
this.beforeGet()
;getter調用
const value = this.getter.call(this.vm, this.vm)
;遞歸調用
if (this.deep) {
traverse(value)
}
;get后回調
this.afterGet()
;返回獲取的值
return value
}
;get前依Dep保存
Watcher.prototype.beforeGet = function () {
prevTarget = Dep.target
Dep.target = this
}
;Watcher.addDep()添加Watcher到Dep
Watcher.prototype.addDep = function (dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
;get后dep恢復
Watcher.prototype.afterGet = function () {
Dep.target = prevTarget
let i = this.deps.length
while (i--) {
const dep = this.deps[i]
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this)
}
}
let tmp = this.depIds
this.depIds = this.newDepIds
this.newDepIds = tmp
this.newDepIds.clear()
tmp = this.deps
this.deps = this.newDeps
this.newDeps = tmp
this.newDeps.length = 0
}
;Watcher.update():watcher刷新
Watcher.prototype.update = function (shallow) {
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
this.shallow = this.queued
? shallow
? this.shallow
: false
: !!shallow
this.queued = true
;添加到刷新隊列
pushWatcher(this)
}
}
;Watcher.run() 刷新過程
Watcher.prototype.run = function () {
if (this.active) {
const value = this.get()
if (
value !== this.value ||((isObject(value) || this.deep) && !this.shallow)
) {
const oldValue = this.value
this.value = value
this.cb.call(this.vm, value, oldValue)
}
this.queued = this.shallow = false
}
}
;Watcher.evaluate計算Watcher表達式的值
Watcher.prototype.evaluate = function () {
const current = Dep.target
this.value = this.get()
this.dirty = false
Dep.target = current
}
~~~
>[info] export
~~~
;(導出)消息訂閱者
export default function Watcher{}
~~~

### 4-6 array.js(數組監控)
>[info] import
~~~
;(導入) 對象屬性定義
import { def } from '../util/index'
~~~
>[info] module
~~~
;數組原型
const arrayProto = Array.prototype
;數組原型方法
export const arrayMethods = Object.create(arrayProto)
;方法修改
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function (method) {
;原始方法
const original = arrayProto[method]
;注冊對象屬性為擴展方法
def(arrayMethods, method, function mutator () {
;參數獲取
let i = arguments.length
const args = new Array(i)
while (i--) {
args[i] = arguments[i]
}
;原始方法調用
const result = original.apply(this, args)
;數據監聽實現
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
inserted = args
break
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
;數組數據監聽
if (inserted) ob.observeArray(inserted)
;依賴刷新
ob.dep.notify()
;返回結果
return result
})
})
~~~
>[info] export
~~~
;(導出)數組方法
export const arrayMethods
~~~

- 概述
- 框架結構
- 編譯入口(\entries)
- web-compiler.js(web編譯)
- web-runtime.js(web運行時)
- web-runtime-wih-compiler.js(web編譯運行)
- web-server-renderer.js(web服務器渲染)
- 核心實現 (\core)
- index.js(核心入口)
- config.js(核心配置)
- core\util(核心工具)
- core\observer(雙向綁定)
- core\vdom(虛擬DOM)
- core\global-api(核心api)
- core\instance(核心實例)
- 模板編譯(\compiler)
- compiler\parser(模板解析)
- events.js(事件解析)
- helper.js(解析助手)
- directives\ref.js(ref指令)
- optimizer.js(解析優化)
- codegen.js(渲染生成)
- index.js(模板編譯入口)
- web渲染(\platforms\web)
- compiler(web編譯目錄)
- runtime(web運行時目錄)
- server(web服務器目錄)
- util(web工具目錄)
- 服務器渲染(\server)
- render-stream.js(流式渲染)
- render.js(服務器渲染函數)
- create-renderer.js(創建渲染接口)
- 框架流程
- Vue初始化
- Vue視圖數據綁定
- Vue數據變化刷新
- Vue視圖操作刷新
- 框架工具
- 基礎工具(\shared)
- 模板編譯助手
- 核心實例工具
- Web渲染工具
- 基礎原理
- dom
- string
- array
- function
- object
- es6
- 模塊(Module)
- 類(Class)
- 函數(箭頭)
- 字符串(擴展)
- 代理接口(Proxy)
- 數據綁定基礎
- 數據綁定實現
- mvvm簡單實現
- mvvm簡單使用
- vdom算法
- vdom實現
- vue源碼分析資料