Vue數(shù)據(jù)響應(yīng)式

數(shù)據(jù)響應(yīng)式

主要原理:深入響應(yīng)式原理
內(nèi)容:深入理解options.data

Vue對(duì)data做了什么岖妄?

const myData = {
  n: 0
};
console.log(myData); 

new Vue({
  data: myData,
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

setTimeout(() => {
  myData.n += 10;
  console.log(myData); 
}, 3000);

3s后n變成10防泵,這次沒有在vm里面加氧腰,而是在外面买猖,說明在外面也可以變更myData泻肯。

一般我們是在vm內(nèi)變更myData的

new Vue({
  template:`
  <div>
    {{n}}
    <button @click="add"> +1 </button>
  </div>
  `,
  methods:{
    add(){
    //this.n +=10
      myData.n +=10
    }
  }
}).$mount("#app")

平常我們都用this.n海洼,今天試試myData.n
分別打印出myData剛聲明和3s后的結(jié)果休里,如果第1次是n:0,那第2次就應(yīng)該是n:10蛆挫,看下結(jié)果:
[圖片上傳失敗...(image-9e0436-1648768426411)]

第2次并不是n:10,那這個(gè)n:(...)是什么呢妙黍?
我們需要先學(xué)習(xí)ES6getter悴侵、setter
示例:要想得到姓名就要調(diào)用函數(shù)obj1.姓名()所以括號(hào)不能省,但是ES6的get語法可以實(shí)現(xiàn)obj1.姓名刪掉括號(hào)拭嫁。

// 需求一可免,姓名不要括號(hào)也能得出值
let obj1 = {
  姓: "高",
  名: "圓圓",
  get 姓名() { //以函數(shù)的形式定義的屬性
    return this.姓 + this.名; 
  }
};
console.log("需求一:" + obj1.姓名);
// 總結(jié):getter就是不加括號(hào)的函數(shù)而已。

// 需求二:姓名可以被寫
let obj2 = {
  姓: "高",
  名: "圓圓",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.substring(1)
  }
};
obj2.姓名 = '高媛媛'
console.log(`需求二:姓 ${obj2.姓}做粤,名 ${obj2.名}`)
// 總結(jié):setter用= xxx觸發(fā)set函數(shù)

把obj2打出來
[圖片上傳失敗...(image-945ae2-1648768426411)]

瀏覽器說你確實(shí)可以對(duì)姓名進(jìn)行讀和寫浇借,但是并不存在一個(gè)叫姓名的屬性。但是你可以通過get和set設(shè)置它怕品。

推斷1: 由此推斷之前得到的n也是一個(gè)getter妇垢、setter
get 姓名、set 姓名說明姓名:(...)不是一個(gè)真實(shí)的屬性
推斷2:n:(...)并不存在屬性n,而是有個(gè)get n堵泽、set n,它們來模擬對(duì)n的讀寫操作修己。

那為什么要把n變成get n、set n呢迎罗?
需要再學(xué)下Object.defineProperty()
之前在使用getter睬愤、setter時(shí),是在定義這個(gè)對(duì)象時(shí)直接使用的纹安。在定義完一個(gè)對(duì)象之后尤辱,要想再添加新的get set時(shí)只能用Object.defineProperty()

Object.defineProperty(obj2,'xxx',{ 
//給obj2添加虛擬屬性xxx,注意xxx是不存在的厢岂。
  var _xxx=0 //_xxx是用來存放set值的
  get(){
    return _xxx
  },
  set(value){
    _xxx=value
  }
})
let data0 = { n: 0 }
// 需求一:用Object.defineProperty定義n
let data1 = {}
Object.defineProperty(data1, 'n', { //給data1添加虛擬屬性n,n=0
  value: 0
})
console.log(`需求一:${data1.n}`) //0
// 這語法把事情搞復(fù)雜了光督?非也,繼續(xù)看塔粒。

// 需求二:n不能小于0
let data2 = {}
data2._n = 0 //用_n存儲(chǔ)n的值
Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){ 
  //set可以添加判斷:小于0直接return,否則將n值置為最新value
    if(value < 0) return 
    this._n = value 
  }
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 設(shè)置為 -1 失敗`)
data2.n = 1
console.log(`需求二:${data2.n} 設(shè)置為 1 成功`)
// 那如果對(duì)方直接用data2._n呢结借?_n可以直接設(shè)置為-1呀

// 需求三:使用代理obj
//括號(hào)里是匿名對(duì)象,直接改為沒有名字的對(duì)象{n:0},data可有可無
let data3 = proxy({ data:{n:0} }) 
function proxy({data}){ //接收data屬性
  const obj = {}
  //理論應(yīng)該遍歷data的所有key卒茬,這里做了簡化
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n //當(dāng)你取obj.n,就返回data.n
    },
    set(value){
      if(value<0)return
      data.n = value //當(dāng)你設(shè)置obj.n,就設(shè)置data.n
    }
  })
  return obj
}
// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n}船老,設(shè)置為 -1 失敗`)
data3.n = 1
console.log(`需求三:${data3.n}咖熟,設(shè)置為 1 成功`)
//杠精說,你看下面代碼

