vue如何实现单页缓存方案分析

实现全站的页面缓存,前进刷新,返回走缓存,并且能记住上一页的滚动位置,参考了很多技术实现,github上的导航组件实现的原理要么使用的keep-alive,要么参考了keep-alive的源码,但是只用keep-alive没法实现相同path,不同参数展示不同view,这就有点坑了,所以需要结合自己要实现的功能,适当改造keep-alive,为了实现每次前进都能刷新,返回走缓存还能自动定位的功能,文章陆续从以下几个方面展开讲:两套技术方案可选,最后定的技术方案的原因,实现的功能和原理,踩过的坑

方案一:vue的keep-alive组件

 

具体使用如下: 

  <keep-alive max="10"><router-view/></keep-alive>

为什么这么使用?
如vue官网(https://cn.vuejs.org/v2/api/#...)介绍:

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。主要用于保留组件状态或避免重新渲染。

因为缓存的需要通常出现在切换页面时,所以就需要结合vue-router的router-view来实现

为什么keep-alive能实现缓存?
 


render () {const slot = this.$slots.defaultconst vnode: VNode = getFirstComponentChild(slot)const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check patternconst name: ?string = getComponentName(componentOptions)const { include, exclude } = thisif (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst 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.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode || (slot && slot[0])}

 

如上keep-alive源码,其中render函数是这样实现的,要渲染的试图组件作为插槽内容被获取到,当渲染到路径匹配到的视图组件时会根据vnode存储的内容拿到对应的name,一次将这些组件实例放到变量cache中,这样根据路由就可以找到缓存的vnode,返回给createComponent方法去执行initComponent,vue组件渲染这块的代码如下

function initComponent (vnode, insertedVnodeQueue) {if (isDef(vnode.data.pendingInsert)) {insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)vnode.data.pendingInsert = null}vnode.elm = vnode.componentInstance.$elif (isPatchable(vnode)) {invokeCreateHooks(vnode, insertedVnodeQueue)setScope(vnode)} else {// empty component root.// skip all element-related modules except for ref (#3455)registerRef(vnode)// make sure to invoke the insert hookinsertedVnodeQueue.push(vnode)}
}

 

这里会有 vnode.elm 缓存了 vnode 创建生成的 DOM 节点。所以对于首次渲染而言,除了在 <keep-alive> 中建立缓存,和普通组件渲染没什么区别。从进入到返回的大致执行流程如下

前进-返回

能实现的功能
能够把要缓存的组件渲染的vnode记到cache里边,当返回的时候用缓存里边的dom直接渲染,还有keep-alive组件提供的include 和 exclude属性,可以有条件的缓存想缓存的组件,如果配置了 max 并且缓存的长度超过了这个max的值,还要从缓存中删除第一个

存在的问题
存在的问题是存储vnode节点的key是name,也就是定义路由时组件对应的name,这就会导致同样的path,不同参数的时候打开的是从cache里边拿到的vnode,会渲染出同样的视图出来,但是很多业务场景都是根据参数来显示不同内容,而keep-alive底层并没有对此做扩展,可以看下keep-alive源码

 const key: ?string = vnode.key == null? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : ''): vnode.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}

 vnode.key就是路由里边定义的name,所以要用这套方案来实现的根据不同参数展示不同视图的功能就要对这里的key做改造,但是keep-alive是vue自带的,没法改底层,然后就诞生了我的第二套方案
 

方案二:navigation组件,scrollbehavior 

