Vue頁面級緩存解決方案feb-alive的示例分析-創(chuàng)新互聯(lián)

這篇文章主要介紹Vue頁面級緩存解決方案feb-alive的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

創(chuàng)新互聯(lián)公司服務緊隨時代發(fā)展步伐,進行技術革新和技術進步,經(jīng)過十載的發(fā)展和積累,已經(jīng)匯集了一批資深網(wǎng)站策劃師、設計師、專業(yè)的網(wǎng)站實施團隊以及高素質(zhì)售后服務人員,并且完全形成了一套成熟的業(yè)務流程,能夠完全依照客戶要求對網(wǎng)站進行網(wǎng)站設計制作、網(wǎng)站建設、建設、維護、更新和改版,實現(xiàn)客戶網(wǎng)站對外宣傳展示的首要目的,并為客戶企業(yè)品牌互聯(lián)網(wǎng)化提供全面的解決方案。

feb-alive

Vue頁面級緩存解決方案feb-alive (上)

在剖析feb-alive實現(xiàn)之前,希望大家對以下基本知識有一定的了解。

  • keep-alive實現(xiàn)原理

  • history api

  • vue渲染原理

  • vue虛擬dom原理

feb-alive與keep-alive差異性

1. 針對activated鉤子差異性

keep-alive配合vue-router在動態(tài)路由切換的情況下不會觸發(fā)activated鉤子,因為切換的時候組件沒有變化,所以只能通過beforeRouteUpdate鉤子或者監(jiān)聽$route來實現(xiàn)數(shù)據(jù)更新,而feb-alive在動態(tài)路由切換時,依然會觸發(fā)activated鉤子,所以用戶可以放心的將業(yè)務更新邏輯寫在activated鉤子,不必關心動態(tài)路由還是非動態(tài)路由的情況。

2. feb-alive是頁面級緩存,而keep-alive是組件級別緩存

所以在上文中講到的使用keep-alive存在的一些限制問題都能夠得到有效的解決

實現(xiàn)原理

首先我們的目標很明確,需要開發(fā)的是一個頁面級別的緩存插件,之前使用keep-alive遇到的諸多問題,歸根結(jié)底是因為它是一個組件級別的緩存。那么我們就需要尋找每個頁面的特征,用來存儲我們需要存儲的路由組件vnode,這里我們就需要思考什么可以作為每個頁面的標記

兩種方式:

  • 通過每個url的查詢參數(shù)來存儲key

  • 通過history.state來存儲key

方案一:使用查詢參數(shù)

優(yōu)點:

可以兼容vue-router的hash模式

缺點:

每個頁面的url后面都會帶一個查詢參數(shù)
每次頁面跳轉(zhuǎn)都需要重寫url

方案二:使用history.state

優(yōu)點:

無需附帶額外的查詢參數(shù)

缺點:

不支持hash模式

相比方案一明顯的缺點,我更較傾向于方案二,舍棄hash模式的兼容性,換來整個插件更加好的用戶體驗效果。
接下來看下feb-alive的實現(xiàn),feb-alive組件與上文的keep-alive一樣都是抽象組件,結(jié)構基本一致,主要區(qū)別在于render函數(shù)的

實現(xiàn)

