keep-alive
生命周期
keep-alive的页面:
第一次进入:created-> mounted-> activated,退出:deactivated。
再次进入,触发activated(这时我们可以拿到组件的所有data,可在此节点做一些请求更新页面数据)
keep-alive不生效实例
// router设置
{
path: '/list',
name: 'List',
component: () => import('@/views/order/list'),
meta: {keepAlive: true} // 设置页面缓存,保持tab状态
},
{
path: '/detail',
name: 'Detail',
component: () => import('@/views/order/detail'),
meta: {keepAlive: false} // 不需要缓存
},
// 页面配置,v-if的位置避免页面不缓存问题
<keep-alive :include="keepAliveView">
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
// 设置需要缓存的白名单
<script>
export default {
data(){
return {
keepAliveView:'', //需要缓存的组件名称列表,用逗号分隔
};
},
created(){
this.getKeepAliveView();
},
methods:{
getKeepAliveView(){ //每一帧都获取可缓存列表,防止前进时设置缓存会慢一步
const self = this;
requestAnimationFrame(function func(){
let list = self.$router.options.routes.filter(item=>{
return item.meta.myKeepAlive;
}).map(item=>{
return item.component.name; //这里使用的是组件名。keep-alive是根据组件名判断展示那个组件,如果组件名和路由配置的名称一样可以直接写 item.name (如果配置了路由懒加载的话没有component.name)
});
self.keepAliveView = "," + list.join(","); //必须首部加逗号。
requestAnimationFrame(func);
});
},
},
}
</script>
需求是前进缓存,后退清除缓存
场景 Vue中前进刷新,后退缓存用户浏览数据
列表页面 =>点击进入详情页=> 后退到列表页 要缓存列表原来数据
重新进入列表页面 => 获取最新的数据
//添加全局路由守卫,用来判断页面前进或是后退
router.beforeEach((to, from, next) => { //页面跳转后添加时间戳参数
if (typeof to.query._t !== "undefined") {
next();
} else {
to.query._t = new Date().getTime().toString();
next(to);
}
});
在需要用到的地方
watch:{
$route(to,from){ //当页面返回时将取消缓存
if (parseInt(to.query._t) < parseInt(from.query._t)){ //表示是返回页面
if(from.name === this.$options.name){ //如果是当前页面返回的话(this.$options.name是当前组件的名字)
from.meta.myKeepAlive = false;
}
}
},
},
当页面返回的时候设置它的myKeepAlive为false,为什么不是直接用from或者to,因为返回页面包括从上一个页面返回或者返回当前页面,from,to都可能不是当前出口的路由对象。
原理
// src/core/components/keep-alive.js
export default {
name: 'keep-alive',
abstract: true, // 判断此组件是否需要在渲染成真实DOM
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created() {
this.cache = Object.create(null) // 创建对象来存储 缓存虚拟dom
this.keys = [] // 创建数组来存储 缓存key
},
mounted() {
// 实时监听include、exclude的变动
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
destroyed() {
for (const key in this.cache) { // 删除所有的缓存
pruneCacheEntry(this.cache, key, this.keys)
}
},
render() {
// 下面讲
}
}
pruneCacheEntry函数
咱们上面实现的生命周期destroyed中,执行了删除所有缓存这个操作,而这个操作是通过调用pruneCacheEntry来实现的,那咱们来说说pruneCacheEntry里做了啥吧
// src/core/components/keep-alive.js
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy() // 执行组件的destory钩子函数
}
cache[key] = null // 设为null
remove(keys, key) // 删除对应的元素
}
复制代码
总结一下就是做了三件事:
- 1、遍历集合,执行所有缓存组件的
$destroy方法 - 2、将
cache对应key的内容设置为null - 3、删除
keys中对应的元素
js预加载
webpack代码分割技术
首先要分析大js的组成,然后看能拆分成多少个独立的js。再分析独立的JS之间的依赖关系,确定加载顺序。 如果你是用webpack相关的打包工具,它也是支持打包成多个文件的,配置就可以了。参考: 代码拆分是 webpack 最引人注目的特性之一。此功能允许您将代码拆分为各种捆绑包,然后可以按需或并行加载。它可用于实现更小的捆绑包并控制资源负载优先级,如果使用得当,将对加载时间产生重大影响。 有三种通用的代码拆分方法:
- 入口点
entry:使用配置手动拆分代码。 - 防止重复:使用条目依赖项或
SplitChunksPlugin重复数据删除和拆分块。 - 动态导入:通过模块内的内联函数调用拆分代码。
预加载技术
web缓存技术
http缓存
文件太大,通常预加载也是等不起的,通过浏览器缓存,这样用户再第一次请求之后,就会缓存下来,下次就从http缓存中读取了。参考 当一个用户发起一个静态资源请求的时候,浏览器会通过以下几步来获取资源:
- 本地缓存阶段:先在本地查找该资源,如果有发现该资源,而且该资源还没有过期,就使用这一个资源,完全不会发送http请求到服务器;
- 协商缓存阶段:如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期,则发一个http请求到服务器,然后服务器判断这个请求,如果请求的资源在服务器上没有改动过,则返回304,让浏览器使用本地找到的那个资源;
- 缓存失败阶段:当服务器发现请求的资源已经修改过,或者这是一个新的请求(在本来没有找到资源),服务器则返回该资源的数据,并且返回200, 当然这个是指找到资源的情况下,如果服务器上没有这个资源,则返回404。
服务器端缓存
CDN缓存
localStorage
构建可缓存站点的建议
来自alloyteam:如何构建可缓存站点
- 同一个资源保证URL的稳定性
- 给Css、js、图片等资源增加HTTP缓存头,并强制入口Html不被缓存
- 减少对Cookie的依赖
- 减少对HTTPS加密协议的使用
- 多用Get方式请求动态Cgi
- 动态CGI也是可以被缓存