Observer
class Observer{
value;
dep;
vmCount;
constructor(value){
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(obj){
const keys = Object.keys(obj)
for(let i = 0;i < keys.length; i ++){
defineReactive(obj,keys[i])
}
}
observeArray(items){
for(let i = 0, l = items.length; i < l; i ++){
observe(items[i])
}
}
}
observe
export function observe(value,asRootData){
if(!isObject(value) || value instanceof VNode){
return
}
let ob
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){
ob = new Observer(value)
}
if(asRootData && ob){
ob.vmCount ++
}
return ob
}
defineReactive
export function defineReactive(obj,key,val,customSetter,shallow){
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj,key)
if(property && property.configurable === false){
return
}
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()
if(childOb){
childOb.dep.depend()
if(Array.isArray(value)){
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter(newVal){
const value = getter ? getter.call(obj) : val
if(newVal === value || (newVal !== newVal && value !== value)){
return
}
if(process.env.NODE_ENV !== 'production' && customSetter){
customSetter()
}
if(getter && !setter) return
if(setter){
setter.call(obj,newVal)
}else{
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
Dep
let uid = 0
export default class Dep{
static target;
id;
subs;
constructor(){
this.id = uid ++
this.subs = []
}
addSub(sub){
this.subs.push(sub)
}
removeSub(sub){
remove(this.subs,sub)
}
depend(){
if(Dep.target){
Dep.target.addDep(this)
}
}
notify(){
const subs = this.subs.slice()
if(process.env.NODE_ENV !== 'production' && !config.async){
subs.sort((a,b) => a.id - b.id)
}
for(let i = 0, l = subs.length; i < l; i++){
subs[i].update()
}
}
}
Dep.target = null
const targetStatck = []
function pushTarget(target){
targetStack.push(target)
Dep.target = target
}
function popTarget(){
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
Watcher
class Watcher{
vm,expression,cb,id,deep,user,lazy,sync,dirty,active,deps,newDeps,depIds,newDepIds,before,getter,value;
constructor(vm,expOrFn,cb,options,isRenderWatcher){
this.vm = vm
if(isRenderWatcher){
vm._watcher = this
}
vm._watchers.push(this)
if(options){
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
this.before = options.before
}else{
this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid
this.active = true
this.dirty = this.lazy
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : ''
if(typeof expOrFn === 'function'){
this.getter = expOrFn
}else{
this.getter = parsePath(expOrFn)
}
this.value = this.lazy ? undefined : this.get()
}
get(){
pushTarget(this)
let value
const vm = this.vm
try{
value = this.getter.call(vm,vm)
}catch(e){
...
}finally{
if(this.deep){
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
addDep(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)
}
}
}
cleanupDeps(){
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
}
update(){
if(this.lazy){
this.dirty = true
}else if(this.sync){
this.run()
}else{
queueWatcher(this)
}
}
run(){
if(this.active){
const value = this.get()
if(value !== this.value || isObject(value) || this.deep){
const oldValue = this.value
this.value = value
if(this.user){
try{
this.cb.call(this.vm,value,oldValue)
}catch(e){
handleError(e,this.vm,`callback for watcher "${this.expression}"`)
}
}else{
this.cb.call(this.vm,value,oldValue)
}
}
}
}
evalute(){
this.value = this.get()
this.dirty = false
}
depend(){
let i = this.deps.length
while(i --){
this.deps[i].depend()
}
}
teardown(){
if(this.active){
if(!this.vm._isBeingDestroyed){
remove(this.vm._watchers,this)
}
let i = this.deps.length
while(i --){
this.deps[i].removeSub(this)
}
this.active = false
}
}
}
总结--双向绑定
- defineReactive -- 通过Object.defineProperty重新定义了get和set
- getter: 判断原属性的get,通过dep添加watcher
- setter: 判断setter,通过dep调用watcher的update,调用watch回调或者update钩子函数;同时根据newVal设置childOb/Observer,在get时更新