你瞭解keep-alive嗎?

這是我參與8月更文挑戰的第5天,活動詳情查看:8月更文挑戰node

概念

keep-alive 是 Vue 內置的一個抽象組件,可使被包含的組件保留狀態,即keep-alive 能夠實現組件的緩存,當組件切換時不會對當前組件進行卸載。正則表達式

做用

在組件切換過程當中將狀態保留在內存中,防止重複渲染DOM,減小加載時間及性能消耗,提升用戶體驗性。緩存

理解

  • 通常結合路由和動態組件一塊兒使用,用於緩存組件;markdown

  • 提供 include 和 exclude 屬性,二者都支持字符串或正則表達式, include 表示只有名稱匹配的組件會被緩存,exclude 表示任何名稱匹配的組件都不會被緩存 ,其中 exclude 的優先級比 include 高;函數

  • 對應兩個鉤子函數 activated 和 deactivated ,當組件被激活時,觸發鉤子函數 activated,當組件被移除時,觸發鉤子函數 deactivated。post

props:性能

  • include - 字符串或正則表達式。只有名稱匹配的組件會被緩存。
  • exclude - 字符串或正則表達式。任何名稱匹配的組件都不會被緩存。
  • max - 數字。最多能夠緩存多少組件實例。
include和exclude屬性是根據組件中的name屬性來進行過濾的,而非路由中的name
複製代碼

原理

在 created 函數調用時將須要緩存的 VNode 節點保存在 this.cache 中/在 render(頁面渲染) 時,若是 VNode 的 name 符合緩存條件(能夠用 include 以及 exclude 控制),則會從 this.cache 中取出以前緩存的 VNode 實例進行渲染。ui

源碼:core/components/keep-alive.jsthis

export default {
    name: 'keep-alive',
    abstract: true, // 抽象組件 
    props: {
        include: patternTypes,
        exclude: patternTypes,
        max: [String, Number]
    },
    created() {
        this.cache = Object.create(null) // 建立緩存列表 
        this.keys = [] // 建立緩存組件的key列表 
    },
    destroyed() { // keep-alive銷燬時 會清空全部的緩存和key 
        for (const key in this.cache) { // 循環銷燬 
                pruneCacheEntry(this.cache, key, this.keys)
        }
    },
    mounted() { // 會監控include 和 exclude屬性 進行組件的緩存處理 
        this.$watch('include', val => {
                pruneCache(this, name => matches(val, name))
        }) this.$watch('exclude', val => {
                pruneCache(this, name => !matches(val, name))
        })
    },
    render() {
        const slot = this.$slots.default // 會默認拿插槽 
        const vnode: VNode = getFirstComponentChild(slot) // 只緩存第一個組件
        const componentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptions
        if (componentOptions) { // check pattern 
            const name: ? string = getComponentName(componentOptions) // 取出組件的名字 
            const {
                    include,
                    exclude
            } = this
            if ( // 判斷是否緩存 
                    // not included 
                    (include && (!name || !matches(include, name))) ||
                    // excluded 
                    (exclude && name && matches(exclude, name))) {
                    return vnode
            }
            const {
                    cache,
                    keys
            } = this
            const key: ? string = vnode.key == null

            // same constructor may get registered as different local components 
            // so cid alone is not enough (#3269) 
            ?componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key, // 若是組件沒key 就本身經過 組件的標籤和key和cid 拼接一個key 
            if (cache[key]) {
                    vnode.componentInstance = cache[key].componentInstance // 直接拿到組件實 例 
                    // make current key freshest 
                    remove(keys, key) // 刪除當前的 [b,c,d,e,a] 
                    // LRU 最近最久未使用法 
                    keys.push(key) // 並將key放到後面[b,a] 
            } else {
                    cache[key] = vnode // 緩存vnode
                    keys.push(key) // 將key 存入 
                    // prune oldest entry 
                    if (this.max && keys.length > parseInt(this.max)) {
                            // 緩存的太多超過了max 就須要刪除掉
                            pruneCacheEntry(cache, keys[0], keys, this._vnode) 
                            // 要刪除第0個 可是現 在渲染的就是第0個 
                    }
            } 
            vnode.data.keepAlive = true // 而且標準keep-alive下的組件是一個緩存組件 
    }
    return vnode || (slot && slot[0]) // 返回當前的虛擬節點 
    }
}
``
複製代碼