// 需求四:繞過代理,通過引用
let myData = {n:0}
let data4 = proxy({ data:myData })//括號(hào)里是匿名對(duì)象柳畔,無法訪問
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n}馍管,設(shè)置為 -1 失敗了嗎!薪韩?`)

// 需求五:就算用戶擅自修改myData确沸,也要攔截他
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) //data5就是myData5的代理對(duì)象
//監(jiān)聽data
function proxy2({data}){ 
  let value = data.n //拿到n,記錄下來
  Object.defineProperty(data, 'n', { 
    get(){ return value },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
//代理的邏輯
  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){ return data.n },
    set(value){
      if(value<0)return
      data.n = value //通過替身value就不能直接修改data.n啦
    }
  })
  return obj
}
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},設(shè)置為 -1 失敗了`)
myData5.n = 1
console.log(`需求五:${data5.n}俘陷,設(shè)置為 1 成功了`)

// 這代碼看著眼熟嗎罗捎?
let data5 = proxy2({ data:myData5 }) 
let vm = new Vue({data: myData})
現(xiàn)在我們可以說說 new Vue 做了什么了

[圖片上傳失敗...(image-151890-1648768426411)]

小結(jié)

4.Object.defineProperty
可以給對(duì)象添加屬性value
可以給對(duì)象添加getter/setter
getter/setter用于對(duì)屬性的讀寫進(jìn)行監(jiān)控

啥是代理(設(shè)計(jì)模式)
對(duì)myData對(duì)象的屬性讀寫,全權(quán)由另一個(gè)對(duì)象vm負(fù)責(zé)
那么vm就是myData的代理
比如myData.n不用岭洲,偏要用vm.n來操作myData.n

vm = new Vue({data: myData})
一.會(huì)讓vm成為myData的代理(proxy)
二.會(huì)對(duì)myData的所有屬性進(jìn)行監(jiān)控
為什么要監(jiān)控宛逗,為了防止myData的屬性變了,vm不知道
vm知道了又如何盾剩?
知道屬性變了可以render(data)刷新呀
UI=render(data)

注意: 全程這個(gè)對(duì)象n:0都沒有被我扔掉過雷激,一直在改這個(gè)對(duì)象里面的東西,不是搞出了一個(gè)新對(duì)象告私。我是把這個(gè)對(duì)象的n給覆蓋掉了屎暇,變成get n、set n,我沒有把這個(gè)對(duì)象刪掉驻粟,因?yàn)槿绻野堰@個(gè)對(duì)象刪掉生成新的對(duì)象根悼,那關(guān)聯(lián)就斷開了,不是同一個(gè)對(duì)象了蜀撑。

我全程都是在這個(gè)對(duì)象上面修改挤巡,修改后得到一個(gè)被修改的對(duì)象。然后在被修改的基礎(chǔ)上酷麦,生成了一個(gè)新的代理矿卑。

明白2件事
1.Vue會(huì)對(duì)data后面的{n:0}進(jìn)行竄改,給它加監(jiān)聽沃饶。
2.會(huì)新生成一個(gè)對(duì)象母廷,這個(gè)對(duì)象會(huì)代理篡改后的對(duì)象。

Object.defineProperty
可以給對(duì)象添加屬性value
可以給對(duì)象添加getter/setter
getter/setter用于對(duì)屬性的讀寫進(jìn)行監(jiān)控

數(shù)據(jù)響應(yīng)式

若一個(gè)物體能對(duì)外界的刺激做出反應(yīng)糊肤,它就是響應(yīng)式的

Vue的data是響應(yīng)式
const vm=new Vue({data:{n:0}})
我如果修改vm.n,那么UI中的n就會(huì)響應(yīng)我
Vue 2通過Object.defineProperty來實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式
響應(yīng)式網(wǎng)頁是啥琴昆?
如果我改變窗口大小,網(wǎng)頁內(nèi)容會(huì)做出響應(yīng)馆揉,那就是響應(yīng)式網(wǎng)頁业舍。

Vue的data的bug

目前你已經(jīng)知道了數(shù)據(jù)響應(yīng)式,但面試不會(huì)考常態(tài)一般考變態(tài)。

Object.defineProperty的問題
Object.defineProperty(obj,'n',{...})
必須要有一個(gè)'n'舷暮,才能監(jiān)聽 & 代理obj.n對(duì)吧
如果前端開發(fā)者比較水蟋座,沒有給n怎么辦
示例1. Vue會(huì)給出一個(gè)警告

