數據改變,視圖未變解決

最近在vue項目中使用了一個複雜對象,發現改變該對象的屬性值,頁面視圖未發生改變,查了一下,發現這個問題你們仍是有遇到過的,記錄一下解決方案。vue

問題:vue數據結構有多層,改變二級結構數據,dom節點沒有從新渲染?????ajax

1.$nextTick()

這個方法的意思就是數據更新後觸發dom,官方文檔說明:在下次DOM更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法 ,獲取更新後的DOM。數組

疑問:數據結構

1.DOM更新循環是指什麼?app

2.下次更新循環是何時?框架

3.修改數據以後使用,是加快了數據更新進度嗎?dom

4.在什麼狀況下要用到?異步

1.1原理

vue實現響應式並非數據發生變化以後DOM當即變化,而是按必定的策略進行DOM的更新ide

在vue的文檔中,說明vue是異步執行DOM更新的。函數

具體來講,異步執行的運行機制以下。

  • 全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。

  • 主線程以外,還存在一個任務隊列(task queue)。只要異步任務有了運行結果,就在「任務隊列」之中放置一個時間。

  • 一旦「執行棧」中的全部同步任務執行玩不,系統就會讀取「任務隊列」,看看裏面有哪些事件。哪些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。

  • 主線程不斷重複上面的第三步。

下圖就是主線程和任務隊列的示意圖。

 

 

 

1.2 事件循環說明

簡單來講,VUE在數據變化後,視圖不會當即更新,而是等同一事件中的全部數據發生變化以後,再統一進行視圖更新。

1 //改變數據
2 vm.message = 'changed'
3 //想要當即使用更新後的DOM,這樣是不行的,由於設置的message後DOM尚未更新
4 console.log(vm.$el.textContent) //並無獲得改變後的changed
5 //使用nextTick()這樣就能夠了,nextTick()裏面的代碼會再DOM更新後執行
6 Vue.nextTick(function(){
7   console.log(vm.$el.textContent) //能夠獲得changed
8 })

事件循環:

第一個tick(圖例中第一個步驟,即‘本次更新循環’):

  • 首先修改數據,這是同步任務。同一事件循環的全部同步任務都在主線程上執行,造成一個執行棧,此時還未涉及到DOM

  • vue開啓任務隊列,並緩衝在此事件中繁盛的全部數據改變。若是同一個watcher被屢次觸發,只會被推入到隊列中一次

第二個tick(圖例中第二個步驟,即‘下次更新循環’)

  • 同步任務執行完畢,開始執行異步watcher隊列中的任務,更新DOM。Vue在內部嘗試對異步隊列使用原生的Promise.then 和MessageChannel方法,若是執行環境不支持,會採用setTimeout(fn,0)代替。

第三個tick(圖例中第三個步驟)

  • 此時就是文檔中所說的下次DOM更新循環結束以後

注意:

Vue.$nextTick()獲取的DOM更新後的數據 ,Vue.nextTick()數據更新後的數據。

此時經過Vue.nextTick(獲取到改變後的DOM).經過setTimeout(fn,0)也能夠獲取到。

簡單總結事件循環:

同步代碼執行-->查找異步隊列,推入執行棧,執行Vue.nextTick[事件循環1] -->查找異步隊列,推入執行棧,執行Vue.nextTick[事件循環2] ...

總之,異步是單獨的一個tick,不會和同步在tick裏發生,也是DOM不會立刻變化的緣由。

1.3 用途

應用場景:須要在視圖更新以後,基於新的視圖進行操做

須要注意的是,在 created 和 mounted 階段,若是須要操做渲染後的試圖,也要使用 nextTick 方法。

注意 mounted 不會承諾全部的子組件也都一塊兒被掛載。若是你但願等到整個視圖都渲染完畢,能夠用 vm.$nextTick 替換掉 mounted.

1 mounted: function () {
2   this.$nextTick(function () {
3     // Code that will run only after the
4     // entire view has been rendered
5   })
6 }

1.4 其餘應用場景

其餘應用場景以下三例:

eg1:

點擊按鈕顯示本來以 v-show = false 隱藏起來的輸入框,並獲取焦點。

1 showsou(){
2   this.showit = true //修改 v-show
3   document.getElementById("keywords").focus()  //在第一個 tick 裏,獲取不到輸入框,天然也獲取不到焦點
4 }

修改成:

1 showsou(){
2   this.showit = true
3   this.$nextTick(function () {
4     // DOM 更新了
5     document.getElementById("keywords").focus()
6   })
7 }
View Code

eg2:

點擊獲取元素寬度。

 1 <div id="app">
 2     <p ref="myWidth" v-if="showMe">{{ message }}</p>
 3     <button @click="getMyWidth">獲取p元素寬度</button>
 4 </div>
 5 
 6 getMyWidth() {
 7     this.showMe = true;
 8     //this.message = this.$refs.myWidth.offsetWidth;
 9     //報錯 TypeError: this.$refs.myWidth is undefined
10     this.$nextTick(()=>{
11         //dom元素更新後執行,此時能拿到p元素的屬性
12         this.message = this.$refs.myWidth.offsetWidth;
13   })
14 }
View Code

eg3:

使用 swiper 插件經過 ajax 請求圖片後的滑動問題。

2.watch 監聽

$nextTick這個方法的意思就是數據更新後觸發dom節點更新,可是數據多層的時候vue監聽不到底層的數據變化,可使用watch方法深度監聽數據的變化,而後使用$nextTick在數據變化後觸發dom節點更新,而且數據獲取到後要遍歷數據放進定義的數組裏否則也不會出發dom節點更新。

$.each(data.resultData,function (index,item) {
        item.showChild = false;
         self.tableData.push(item)
     })
View Code
1 //this.$nextTick()不能監聽到底層的數據變化
2 showDetail(item){
3       console.log(this.tableData)
4       console.log(item)
5       this.$nextTick(function () {
6         item.showChild = !item.showChild
7       })
8  },
View Code

因此在此用深度監聽:

1 watch:{
2   tableData:{
3     handler:function(val,oldVal){
4       this.tableData = val
5     },
6       //深度監聽
7       deep:true
8   }
9 }

watch 還有屬性deep。當設置爲true時,它會進行深度監聽。簡而言之就是你有一個 const obj={a:1,b:2},裏面任意一個 key 的 value 發生變化的時候都會觸發watch。應用場景:好比我有一個列表,它有一堆query篩選項,這時候你就能deep watch它,只有任何一個篩序項改變的時候,就自動請求新的數據。或者你能夠deep watch一個 form 表單,當任何一個字段內容發生變化的時候,你就幫它作自動保存等等。

2.1 小技巧與建議

Watch immediate

這個已經算是一個比較常見的技巧了,這裏就簡單說一下。當 watch 一個變量的時候,初始化時並不會執行,以下面的例子,你須要在created的時候手動調用一次。

1 // bad
2 created() {
3   this.fetchUserList();
4 },
5 watch: {
6   searchText: 'fetchUserList',
7 }

你能夠添加immediate屬性,這樣初始化的時候也會觸發,而後上面的代碼就能簡化爲:

// good
watch: {
  searchText: {
    handler: 'fetchUserList',
    immediate: true,
  }
}

3 $forceUpdate()

在使用Vue框架開發時,在函數中改變了頁面中的某個值,在函數中查看是修改爲功了,嘗試了上面兩種方式後,但在頁面中沒有及時刷新改變後的值。

有時數據層次太多,視圖沒有自動更新,需手動強制刷新。添加this.$forceUpdate()進行強制渲染,效果實現。

相關文章
相關標籤/搜索