// feb-alive/src/components/feb-alive.js
render () {
  // 取到router-view的vnode
  const vnode = this.$slots.default ? this.$slots.default[0] : null
  const disableCache = this.$route.meta.disableCache
  // 如果不支持html5 history則不做緩存處理
  if (!supportHistoryState) {
    return vnode
  }
  // 嘗試寫入key
  if (!history.state || !history.state[keyName]) {
    const state = {
      [keyName]: genKey()
    }
    const path = getLocation()
    history.replaceState(state, null, path)
  }
  // 有些瀏覽器不支持往state中寫入數(shù)據(jù)
  if (!history.state) {
    return vnode
  }
  // 指定不使用緩存
  if (disableCache) {
    return vnode
  }
  // 核心邏輯
  if (vnode) {
    const { cache, keys } = this
    const key = history.state[keyName]
    const { from, to } = this.$router.febRecord
    let parent = this.$parent
    let depth = 0
    let cacheVnode = Object.create(null)
    vnode && (vnode.data.febAlive = true)
    while (parent && parent._routerRoot !== parent) {
      if (parent.$vnode && parent.$vnode.data.febAlive) {
        depth++
      }
      parent = parent.$parent
    }

    // 記錄緩存及其所在層級
    febCache[depth] = cache

    // /home/a backTo /other
    // 內(nèi)層feb-alive實例會被保存,防止從/home/a 跳轉(zhuǎn)到 /other的時候內(nèi)層feb-alive執(zhí)行render時候,多生成一個實例
    if (to.matched.length < depth + 1) {
      return null
    }
    if (from.matched[depth] === to.matched[depth] && (from.matched.slice(-1)[0] !== to.matched.slice(-1)[0])) {
      // 嵌套路由跳轉(zhuǎn) && 父級路由
      // /home/a --> /home/b
      // 父路由通過key進行復用
      cache[key] = cache[key] || this.keys[this.keys.length - 1]
      cacheVnode = getCacheVnode(cache, cache[key])
      if (cacheVnode) {
        vnode.key = cacheVnode.key
        remove(keys, key)
        keys.push(key)
      } else {
        this.cacheClear()
        cache[key] = vnode
        keys.push(key)
      }
    } else {
      // 嵌套路由跳轉(zhuǎn) && 子路由
      // 正常跳轉(zhuǎn) && 動態(tài)路由跳轉(zhuǎn)
      // /a --> /b
      // /page/1 --> /page/2
      vnode.key = `__febAlive-${key}-${vnode.tag}`
      cacheVnode = getCacheVnode(cache, key)
      // 只有相同的vnode才允許復用組件實例,否則雖然實例復用了,但是在patch的最后階段,會將復用的dom刪除
      if (cacheVnode && vnode.tag === cacheVnode.tag) {
        // 從普通路由后退到嵌套路由時,才需要復原key
        vnode.key = cacheVnode.key
        vnode.componentInstance = cacheVnode.componentInstance
        remove(keys, key)
        keys.push(key)
      } else {
        this.cacheClear()
        cache[key] = vnode
        keys.push(key)
      }
    }
    vnode.data.keepAlive = true
  }
  return vnode
}

幾個關鍵的點都加上了注釋,現(xiàn)在我們一步一步解析

const vnode = this.$slots.default ? this.$slots.default[0] : null
const disableCache = this.$route.meta.disableCache

此處與上一篇文章分析keep-alive實現(xiàn)一樣,在feb-alive組件的render函數(shù)中可以通過this.$slots.default[0]獲取到嵌套的第一個默認插槽的vnode,也就是router-view組件vnode,同時獲取到了路由配置disableCache用來判斷用戶是否配置改頁面啟用緩存。

// 如果不支持html5 history 寫操作則不做緩存處理
if (!supportHistoryState) {
  return vnode
}
// 嘗試寫入key
if (!history.state || !history.state[keyName]) {
  const state = {
    [keyName]: genKey()
  }
  const path = getLocation()
  history.replaceState(state, null, path)
}
// 有些瀏覽器不支持往state中寫入數(shù)據(jù)
if (!history.state) {
  return vnode
}
// 指定不使用緩存
if (disableCache) {
  return vnode
}

首先判斷了當前宿主環(huán)境是否支持history。之后判斷當前頁面的history.state是否存在對應的頁面key,如果沒有則創(chuàng)建,并通過history.replaceState進行key值寫入。

最后又做了一層history.state判斷,因為有些瀏覽器不支持history的寫入操作。

當宿主環(huán)境不支持history的時候直接返回vnode。

當route.meta.disableCache為true時,也直接返回vnode

// 核心邏輯
if (vnode) {
  const { cache, keys } = this
  const key = history.state[keyName]
  const { from, to } = this.$router.febRecord
  let parent = this.$parent
  let depth = 0
  let cacheVnode = Object.create(null)
  vnode && (vnode.data.febAlive = true)
  while (parent && parent._routerRoot !== parent) {
    if (parent.$vnode && parent.$vnode.data.febAlive) {
      depth++
    }
    parent = parent.$parent
  }

  // 記錄緩存及其所在層級
  febCache[depth] = cache

  // /home/a backTo /other
  // 由于feb-alive實例會被保存,防止例如/home/a 后退到 /other的時候內(nèi)層feb-alive執(zhí)行render時候,多生成一個實例
  if (to.matched.length < depth + 1) {
    return null
  }
  if (from.matched[depth] === to.matched[depth] && (from.matched.slice(-1)[0] !== to.matched.slice(-1)[0])) {
    // ...
  } else {
    // ...
  }
  vnode.data.keepAlive = true
}

