1、思考
Vue的響應式到底要干什么?
- 無非就是要知道當你讀取對象的時候,要知道它讀了。要做一些別的事情
- 無非就是要知道當你修改對象的時候,要知道它改了。要做一些別的事情
- 所以要想一個辦法,把讀取和修改的動作變成一個函數(shù),讀取和修改的時候分別調(diào)用對應的函數(shù)
在ES6之前,只能通過Object.defineproperty 給它變成一個get和set函數(shù)。當讀取這個屬性的時候運行g(shù)et,修改這個屬性的時候運行set
在ES6之后,就能通過Porxy去代理整個對象
2、Vue2的做法?
針對某個對象中某個屬性的做法
通過Object.defineProperty去針對某個對象的屬性去進行監(jiān)聽? ? ? ? ? ? ? ? ? ? ? ? ? ?
const obj = { a: 1, b: 2, c: { d: 3, e: 4, }, } // 保存初始值 let v = obj.a Object.defineProperty(obj, 'a', { get() { console.log('a', '讀取') return v }, set(val) { // 當原來的值與重新賦值的值不一樣的時候才進行修改 if (val !== v) { console.log('a', '更改了') v = val } }, })
當我們?nèi)?strong>讀取 obj.a 這個屬性的時候 get 函數(shù)?就會調(diào)用。
當我們?nèi)?strong>修改?obj.a 這個屬性的時候 set 函數(shù)?就會調(diào)用。
針對某個對象中多個屬性的做法
在Vue2的源碼中,有一個函數(shù)叫做?observe(觀察器)。在這個函數(shù)中,去深度遍歷對象中的每一個屬性,給每一個屬性添加?Object.defineProperty,這樣就能對對象中的每一個屬性叫做監(jiān)聽。這個過程就叫做 觀察
const obj = { a: 1, b: 2, c: { d: 3, e: 4, }, } // 輔助函數(shù) 判斷這個值是不是一個對象 function _isObject(v) { return typeof v === 'object' && v !== null } function observe(obj) { for (const k in obj) { let v = obj[k] // 如果這一個屬性仍然是一個對象的話,就需要深度遍歷 if (_isObject(v)) { observe(v) } Object.defineProperty(obj, k, { get() { console.log(k, '讀取了') return v }, set(val) { // 當原來的值與重新賦值的值不一樣的時候才進行修改 if (val !== v) { console.log(k, '更改了') v = val } }, }) } } observe(obj)
當我們?nèi)?strong>讀取對象中的某個屬性的時候 get 函數(shù)?就會調(diào)用。
當我們?nèi)?strong>修改對象中的某個屬性的時候 set 函數(shù)?就會調(diào)用。
打印內(nèi)容解釋:
- obj.a? =? 3? ?更改
- obj.c.d? = 4? 先讀取 obj.c 的值,再更改?obj.c.d?的值
- obj.c.e? ?先讀取 obj.c 的值,再讀取?obj.c.e?的值
總結(jié)
- 在Vue2里面觀察的方式就是?深度遍歷每一個屬性 把每一個屬性的讀取和賦值變成函數(shù)get和set。
- 在這種做法下有一個天生的缺陷,由于它是針對每個屬性的監(jiān)聽,所以就必須要進行深度的遍歷,這樣會有效率損失。?
- 由于在?observe(obj)?觀察這個步驟里邊完成了深度遍歷,也就是說在這個時間點里邊,這些屬性被我們監(jiān)聽到了都被改成get和set了
但是這一步一旦做完了之后。再去新增的話它就不知道了。比如 obj.qwertr = 3?對于這個屬性而言,它就是沒有監(jiān)聽的,因為監(jiān)聽的步驟已經(jīng)結(jié)束了
這就是為什么Vue2它無法監(jiān)聽屬性的新增,當然也包括屬性的刪除。它也收不到通知。因為在?Object.defineProperty 既不會運行g(shù)et也不會運行set
3、Vue3的做法
其實核心道理都是一樣的。無論是Vue2還是Vue3,都必須要把讀取和賦值變成函數(shù)。只不過Vue3變成函數(shù)的方式不一樣。在Vue3里面不會去對對象的每一個屬性進行監(jiān)聽了,而是直接監(jiān)聽整個對象。將來不管是在這個對象中添加還是刪除屬性都不怕了。因為監(jiān)聽的是整個對象,這要動了這個對象就能收到通知。
做法
Vue3使用的是Proxy
const obj = { a: 1, b: 2, c: { d: 3, e: 4, }, } const proxy = new Proxy(obj, { // 讀這個對象的屬性的時候收到通知 get(target, k) { // target就是obj,k是屬性名 let v = target[k] console.log(k, '讀取') return v }, // 修改這個對象的屬性的時候收到通知 set(target, k, val) { // target就是obj,k是屬性名,val是新值 if (target[k] !== val) { target[k] = val console.log(k, '更改') return target[k] } }, // 刪除對象的屬性的時候收到通知 deleteProperty(target, k, val) { console.log(k, '刪除') return target[k] }, })
會產(chǎn)生一個代理對象propx,將來去讀屬性也好,重新給屬性賦值也好都是通過這個代理對象去做的。
文章來源:http://www.zghlxwxcb.cn/news/detail-854516.html
總結(jié)
- Proxy 對象可以直接代理整個對象,而不需要遍歷對象屬性進行劫持,這樣可以減少運行時的性能開銷。在 Vue 2 中,由于每個屬性都需要單獨設置 get?和 set,對于大量的屬性或嵌套屬性,這種劫持可能會導致性能下降。
- 另外,使用 Proxy 的方式更符合現(xiàn)代 JavaScript 的發(fā)展趨勢,更好地利用了 JavaScript 引擎的優(yōu)化。
4、總結(jié)
綜上所述,Vue 3 中使用 Proxy 對象代替了 Vue 2 中的?
Object.defineProperty
,帶來了更強大、更靈活和更高效的屬性攔截和代理功能,同時也提升了開發(fā)體驗和調(diào)試效率。這些改進使得 Vue 3 在處理 Props 的方式更加現(xiàn)代化和優(yōu)雅,提升了整體的性能和可維護性。文章來源地址http://www.zghlxwxcb.cn/news/detail-854516.html
到了這里,關于前端開發(fā)攻略---從源碼角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章讓你徹底弄懂響應式原理。的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!