github上找到类似功能的组件vue-navigation,这个vue组件可以实现返回走缓存,底层原理跟keep-alive一样,实际上是改写了keep-alive组件,前进刷新时新增了一个参数VNK,这样在路由发生变化的时候都会用给url带一个参数,并且cache的key取值依赖这个参数,借鉴这个组件的思路,做了一个类似keep-alive的组件,其中key的值是getKey方法获取的,改写以后的render方法如下

 render () {var vnode = this.$slots.default ? this.$slots.default[0] : nullif (vnode) {vnode.key = vnode.key || (vnode.isComment ? 'comment' : vnode.tag)const { cache, keys } = thisvar key = getKey(this.$route, keyName)if (vnode.key.indexOf(key) === -1) {vnode.key = '__navigation-' + key + '-' + vnode.key}if (cache[key]) {if (vnode.key === cache[key].key) {vnode.componentInstance = cache[key].componentInstance} else {cache[key].componentInstance.$destroy()cache[key] = vnode}remove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode}

 getKey方法实现
//url上新增参数vnk的值

export function genKey() {// const t  = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'const t = 'xxxxxxxx'return t.replace(/[xy]/g, function (c) {const r = Math.random() * 16 | 0const v = c === 'x' ? r : (r & 0x3 | 0x8)return v.toString(16)})
}
//
export function getKey(route, keyName) {return `${route.name || route.path}?${route.query[keyName]}`
}

通过新写一个install方法挂载这个导航组件到vue上就可以实现前进刷新,返回走缓存,并且可以配置最大缓存数,后续开源到github

最后剩下返回上一页记住上一页的位置,之所以没有用开源的这个组件的记位置,是因为直接套用需要改整体布局,height:100%;样式造成$(windows).scrollTop失效,整体考虑改造成本较大,还是使用了vue-router提供的scrollBehavior,在路由配置里引入

实现如下:

var scrollBehavior = async (to, from, savedPosition) => {if (savedPosition) {return savedPosition} else {return new Promise((resolve, reject) => {setTimeout(() => {resolve({ x: 0, y: to.meta.savedPosition || 0 })}, 300)})}
}
const router = new VueRouter({mode: 'history',scrollBehavior,routes: [{path: '',redirect: '/mobile/home.html',meta: {needMtaReport: true,parentsStyle: {height: '100%',minHeight: '100%'}}},{name: 'scienceCompetition',path: '/mobile/scienceCompetition.html',component: scienceCompetition}]
}

总结:

1.单页缓存下js加载解析编译执行的时间缩短了,返回的时候由于走缓存js脚本的占用时间完全可以忽略,从而整体上缩减了页面的加载渲染时间

  2. 因为项目以前不是单页,代码里边定义了很多全局变量或者全局事件绑定,改成单页后全局变量的值依然存在,就会导致业务逻辑出现bug,所以使用单页需要注意全局变量或是事件的谨慎使用,具体的踩坑记录在https://www.cnblogs.com/after...

  3.通过push进入下一页时,head里边会累加前面页面的静态资源,访问的页面越多,最后的页面挂的静态的资源越多,返回的时候并不会减少已经加载的静态资源,单页缓存是典型的空间换时间的方案,内存的开销比较大,能否对资源动态增减以及内存占用的优化一直在探索中,暂时没有找到很好的解决方法。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450617.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于websocket的聊天实现逻辑(springboot)

websocket的知识点&#xff1a;当用户建立socket连接请求之后&#xff0c;服务器会给客户段建一个session&#xff08;非httpsession&#xff09;,这是是对客户端的唯一识别码&#xff0c;用于消息通信 第二上流程图&#xff0c;流程图解释&#xff1a;用户1要给用户2发送消息…

大学生学编程系列」第五篇:自学编程需要多久才能找到工作?

很多编程初学者都会有这种疑问&#xff0c;自学学到什么程度或者学多久能够找到工作&#xff0c;这种问题没有统一答案&#xff0c;因为每个人的出发时候的基础以及在学习过程中掌握的程度不尽相同&#xff0c;也会导致结果不一样&#xff0c;只能说要看个人的造化了&#xff0…

chrome 谷歌浏览器怎么添加Axure扩展

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 工具/原料 谷歌浏览器Axure RP Extension for Chrome方法/步骤 百度搜索Axure RP&#xff0c;下载Axure RP&#xff0c;并进行安装 安装后…

配置nginx-rtmp流媒体服务器(宝塔面板配置教程)

参考文档&#xff1a;https://www.kancloud.cn/jiangguowu/kfjsdkfjskd/1209896 1.在宝塔面板中安装带nginx的服务器 2.在宝塔面板中卸载nginx&#xff08;因为nginx-rtmp和nginx的配置不同&#xff0c;并且宝塔面板中不支持安装nginx-rtmp&#xff09; 3.开始预下载nginx &a…

css控制div等比高度

在移动端开发中&#xff0c;在banner轮播图未加载出来之前&#xff0c;banner层是不占文档流高度的&#xff0c;当从服务器获取完banner数据&#xff0c;展示的时候&#xff0c;banner层因为有了内容 所以会撑开&#xff0c;导致banner层下面的内容也随之移动&#xff0c;为解决…

2018杭州云栖大会,梁胜博士的演讲PPT来啦!

2019独角兽企业重金招聘Python工程师标准>>> 2018杭州云栖大会已经结束&#xff0c;Rancher作为阿里云的紧密合作伙伴&#xff0c;Rancher Labs联合创始人兼CEO梁胜博士&#xff0c;在9月21日上午受邀出席大会并作题为**“如何能让每个人都用Kubernetes和Service Me…

利用jquery修改elment的自定义组件多选框el-select(修改多选框的颜色)

先上图片 一实现逻辑 我们知道element颜色默认为灰色&#xff0c;首先便签名称是唯一的&#xff0c;我的实现逻辑是后端传给前端 含有颜色&#xff0c;名称的数组&#xff0c;然后vue记录一个对象{名称 > 颜色}&#xff0c;当用户选择标签之后&#xff0c;触发 点击事件&…

中文地址

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/2935389/blog/2209087

Intellij IDEA Debug调试技巧

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、这里以一个web工程为例&#xff0c;点击图中按钮开始运行web工程。 2、设置断点 3、使用postman发送http请求 4、请求发送之后会自动…

15行代码让苹果设备崩溃,最新的iOS 12也无法幸免

安全研究人员Sabri Haddouche发现了一个只需几行代码就可以让iPhone崩溃并重启的方法。\\Sabri Haddouche在GitHub上发布了一个示例网页&#xff0c;只有15行代码&#xff0c;如果在iPhone或iPad上访问这个页面&#xff0c;就会崩溃并重启。在macOS上使用Safari打开该页面也会出…

appium更新到1.8.2,不能打开运行的解决办法

1、更新下载appium 1.8.2 打开运行 一直是这个界面。很烦躁&#xff0c;重启电脑或者卸载后重新安装还是没有用。 解决版本&#xff1a; 1、查看老版本和新版本的安装位置 老版本默认是 C:\Program Files (x86)/appium安装新的版本后&#xff0c;地址是&#xff1a;C:\Users…

laraval如何使用tdd

1.首先新建一个laravel birdboard项目 composer create-project --prefer-dist birdboard 2.新建单元测试 php artisan make:test ProjectTest 3.书写单元测试 对于初学着来说&#xff0c;最好先预测tdd即将要出现的错误&#xff0c;然后进行测试&#xff0c;判断是否和自…

spring-data-JPA使用JpaRepository注解自定义SQL查询数据库多表查询

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一. 首先在Entity注解的类里面要写好外键关系. 这个 ManyToOne 注解可以建立外键关系, 不要在自己傻傻的写一个 private int grades_id…

posman使用教程

1.新建文件夹 2.新建请求&#xff0c;右击文件夹&#xff0c; 3.点开测试文件&#xff0c;有get,put,post方法&#xff0c;我经常使用的是put方法&#xff0c;区别我就不讲了 4.我基本上填在body里面 5.这是基本的使用&#xff0c;我来更高级一点添加环境变量&#xff0c;一共有…

Linux实战教学笔记42:squid代理与缓存实践(一)

第1章 Squid介绍 1.1 缓存服务器介绍 缓存服务器&#xff08;英文意思cache server&#xff09;,即用来存储&#xff08;介质为内存及硬盘&#xff09;用户访问的网页&#xff0c;图片&#xff0c;文件等等信息的专用服务器。这种服务器不仅可以使用户可以最快的得到他们想要的…

mac电脑php中安装swoole扩展件

1.首先更新php版本&#xff0c;如果已经是最新的请忽略&#xff0c; &#xff08;1&#xff09;查看是否安装php brew search php &#xff08;2&#xff09;安装最新版本php brew install php 2.查看是否安装openssl&#xff0c;安装了请忽略 &#xff08;1&#xff09;查看…

再谈C语言指针—指向另一指针的指针

一、回顾指针概念 早在本书第贰篇中我就对指针的实质进行了阐述。今天我们又要学习一个叫做“指向另一指针地址”的指针。让我们先回顾一下指针的概念吧&#xff01;当我们程序如下声明变量&#xff1a;short int i;char a;short int * pi;程序会在内存某地址空间上为各变量开辟…

spring-data-jpa 介绍 复杂查询,包括多表关联,分页,排序

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 本篇进行Spring-data-jpa的介绍&#xff0c;几乎涵盖该框架的所有方面&#xff0c;在日常的开发当中&#xff0c;基本上能满足所有需求。…

Alpha 冲刺五

团队成员 051601135 岳冠宇051604103 陈思孝031602629 刘意晗031602248 郑智文031602234 王淇会议照片 项目燃尽图 项目进展 暂无实质性进展。 项目描述 问题困扰&#xff1a; 商品分类出现困惑。交互部分向服务器发送请求失败&#xff0c;安卓在4.0后对网络请求有限制要求&…

python -m xxx.py和python xxx.py的区别

先看下python -m site作用是显示sys.path的值内容&#xff0c;也就是python搜索模块的目录&#xff0c;作用类似于linux下的PATH python -m SimpleHTTPServer 会在sys.path的所有路径下查找SimpleHTTPServer.py文件&#xff1b; 而python SimpleHTTPServer.py则是在当前查找文…