這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 vm.$forceUpdate (1)作用 迫使Vue.js實例重新渲染。註意它僅僅影響實例本身以及插入插槽內容的子組件,而不是所有子組件。 (2)實現 只需要執行watcher的update方法,就可以讓實例重新渲染。 Vue.js的每 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
vm.$forceUpdate
(1)作用
迫使Vue.js實例重新渲染。註意它僅僅影響實例本身以及插入插槽內容的子組件,而不是所有子組件。
(2)實現
只需要執行watcher的update方法,就可以讓實例重新渲染。
Vue.js的每一個實例都有一個watcher。當狀態發生改變時,會通知到組件級別,然後組件內部使用虛擬DOM進行更詳細的重新渲染操作。
事實上,組件就是Vue.js實例,所以組件幾倍的watcher和Vue.js實例上的watcher說的是同一個watcher。
手動執行實例watcher的update方法,就可以使Vue.js實例重新渲染。
Vue.prototype.$forceUpdate = function(){ const vm = this; if(vm._watcher){ vm._watcher.update(); } }
vm._watcher就是Vue.js實例的watcher,每當組件內依賴的數據發生變化時,都會自動觸發Vue.js實例中_watcher的update方法。
重新渲染的實現原理並不難,Vue.js的自動渲染通過變化偵測來偵測數據,即當數據發生變化時,Vue.js實例重新渲染。而vm.$forceUpdate是手動通知Vue.js實例重新渲染。
vm.$destroy
(1)作用
完全銷毀一個實例,它會清理該實例與其他實例的連接,並解綁其全部指令及監聽器,同時會觸發beforeDestory和destroyed的鉤子函數。
(2)這個方法並不是很常用,大部分場景下並不需要銷毀組件,只需要使用v-if或則v-for等指令以數據驅動的方式控制子組件的生命周期即可。
(3)實現原理
Vue.prototype.$destory = function(){ const vm = this; if(vm._isBeingDestroyed){ return; } callHook(vm,"beforeDestroy"); vm._isBeingDestroyed = = true; }
1、為了防止vm.$destroy被反覆執行,先對屬性_isBeingDestroyed進行判斷,如果它為true,說明Vue.js實例正在被銷毀,直接使用return語句退出函數執行邏輯。因為銷毀只需要銷毀一次即可,不需要反覆銷毀。
2、然後調用callHook函數觸發beforeDestroy的鉤子函數(callHook會觸發參數中提供的鉤子函數)。
(4)銷毀實例的邏輯1 首先,需要清理當前組件與父組件之間的連接。組件就是Vue.js實例,所以要清理當前組件與父組件之間的連接,只需要將當前組件實例從父組件實例的$children屬性中刪除即可。
說明:Vue.js實例的$children屬性存儲了所有子組件
const parent = vm.$parent; if(parent && !parent._isBeingDestroyed && !vm.$options.abstract){ remove(parent.$children,vm) }
1、如果當前實例有父級,同時父級沒有被銷毀且不是抽象組件,那麼將自己從父級的子列表中刪除,也就是將自己的實例從父級的$children屬性中刪除。
2、事實上,子組件在不同父組件中是不同的Vue.js實例,所以一個子組件實例的父級只有一個,銷毀操作也只需要從父級的子組件列表中銷毀當前這個Vue.js實例。
export function remove(arr,item){ if(arr.length){ const index = arr.indexOf(item); if(index>-1){ return arr.splice(index,1); } } }
(5)銷毀實例的邏輯2
1、父子組件間的鏈接斷掉之後,需要銷毀實例上的所有watcher,也就是說需要將實例上所有的依賴追蹤斷掉。
2、狀態會收集一些依賴,當狀態發生改變時會向這些依賴發送通知,而被收集的依賴就是watcher實例。因此,當Vue.js實例被銷毀時,應該將實例所監聽的狀態都取消掉,也就是從狀態的依賴列表中將watcher移除。
3、watcher的teardown方法,它的作用是從所有依賴項的Dep列表中將自己移除。即只要執行這個方法,就可以斷掉這個watcher所監聽的所有狀態。
4、斷掉Vue.js實例自身的watcher實例監聽的所有狀態。
if(vm._watcher){ vm._watcher.teardown(); }
5、執行了組件自身的watcher實例的teardown方法,從所有依賴項的訂閱列表中刪除watcher實例。刪除之後,當狀態發生變化時,watcher實例就不會再得到通知。
6、vm._watcher來源
當執行new Vue()時,會執行一系列初始化操作並渲染組件到實體上,其中就包括vm._watcher的處理
7、從Vue.js2.0開始,變化偵測的粒度調整為中等粒度,它只會發送通知到組件級別,然後組件使用虛擬DOM進行重新渲染。組件其實就是Vue.js實例。
8、怎麼通知到組件級別
在Vue.js實例上,有一個watcher,也就是vm._watcher,它會監聽這個組件中用到的所有狀態,即這個組件內用到的所有狀態的依賴列表中都會收集到vm._watcher。當這些狀態發生變化時,也都會通知vm._watcher,然後這個watcher再調用虛擬DOM進行重新渲染。
(6)銷毀實例的邏輯
1、只從狀態的依賴列表中刪除Vue.js實例上的watcher實例是不夠的。Vue.js提供了vm.watch所創建的watcher實例。
2、從狀態的依賴列表中銷毀用戶創建的watcher實例和銷毀Vue實例上的watcher實例相同,只需要執行watcher的teardown方法。
3、問題:如何知道用戶創建了多少個watcher?
1)Vue.js的解決方案是執行new Vue()時,在初始化的流程中,在this上添加一個_watchers屬性
vm._watchers = [];
2)每當創建watcher實例時,都會將watcher實例添加到vm._watchers中
export default class Watcher{ constructor(vm,expOrFn,cb){ <!-- 每當創建watcher實例時,都將watcher實例添加到vm._watchers中 --> vm._watchers.push(this); } }
4、只需要遍歷vm._watchers並依次執行每一項watcher實例的teardown方法,就可以將watcher實例從它所監聽的狀態的依賴列表中移除。
let i = vm._watchers.length; while(i--){ vm._watchers[i].teardown(); }
(7)向Vue.js實例添加_isDestroyed屬性來表示Vue.js實例已經被銷毀。
vm._isDestroyed = true;
(8)當vm.$destroy執行時,Vue.js不會將已經渲染到頁面中的DOM節點移除,但會將模板中的所有指令解綁。
vm._patch_(vm._vnode,null)
(9)觸發destroyed鉤子函數
callHook(vm,'destroyed')
(10)最後,移除實例上的所有事件監聽器。
vm.$off()
(11)完整代碼
Vue.prototype.$destory = function(){ const vm = this; <!-- 防止重覆銷毀 --> if(vm._isBeingDestroyed){ return; } <!-- 調用鉤子函數beforeDestroy --> callHook(vm,"beforeDestroy"); vm._isBeingDestroyed = = true; <!-- 刪除自己與父級之間的連接 --> const parent = vm.$parent; if(parent && !parent._isBeingDestroyed && !vm.$options.abstract){ remove(parent.$children,vm); } <!-- 從watcher監聽的所有狀態的依賴列表中移除watcher --> if(vm._watcher){ vm._watcher.teardown(); } let i = vm._watchers.length; <!-- 將從vm.$watcher創建的watcher實例從它所監聽的狀態的依賴列表中移除 --> while(i--){ vm._watchers[i].teardown(); } <!-- 表示實例已經被銷毀 --> vm._isDestroyed = true; <!-- 將模板中的所有指令解綁 --> vm._patch_(vm._vnode,null) <!-- 觸發destroyed鉤子函數 --> callHook(vm,'destroyed') <!-- 移除實例上的所有事件監聽器 --> vm.$off(); }