宝哥软件园

谈Vue的页面级缓存解决方案feb-alive(下)

编辑:宝哥软件园 来源:互联网 时间:2021-08-26

活着的二月

Github地址体验链接

虚拟页面级缓存解决方案2月上线(上)

在分析feb-alive的实现之前,希望大家对以下基础知识有一定的了解。

保活实现原理历史api vue渲染原理vue虚拟dom原理二月保活和保活的区别

1.根据激活钩子的不同

Keep-alive和vue-router在动态路由切换的情况下不会触发激活的钩子,因为切换时组件没有变化,所以数据更新只能通过beforeRouteUpdate钩子或monitoring $route来实现,而feb-alive在动态路由切换的情况下仍然会触发激活的钩子,所以用户可以在激活的钩子上安全地写入业务更新逻辑,无需关心动态路由或非动态路由。

2.feb-alive是页面级缓存,而keep-alive是组件级缓存

因此,可以有效地解决使用上述保活的一些限制

实施原则

首先,我们的目标很明确。我们需要开发的是页面级缓存插件。归根结底,使用保活遇到的很多问题都是因为它是组件级缓存。然后我们需要找到每个页面的特征来存储我们需要存储的路由组件vnode,这里我们需要考虑什么可以用作每个页面的标签

两种方式:

通过每个url的查询参数存储密钥。通过历史记录存储密钥。状态方案1:使用查询参数

优点:

兼容vue路由器的哈希模式

缺点:

每个页面的url后面都有一个查询参数,每次页面跳转时都需要重写url

选项2:使用历史记录

优点:

不需要附加额外的查询参数

缺点:

不支持哈希模式

相比方案一明显的缺点,我更喜欢方案二,为了整个插件更好的用户体验,放弃了哈希模式的兼容性。接下来,看看feb-alive的实现。feb-alive组件与上面的keep-alive相同,结构基本相同。主要区别在于渲染功能

实现

//feb-alive/src/components/feb-alive。js render(){//取到路由器视图的虚拟节点常量vnode=这个$ slots.default?这个$插槽。默认值[0]: null常量禁用缓存=这.$route.meta.disableCache //如果不支持html5历史记录则不做缓存处理if(!supportHistoryState){ return vnode }//尝试写入key if(!history.state ||!历史。state[KeyName]){ const state={[KeyName]: GenKey()} const path=GetLocation()history。替换状态(状态,null,路径)} //有些浏览器不支持往状态中写入数据if(!history.state) { return vnode } //指定不使用缓存if(disable CACHE){ return vnode }//核心逻辑if (vnode) { const { cache,keys }=这个const key=history。state[KeyName]const { from,to }=this .$router.febRecord让家长=这个.$ parent let depth=0 let cacheVnode=object。创建(空)vnode(vnode。数据。febalive=true)while(parent parent ._ routerRoot!==parent) { if (parent .$vnode父级$ vnode。数据。FeBalive){ depth } parent=parent .$parent } //记录缓存及其所在层级febCache[depth]=cache///home/a backTo/other//内层活着的二月实例会被保存,防止从/home/a跳转到/其他的时候内层活着的二月执行提出时候,多生成一个实例如果(到。匹配。长度深度1){返回null } if(从。匹配[深度]==到。匹配的[深度](从。匹配。切片(-1)[0]!==to。匹配。切片(-1)[0]){//嵌套路由跳转父级路由///home/a - /home/b //父路由通过键进行复用cache[key]=cache[key]| | this。钥匙[这个。钥匙。length-1]cacheVnode=getCacheVnode(缓存,缓存[键])if(cacheVnode){ vnode。key=cacheVnode。键移除(键,键)键。按(键)else { this。cacheclear()缓存[键]=vnode键。push(key)} } else {//嵌套路由跳转子路由//正常跳转动态路由跳转///a-/b///page/1-/page/2 vnode。key=` _ _ febAlive-$ { key }-$ { vnode。标记} ` cacheVnode=getCacheVnode(缓存,密钥)//只有相同的虚拟节点才允许复用组件实例,否则虽然实例复用了,但是在修补的最后阶段,会将复用的数字正射影像图删除if(cacheVnode vnode。标记===cacheVnode。标签){//从普通路由后退到嵌套路由时,才需要复原密钥vnode。key=cachevnode。密钥vnode。组件实例=cachevnode。组件实例移除(键,键)键。按(键)else { this。cacheclear()缓存[键]=vnode键。push(key)} } vnode。数据。keepalive=true }返回vnode }几个关键的点都加上了注释,现在我们一步一步解析

const vnode=this .$slots.default?这个$插槽。默认值[0]: null const disable CACHE=this .$route.meta.disableCache此处与上一篇文章分析点火电极实现一样,在活着的二月组件的提出函数中可以通过这个$slots.default[0]获取到嵌套的第一个默认插槽的vnode,也就是路由器视图组件vnode,同时获取到了路由配置禁用缓存用来判断用户是否配置改页面启用缓存。

