重學Vue(二)--響應式

前言

不同於React的不可變數據,Vue的一套響應式機制成爲Vue的一套核心原則,從2.x基於Object.defineproperty到3.0即將推出的proxy和reflect,弄懂這一套還是很有必要的。

2.x

首先,對於2.x中的響應式原理,官網已經有很詳細的解釋了,這裏我們拋開整個響應式流程,只從JS的角度去研究一下。

let a = 2, obj = {}
Object.defineProperty(obj,'a',{
  get(){console.log('get');return a},
  set(v) { a = v;console.log('set')}
})
obj.a
VM766:2 get
2
obj.a = 3
VM766:3 set
3

借用此特性,我們可以實現一個Vue的響應式更新的函數:

function bindReactive(target, key, value){
  Object.defineProperty(target,key,{
    get(){return value},
    set(val) {
      value = val
      render()
    }
  })
}
function reactive(t){
  if(typeof t!=='object') return t
  for(let key in t){
    bindReactive(t,key,t.key)
  }
}
reactive(t)

上面只是簡化版的,爲了能夠遞歸的構建響應式數據,還需要實現遞歸。當然這樣的計算量也會很大,這也是2.x的一個很大的缺點。

function bindReactive(target, key, value){
  reactive(value)
  Object.defineProperty(target,key,{
    get(){return value},
    set(val) {
      reactive(value)
      value = val
      render()
    }
  })
}
function reactive(t){
  if(typeof t!=='object') return t
  for(let key in t){
    bindReactive(t,key,t.key)
  }
}
reactive(t)

同時,如果我們新增屬性和刪除屬性也不會走Object.defineProperty。

最後對於數組,我們這樣做是不成的,需要去重新定義數組原型。

const proto = Array.prototype
const arrProto = Object.create(proto)
arrProto['push'] = function(){
  render()
  proto['push'].call(this,...arguments)
}

vue 3.0

新版本的響應式更新是一大看點,使用到了Proxy和Reflect。

let handle = {get:function(t,k){console.log('get');return t[k]}}
let  p = new Proxy({},handle)
> undefined
p.a=3
> 3
p.a
> get
> 3
p
> Proxy {a: 3}

關於reflect,這裏有個小例子:

// 老寫法
'assign' in Object // true

// 新寫法
Reflect.has(Object, 'assign') // true

兩種寫法是等價的,即把Object的操作變成函數式的行爲,和proxy放在一塊使用。

因此基於這個我們實現一套新的響應式:

function reactive(target){
  const handler = {
    get(target, key){
      const result= Reflect.get(target,key)
      return result
    }
    set(target,key,value){
      const result = Reflect.set(target, key, val)
      // 其他邏輯,包括渲染
      return result
    }
  }
  return new Proxy(target, handler)
}

這樣既拜託了了遞歸的不足,數組無法響應式的問題,同時也可以實現屬性的增刪(set包括增刪的功能,還有deleteProperty方法,代碼並沒有體現),以及是否增刪成功的問題(通過Reflect和返回的result)。

唯一的問題就是兼容性,畢竟proxy是沒有辦法polyfill的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章