亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談Vue頁(yè)面級(jí)緩存解決方案feb-alive (下)

 更新時(shí)間:2019年04月14日 09:40:36   作者:米老糍  
這篇文章主要介紹了淺談Vue頁(yè)面級(jí)緩存解決方案feb-alive(下),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

feb-alive

github地址
體驗(yàn)鏈接

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

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

  • keep-alive實(shí)現(xiàn)原理
  • history api
  • vue渲染原理
  • vue虛擬dom原理

feb-alive與keep-alive差異性

1. 針對(duì)activated鉤子差異性

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

2. feb-alive是頁(yè)面級(jí)緩存,而keep-alive是組件級(jí)別緩存

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

實(shí)現(xiàn)原理

首先我們的目標(biāo)很明確,需要開(kāi)發(fā)的是一個(gè)頁(yè)面級(jí)別的緩存插件,之前使用keep-alive遇到的諸多問(wèn)題,歸根結(jié)底是因?yàn)樗且粋€(gè)組件級(jí)別的緩存。那么我們就需要尋找每個(gè)頁(yè)面的特征,用來(lái)存儲(chǔ)我們需要存儲(chǔ)的路由組件vnode,這里我們就需要思考什么可以作為每個(gè)頁(yè)面的標(biāo)記

兩種方式:

  • 通過(guò)每個(gè)url的查詢參數(shù)來(lái)存儲(chǔ)key
  • 通過(guò)history.state來(lái)存儲(chǔ)key

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

優(yōu)點(diǎn):

可以兼容vue-router的hash模式

缺點(diǎn):

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

方案二:使用history.state

優(yōu)點(diǎn):

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

缺點(diǎn):

不支持hash模式

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

實(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
  }
  // 嘗試寫(xiě)入key
  if (!history.state || !history.state[keyName]) {
    const state = {
      [keyName]: genKey()
    }
    const path = getLocation()
    history.replaceState(state, null, path)
  }
  // 有些瀏覽器不支持往state中寫(xiě)入數(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
    }

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

    // /home/a backTo /other
    // 內(nèi)層feb-alive實(shí)例會(huì)被保存,防止從/home/a 跳轉(zhuǎn)到 /other的時(shí)候內(nèi)層feb-alive執(zhí)行render時(shí)候,多生成一個(gè)實(shí)例
    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) && 父級(jí)路由
      // /home/a --> /home/b
      // 父路由通過(guò)key進(jìn)行復(fù)用
      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) && 動(dòng)態(tài)路由跳轉(zhuǎn)
      // /a --> /b
      // /page/1 --> /page/2
      vnode.key = `__febAlive-${key}-${vnode.tag}`
      cacheVnode = getCacheVnode(cache, key)
      // 只有相同的vnode才允許復(fù)用組件實(shí)例,否則雖然實(shí)例復(fù)用了,但是在patch的最后階段,會(huì)將復(fù)用的dom刪除
      if (cacheVnode && vnode.tag === cacheVnode.tag) {
        // 從普通路由后退到嵌套路由時(shí),才需要復(fù)原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
}

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

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

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

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

首先判斷了當(dāng)前宿主環(huán)境是否支持history。之后判斷當(dāng)前頁(yè)面的history.state是否存在對(duì)應(yīng)的頁(yè)面key,如果沒(méi)有則創(chuàng)建,并通過(guò)history.replaceState進(jìn)行key值寫(xiě)入。

最后又做了一層history.state判斷,因?yàn)橛行g覽器不支持history的寫(xiě)入操作。

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

當(dāng)route.meta.disableCache為true時(shí),也直接返回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
  }

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

  // /home/a backTo /other
  // 由于feb-alive實(shí)例會(huì)被保存,防止例如/home/a 后退到 /other的時(shí)候內(nèi)層feb-alive執(zhí)行render時(shí)候,多生成一個(gè)實(shí)例
  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
}

首先,我們?cè)诿總€(gè)feb-alive組件的render函數(shù)中計(jì)算了當(dāng)前的feb-alive所在層級(jí),這是為了解決嵌套路由的使用。

每個(gè)層級(jí)的feb-alive組件實(shí)例都維護(hù)著當(dāng)前所在層級(jí)的路由組件實(shí)例的緩存。這樣設(shè)計(jì),feb-alive組件只需要關(guān)心自身所處層級(jí)的情況即可,減少了緩存路由實(shí)例的成本。

繼續(xù)分析代碼

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

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

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

例如/home/a -> /home/b,其中home組件在嵌套路由跳轉(zhuǎn)時(shí)不應(yīng)該重新實(shí)例化,因?yàn)榍短茁酚商D(zhuǎn)的時(shí)候,父路由組件狀態(tài)應(yīng)該被保存,而復(fù)用home組件,無(wú)需主動(dòng)設(shè)置componentInstance,直接進(jìn)行key設(shè)置復(fù)用即可

這里需要重點(diǎn)關(guān)注下父組件實(shí)例緩存的技巧

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)
}

