雖然 Vue.js 通常鼓勵開發人員沿着“數據驅動”的方式思考,避免直接接觸 DOM,但是有時我們確實要這么做。比如一個新聞滾動的列表項。如果在這里需要操作dom, 應該是等待 Vue 完成更新 DOM之后。
一、新聞滾動列表
1、在created函數中獲取后台數據;
2、模板引擎中用v-for生成列表項;
3、調用滾動函數,假設該滾動函數式原生方法寫的;
4、什么時候開始調用滾動函數比較合適呢?
二、this.$nextTick()
官方解釋:將回調延遲到下次 DOM 更新循環之后執行。在修改數據之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一樣,不同的是回調的 this 自動綁定到調用它的實例上。
Vue.component('example', { template: '<span>{{ message }}</span>', data: function () { return { message: 'not updated' } }, methods: { updateMessage: function () { this.message = 'updated' console.log(this.$el.textContent) // => 'not updated' this.$nextTick(function () { console.log(this.$el.textContent) // => 'updated' }) } } })
三、新聞滾動列表中的this.$nextTick()放哪里?
因為數據是根據請求之后獲取的,所以應該放到請求的回調函數里面。
四、原理【重點】
前面只是利用一個例子引入話題。
在進行獲取數據后,需要對新視圖進行下一步操作或者其他操作時,為什么獲取不到 DOM呢?
原因:
這里就涉及到 Vue 一個很重要的概念:異步更新隊列(JS運行機制 、 事件循環)。
Vue 在觀察到數據變化時並不是直接更新 DOM,而是開啟一個隊列,並緩沖在同一事件循環中發生的所有數據改變。
在緩沖時會去除重復數據,從而避免不必要的計算和DOM操作。
然后,在下一個事件循環 tick 中,Vue 刷新隊列並執行實際(已去重的)工作。
所以如果用 for 循環來動態改變數據100次,其實它只會應用最后一次改變,如果沒有這種機制,DOM就要重繪100次,是一個很大的開銷,損耗性能。
例子:
//改變數據 vm.message = 'changed' //想要立即使用更新后的DOM。這樣不行,因為設置message后DOM還沒有更新 console.log(vm.$el.textContent) // 並不會得到'changed' //這樣可以,nextTick里面的代碼會在DOM更新后執行 Vue.nextTick(function(){ console.log(vm.$el.textContent) //可以得到'changed' })
五、常見應用
點擊按鈕顯示原本以 v-show = false 隱藏起來的輸入框,並獲取焦點。
showsou(){ this.showit = true //修改 v-show document.getElementById("keywords").focus() //在第一個 tick 里,獲取不到輸入框,自然也獲取不到焦點 } //修改 showsou(){ this.showit = true this.$nextTick(function () { // DOM 更新了 document.getElementById("keywords").focus() }) }