首先,我們在每個feb-alive組件的render函數(shù)中計算了當前的feb-alive所在層級,這是為了解決嵌套路由的使用。

Vue頁面級緩存解決方案feb-alive的示例分析

每個層級的feb-alive組件實例都維護著當前所在層級的路由組件實例的緩存。這樣設計,feb-alive組件只需要關心自身所處層級的情況即可,減少了緩存路由實例的成本。

繼續(xù)分析代碼

if (from.matched[depth] === to.matched[depth] && depth !== to.matched.length - 1) {
  // ...
} else {
  // ...
}

Q: 這里的if條件什么時候成立呢?

答案:被包裹組件是嵌套路由中的父級路由組件

例如/home/a -> /home/b,其中home組件在嵌套路由跳轉(zhuǎn)時不應該重新實例化,因為嵌套路由跳轉(zhuǎn)的時候,父路由組件狀態(tài)應該被保存,而復用home組件,無需主動設置componentInstance,直接進行key設置復用即可

這里需要重點關注下父組件實例緩存的技巧

cache[key] = cache[key] || this.keys[this.keys.length - 1]
cacheVnode = getCacheVnode(cache, cache[key])
if (cacheVnode) {
  vnode.key = cacheVnode.key
  remove(keys, key)
  keys.push(key)
} else {
  this.cacheClear()
  cache[key] = vnode
  keys.push(key)
}

我們一步步分析

當我們首次訪問/home/a的時候,home組件對應的是層級為0,也就是最外層的feb-alive需要緩存的vnode對象,這里姑且用feb-alive[0]來描述,此時cache[key]取到為undefined,cacheVnode也是undefined,這樣會進入到else邏輯,將home組件的vnode緩存到cache[key]中。

當我們從/home/a 跳轉(zhuǎn)到 /home/b 時,針對home組件會再次進入到上面的代碼片段

// 取到的是/home/a頁面的key
cache[key] = cache[key] || this.keys[this.keys.length - 1]

取到的是/home/a頁面的key,所以之后cacheVnode就可以取到/home/a頁面訪問時存儲的home組件的vnode,這個時候只需要將其key賦給當前的home組件的vnode即可,之后Vue在渲染的時候會通過key復用實例。從而保證/home/a -> /home/b 時,會復用home組件實例。

這樣我們就實現(xiàn)了嵌套路由中父級路由的復用。

其他情況的話就會走else邏輯

1. 普通路由跳轉(zhuǎn)

/foo -> /bar

2. 動態(tài)路由跳轉(zhuǎn)

/page/1 -> /page/2

3. 嵌套路由中的子級路由

/home/foo -> /home/bar 中的foo, bar組件

/home/foo/a -> /home/bar/a 中的foo, bar組件,注意a組件依然會走if邏輯,不過其操作沒有太大意義

/home/page/1 -> /home/page/2 中的page組件

針對else這層邏輯和keep-alive一樣,非常簡單

// 根據(jù)規(guī)則拼接vnode key
vnode.key = `__febAlive-${key}-${vnode.tag}`

// 獲取緩存vnode
cacheVnode = getCacheVnode(cache, key)

// 判斷是否命中緩存vnode,此處還必須保證兩個vnode的tag相同
if (cacheVnode && vnode.tag === cacheVnode.tag) {
  vnode.key = cacheVnode.key
  vnode.componentInstance = cacheVnode.componentInstance
  remove(keys, key)
  keys.push(key)
} else {
  this.cacheClear()
  cache[key] = vnode
  keys.push(key)
}

此處根據(jù)key獲取到緩存vnode,如果存在則復用實例并刷新key的順序,否則緩存當前的vnode,供下次緩存恢復使用。

以上是“Vue頁面級緩存解決方案feb-alive的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關知識,歡迎關注創(chuàng)新互聯(lián)成都網(wǎng)站設計公司行業(yè)資訊頻道!

另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

名稱欄目:Vue頁面級緩存解決方案feb-alive的示例分析-創(chuàng)新互聯(lián)
當前路徑:http://muchs.cn/article0/iphoo.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供建站公司軟件開發(fā)、App設計小程序開發(fā)、企業(yè)網(wǎng)站制作微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化