new Vue({
  data: {},
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

示例2. Vue只會(huì)檢查第一層屬性

new Vue({
  data: {
    obj: {
      a: 0 //obj.a會(huì)被Vue監(jiān)聽 & 代理
    //b:undefined  
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; //頁面中不會(huì)顯示1
    //Vue.set(this.obj,'b',1)
    //this.$set(this.obj,'b'脚牍,1) 
    }
  }
}).$mount("#app");

解決辦法
1.把k都聲明好,后面不再加屬性 比如b:undefined
2.使用Vue.set或者this.$set

$是為了防止重名
比如Vue.set(this.obj,'b')或者this.$set(this.obj,'b'巢墅,1)

Vue.set和this.$set作用:
新增key
自動(dòng)創(chuàng)建代理和監(jiān)聽(如果沒有創(chuàng)建過)
觸發(fā)UI更新(但不會(huì)立刻更新)

數(shù)組的變異方法

data中有數(shù)組诸狭,沒法提前聲明所有key怎么辦?
示例

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
    //this.array[3] = "d"; 頁面中不會(huì)顯示'd'
    //this.$set(this.array,3,'d') 增加下標(biāo)的方式實(shí)現(xiàn)添加
    this.array.push('d')//尤雨溪的做法
    console.log(this.array)
    }
  }
}).$mount("#app");

[圖片上傳失敗...(image-76f371-1648768426411)]

尤雨溪的做法:篡改數(shù)組的API
Vue在這個(gè)對(duì)象君纫,你以為這個(gè)是數(shù)組對(duì)象驯遇,你傳給Vue之后,Vue就會(huì)篡改這個(gè)數(shù)組蓄髓。它會(huì)在中間加一層原型叉庐。這個(gè)原型有7個(gè)方法,這7個(gè)方法跟以前是同名的但是代碼被尤雨溪改了,會(huì)幫你set(監(jiān)聽,每次push都會(huì)通知Vue)会喝。也就是說push會(huì)做2件事情:調(diào)以前的push,調(diào)完后通知Vue添加監(jiān)聽和代理陡叠。
這7個(gè)API都會(huì)被Vue篡改,調(diào)用后會(huì)更新UI

總結(jié)
1.對(duì)象中新增的key
Vue無法事先監(jiān)聽和代理
要使用set來新增key,創(chuàng)建監(jiān)聽和代理,更新UI
最好提前把屬性都寫出來肢执,不要新增key
但數(shù)組做不到「不新增key」
2.數(shù)組中新增的key
也可用set來新增key,更新UI
不過尤雨溪篡改了7個(gè)API方便你對(duì)數(shù)組進(jìn)行增刪
這7個(gè)API會(huì)自動(dòng)處理監(jiān)聽和代理枉阵,并更新UI
結(jié)論:數(shù)組新增key最后通過7個(gè)API
[圖片上傳失敗...(image-151872-1648768426411)]

面試題
說說你對(duì)Vue數(shù)據(jù)響應(yīng)式的理解
Vue數(shù)據(jù)響應(yīng)式,使得數(shù)據(jù)更新時(shí)得到及時(shí)的渲染。vue通過Object.defineProperty()給數(shù)據(jù)對(duì)象添加value屬性预茄,設(shè)置getter和setter監(jiān)控屬性的讀寫兴溜,并使用vm對(duì)象負(fù)責(zé)數(shù)據(jù)對(duì)象的代理,當(dāng)屬性更新時(shí)耻陕,調(diào)用rander()更新拙徽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市诗宣,隨后出現(xiàn)的幾起案子膘怕,更是在濱河造成了極大的恐慌,老刑警劉巖梧田,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淳蔼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡裁眯,警方通過查閱死者的電腦和手機(jī)鹉梨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿稳,“玉大人存皂,你說我怎么就攤上這事。” “怎么了旦袋?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵骤菠,是天一觀的道長。 經(jīng)常有香客問我疤孕,道長商乎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任祭阀,我火速辦了婚禮鹉戚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘专控。我一直安慰自己抹凳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布伦腐。 她就那樣靜靜地躺著赢底,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柏蘑。 梳的紋絲不亂的頭發(fā)上幸冻,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音辩越,去河邊找鬼嘁扼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛黔攒,可吹牛的內(nèi)容都是我干的趁啸。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼督惰,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼不傅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赏胚,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤访娶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后觉阅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崖疤,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年典勇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劫哼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡割笙,死狀恐怖权烧,靈堂內(nèi)的尸體忽然破棺而出眯亦,到底是詐尸還是另有隱情蚌卤,我是刑警寧澤陨晶,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布碑隆,位于F島的核電站印屁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏献起。R本人自食惡果不足惜铭腕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一柿究、第九天 我趴在偏房一處隱蔽的房頂上張望券时。 院中可真熱鬧囊嘉,春花似錦、人聲如沸革为。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽震檩。三九已至,卻和暖如春蜓堕,著一層夾襖步出監(jiān)牢的瞬間抛虏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工套才, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迂猴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓背伴,卻偏偏與公主長得像沸毁,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傻寂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容