我們一步步分析

當(dāng)我們首次訪問(wèn)/home/a的時(shí)候,home組件對(duì)應(yīng)的是層級(jí)為0,也就是最外層的feb-alive需要緩存的vnode對(duì)象,這里姑且用feb-alive[0]來(lái)描述,此時(shí)cache[key]取到為undefined,cacheVnode也是undefined,這樣會(huì)進(jìn)入到else邏輯,將home組件的vnode緩存到cache[key]中。

當(dāng)我們從/home/a 跳轉(zhuǎn)到 /home/b 時(shí),針對(duì)home組件會(huì)再次進(jìn)入到上面的代碼片段

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

取到的是/home/a頁(yè)面的key,所以之后cacheVnode就可以取到/home/a頁(yè)面訪問(wèn)時(shí)存儲(chǔ)的home組件的vnode,這個(gè)時(shí)候只需要將其key賦給當(dāng)前的home組件的vnode即可,之后Vue在渲染的時(shí)候會(huì)通過(guò)key復(fù)用實(shí)例。從而保證/home/a -> /home/b 時(shí),會(huì)復(fù)用home組件實(shí)例。

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

其他情況的話就會(huì)走else邏輯

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

/foo -> /bar

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

/page/1 -> /page/2

3. 嵌套路由中的子級(jí)路由

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

/home/foo/a -> /home/bar/a 中的foo, bar組件,注意a組件依然會(huì)走if邏輯,不過(guò)其操作沒(méi)有太大意義

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

針對(duì)else這層邏輯和keep-alive一樣,非常簡(jiǎn)單

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

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

// 判斷是否命中緩存vnode,此處還必須保證兩個(gè)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,如果存在則復(fù)用實(shí)例并刷新key的順序,否則緩存當(dāng)前的vnode,供下次緩存恢復(fù)使用。
到此,feb-alive核心邏輯闡述完畢。

參考文檔

vue-navigation
Vue.js 技術(shù)揭秘

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue實(shí)現(xiàn)裁切圖片同時(shí)實(shí)現(xiàn)放大、縮小、旋轉(zhuǎn)功能

    vue實(shí)現(xiàn)裁切圖片同時(shí)實(shí)現(xiàn)放大、縮小、旋轉(zhuǎn)功能

    這篇文章主要介紹了vue實(shí)現(xiàn)裁切圖片同時(shí)實(shí)現(xiàn)放大、縮小、旋轉(zhuǎn)功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • vue腳手架搭建項(xiàng)目的兼容性配置詳解

    vue腳手架搭建項(xiàng)目的兼容性配置詳解

    這篇文章主要介紹了vue腳手架搭建項(xiàng)目的兼容性配置詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • 使用Vue自定義數(shù)字鍵盤(pán)組件(體驗(yàn)度極好)

    使用Vue自定義數(shù)字鍵盤(pán)組件(體驗(yàn)度極好)

    最近做 Vue 開(kāi)發(fā),因?yàn)橛胁簧夙?yè)面涉及到金額輸入,產(chǎn)品老是覺(jué)得用原生的 input 進(jìn)行金額輸入的話 體驗(yàn)很不好,于是自己動(dòng)手寫(xiě)了一個(gè)使用Vue自定義數(shù)字鍵盤(pán)組件,具體實(shí)現(xiàn)代碼大家參考下本文
    2017-12-12
  • 解析Vue.use()是干什么的

    解析Vue.use()是干什么的

    今天通過(guò)本文給大家分享Vue.use是什么,主要包括vueEsign?插件的install是什么,element-ui的install是什么,為什么有的庫(kù)就不需要使用Vue.use,對(duì)vue.use()相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-06-06
  • vue實(shí)現(xiàn)移動(dòng)端拖動(dòng)排序

    vue實(shí)現(xiàn)移動(dòng)端拖動(dòng)排序

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)移動(dòng)端拖動(dòng)排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 關(guān)于找到任意組件實(shí)例的方法

    關(guān)于找到任意組件實(shí)例的方法

    這篇文章主要介紹了關(guān)于找到任意組件實(shí)例的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 使用vue組件封裝共用的組件

    使用vue組件封裝共用的組件

    這篇文章主要介紹了使用vue組件封裝共用的組件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue SSR 組件加載問(wèn)題

    Vue SSR 組件加載問(wèn)題

    這篇文章主要介紹了Vue SSR 組件加載問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-05-05
  • vue封裝tree組件實(shí)現(xiàn)搜索功能

    vue封裝tree組件實(shí)現(xiàn)搜索功能

    本文主要介紹了vue封裝tree組件實(shí)現(xiàn)搜索功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • vscode下vue項(xiàng)目中eslint的使用方法

    vscode下vue項(xiàng)目中eslint的使用方法

    這篇文章主要給大家介紹了關(guān)于vscode下vue項(xiàng)目中eslint的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01

最新評(píng)論