您好,登錄后才能下訂單哦!
前言
前段時間做一個運營活動的項目,上線后產品反饋頁面埋點不對,在排查過程中發(fā)現(xiàn),問題竟然是由于Vue中的data初始值導致,而data的初始值來自于props。為方便描述,現(xiàn)將問題抽象如下:
一、現(xiàn)象
代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用props初始化data中變量</title> <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script> </head> <body> <div id="app"> <user-info :user-data="user"></user-info> </div> <script> //全局組件 let userInfo = Vue.component('userInfo' ,{ name: 'user-info', props: { userData: Object }, data() { return { userName: this.userData.name } }, template: ` <div> <div>姓名:{{userName}}</div> <div>性別:{{userData.gender}}</div> <div>生日:{{userData.birthday}}</div> </div> ` }); //Vue實例 new Vue({ el: '#app', data: { user: { name: '', gender: '', birthday: '' } }, created(){ this.getUserData(); }, methods:{ getUserData(){ setTimeout(()=>{ this.user = { name: '于永雨', gender: '男', birthday: '1991-7' } }, 500) } }, components: { userInfo } }); </script> </body> </html>
代碼解讀:
結果:
頁面初始化后,姓名、性別、生日都顯示為空,500毫秒后性別和生日顯示正常結果,僅姓名沒有變化。
為什么會這樣呢?
我最初的想法:user.name是String,屬于基本數(shù)據(jù)類型,用它給子組件data中userName賦值,屬于基本數(shù)據(jù)類型賦值,所以當父組件中user.name變化時,子組件中userName并不會隨之變化。
是這樣的嗎?于是我決定將user.name改為對象,通過引用數(shù)據(jù)類型賦值,然后觀察是否符合預期。代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用props初始化data中變量-對象形式</title> <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script> </head> <body> <div id="app"> <user-info :user-data="user"></user-info> </div> <script> //全局組件 let userInfo = Vue.component('userInfo' ,{ name: 'user-info', props: { userData: Object }, data() { return { userName: this.userData.name } }, template: ` <div> <div>姓名:{{userName.text}}</div> <div>性別:{{userData.gender}}</div> <div>生日:{{userData.birthday}}</div> </div> ` }); //Vue實例 new Vue({ el: '#app', data: { user: { name: {text: ''}, gender: '', birthday: '' } }, created(){ this.getUserData(); }, methods:{ getUserData(){ setTimeout(()=>{ this.user = { name: {text: '于永雨'}, gender: '男', birthday: '1991-7' } }, 500) } }, components: { userInfo } }); </script> </body> </html>
運行結果:姓名仍然沒有值,和第一次結果一樣!??!
二、原因
那么,原因到底是什么呢?百思不得解,后來和小伙伴們討論時,有人提出:會不會因為data在初始化時深拷貝?
我覺得這種解釋比較靠譜,于是去收集證據(jù),首先去Vue官網翻了一下關于data的文檔,其中:
當看到"遞歸地"那個詞,基本上就能斷定上面的推論是正確的,因為深拷貝的核心原理就是遞歸。
原來,Vue初始化時會遞歸地遍歷data所有的屬性,并使用Object.defineProperty把這些屬性全部轉為getter/setter,用于實現(xiàn)雙向綁定。官方文檔在Reactivity in Depth一章明確有說:
還順便解釋了一下為什么Vue不支持IE8的原因:IE8不支持Object.defineProperty。
三、解決辦法
既然因為data深拷貝的原因,data無法隨著props的變化而更新,我們很自然的就想到Vue中有監(jiān)聽作用的兩個功能:watch、computed。
修改代碼如下,觀察結果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>解決方案:watch、computed</title> <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script> </head> <body> <div id="app"> <user-info :user-data="user"></user-info> </div> <script> //全局組件 let userInfo = Vue.component('userInfo' ,{ name: 'user-info', props: { userData: Object }, data() { return { userName: this.userData.name } }, computed: { computedUserName(){ return this.userData.name } }, watch: { 'userData.name': function (val) {//監(jiān)聽props中的屬性 this.userName = val; } }, template: ` <div> <div>姓名(watch):{{ userName }}</div> <div>姓名(computed):{{ computedUserName }}</div> <div>性別:{{ userData.gender }}</div> <div>生日:{{ userData.birthday }}</div> </div> ` }); //Vue實例 new Vue({ el: '#app', data: { user: { name: '', gender: '', birthday: '' } }, created(){ this.getUserData(); }, methods:{ getUserData(){ setTimeout(()=>{ this.user = { name: '于永雨', gender: '男', birthday: '1991-7' } }, 500) } }, components: { userInfo } }); </script> </body> </html>
運行結果
完美?。?!
四、總結:關于Vue中props的要點
事后又仔細翻了一下關于props的文檔:
大概梳理一下:
1.props是單向數(shù)據(jù)流:父組件的數(shù)據(jù)變化,通過props實時反應在子組件中,反之不然
2.不允許在子組件中直接操作props
3.可以變相操作props
(1)在data中聲明局部變量,并用props初始化,弊端:局部變量不隨著props更新而更新
(2)在computed中對props值轉換后輸出
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。