//如果不支持html5历史记录写操作则不做缓存处理if(!supportHistoryState){ return vnode }//尝试写入keyif(!history.state ||!历史。state[KeyName]){ const state={[KeyName]: GenKey()} const path=GetLocation()history。替换状态(状态,null,路径)}//有些浏览器不支持往状态中写入数据if(!history.state) { return vnode}//指定不使用缓存if (disableCache) { return vnode}首先判断了当前宿主环境是否支持历史。之后判断当前页面的历史。国家是否存在对应的页面关键,如果没有则创建,并通过history.replaceState进行键值写入。

最后又做了一层历史。国家判断,因为有些浏览器不支持历史的写入操作。

当主机环境不支持历史记录时,它会直接返回vnode。

当route.meta.disableCache为真时,还会直接返回vnode

//核心逻辑if (vnode) {const {cache,keys}=这个const key=history。state [keyname] const {from,to }=this。$router.febRecord让parent=this。$ parent let depth=0 let cacheVnode=object . create(null)vnode(vnode . data . febalive=true)while(parent parent。_ routerRoot!==parent) {if (parent。$ vnode父级。$ vnode . data . febalive){ depth } parent=parent。$ parent }//记录缓存及其级别febCache[depth]=cache///home/A backTo/other//由于将保存feb-alive实例,因此防止了例如当/home/A后退到/other时,当内层feb-alive执行render时,如果(To。matched . length dept 1){ return null } if(from。匹配[深度]==到。匹配[深度](从。matched.slice (-1) [0]!==to。matched . slice(-1)[0]){//.} else {//.} vnode.data.keepalive=true}首先,我们在每个feb-alive组件的render函数中计算feb-alive的当前级别,这是为了解决嵌套路由的使用问题。

每一级的feb-alive组件实例在当前级维护路由组件实例的缓存。通过这种设计,feb-alive组件只需要关心它自己的级别,这降低了缓存路由实例的成本。

继续分析代码

if(from . matched[depth]==to . matched[depth]depth!==to。matched.length-1) {//.} else {//.} q:这里的if条件何时成立?

回答:包装组件是嵌套路由中的父路由组件

例如/home/a-/home/b,其中在嵌套路由跳转时不应该重新实例化home组件,因为在嵌套路由跳转时,应该保存父路由组件的状态,home组件的重用不需要主动设置componentInstance,而是可以直接重用key设置。

这里,我们需要关注缓存父组件实例的技巧

Cache[key]=Cache[key]| | this . keys[this . keys . length-1]cacheVnode=getCacheVnode(Cache,Cache[key])if(Cache vnode){ vnode。key=缓存vnode。键移除(键,键)键。按(键)else {this。cache clear()cache[key]=vnode keys。推(键)}我们一步一步分析

当我们第一次访问/home/a时,home组件对应于级别为0的vnode对象,也就是说,最外层的feb-alive需要缓存。让我们在这里用feb-alive[0]来描述它。此时,缓存[key]被视为未定义,cacheVnode也未定义,这将进入else逻辑并将Home组件的Vnode缓存到缓存[key]

当我们从/home/a跳转到/home/b时,home的组件将再次输入上面的代码片段

//获取keycache [key]=cache [key] | | this。钥匙[这个。keys.length-1]的/home/a页面,因此cacheVnode可以获取在访问/home/a页面期间存储的home组件的Vnode,此时只需要将其密钥分配给当前home组件的vnode即可。从而确保当/home/a//home/b.

这样,我们实现了嵌套路由中父路由的重用。

否则在其他情况下将遵循逻辑

1.普通路线跳跃

/foo-/bar2。动态路由跳跃

/page/1-/page/23。嵌套路线中的子路线

/home/foo-/home/bar/home/foo/a-/home/bar/a中的foo和bar组件,请注意,如果逻辑仍将用于组件a,但其操作没有意义。/home/page/1-/home/page/2中的页面组件针对的是else逻辑和保持活动

//根据规则,拼接vnode key vnode . key=` _ _ febalive-$ { key }-$ { vnode . tag } `//获取cachevnode cachenode=getcachevnode(cache,key)//判断cache vnode是否命中。在这种情况下,必须确保两个vnode的标记具有相同的if(cachevnode vnode . tag==cachevnode . tag){ vnode . key=cachevnode . Key vnode.component instance=cachevnode.component instance remove(Key,Key)键。按(键)else {this。cache clear()cache[key]=vnode keys。push (key)}这里,缓存的vnode是根据key获取的,如果存在,则重用实例并刷新key的顺序,否则缓存当前的vnode,用于下一次缓存恢复。至此,feb-alive的核心逻辑已经阐述完毕。

参考文件

vue导航的秘密——vue . js技术

以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

更多资讯
游戏推荐
更多+