读完这篇文章,我希望我可以给大家分享到的知识点
Vue.js 中那些空间换时间的操作
cache-loader的实现原理,缓存的应用
babel-loader,cacheDirectory=true时缓存的应用
浏览器缓存

我们知道vue2.x是通过遍历对象的key去创建响应式对象的,如果一个对象足够大,那么递归遍历是非常耗时的
vue2.x中使用Object.defineProperty把对象变成响应式,一旦某个对象经过Object.defineProperty变成响应式对象后,会把响应式结果通过__ob__存储起来,这样一来,同样的对象如果再次执行observe,则从缓存的__ob__中直接拿到对应的响应式值并返回
vue对data进行处理,initState => initData => observe => 创建Observer对象 => def(value, 'ob', this)通过执行 def 函数把自身实例添加到数据对象 value 的 ob 属性上
在谈keep-alive前,先聊一个算法LRU,LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面!
新数据插入到链表头部
每当缓存命中(即缓存数据被访问),则将数据移到链表头部
当链表满的时候,将链表尾部的数据丢弃
keep-alive实现了vue的组件实例缓存,2.x和3.0的实现策略一致,都是对LRU缓存策略的应用,可以去看下源码实现
vue2.x源码在vue/src/core/components/keep-alive.js
vue3.0源码在vue-next-master/packages/runtime-core/src/components/KeepAlive.ts
我们都知道.vue文件中的template是通过vue-template-compiler进行ast转换的,然而编译是非常耗时的,如果每次都重新分析ast,组装render函数,性能肯定大大降低,因此在设计的时候也用到了缓存,以空间换时间,达到了渲染优化的效果,这里以2.x版本为例
cache-loader
webpack编译时优化,cache-loader是一个很好的应用,cache-loader作为webpack的loader,会在pitch和loader两个阶段分别做一些事情:
pitch阶段:校验缓存文件是否可用
loader阶段:判断当前loader的文件是否需要重新生成缓存
webpack再次编译时,根据当前正在处理的文件,尝试读取.cache目录中对应的cache文件,处理当前文件以及所以来的文件,比较当前文件与上一次loader的产物
判断当前文件是否需要重新生成缓存,判断逻辑:如果pitch阶段的判断当前文件的缓存失效了,那么loader阶段就要去生成缓存
babel-loader缓存的应用
webpack配置中使用babel-loader解析源码时可以配置cacheDirectory=true来开启缓存,执行babel-loader中定义的cache方法
cache方法的核心实现在handleCache中,实现原理和cache-loader类似,尝试读取缓存,存在就返回,否则继续创建缓存目录,通过babel.transform转译代码,执行gzip压缩并写入缓存
service worker
memory cache
disk cache
push cache
Expires
用来指定资源到期的时间,是服务端的具体时间点,Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效
Cache-Control
已知Expires的缺点之后,在HTTP/1.1中,增加了一个字段Cache-control,Cache-Control的优先级高于Expires,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求,与Expires的区别就是前者是绝对时间,而后者是相对时间
指令
作用
public
响应可以由任何缓存存储,即使响应通常是不可缓存的
private
响应可能仅由浏览器的缓存存储,即使响应通常是不可缓存的
no-cache
响应可以由任何缓存存储,即使响应通常是不可缓存的。但是,在使用存储的响应之前,必须首先通过源服务器的验证,因此,不能将no cache与immutable结合使用
no-store
响应不能存储在任何缓存中
max-age=<seconds>
资源被认为是新鲜的最长时间。与Expires不同,此指令与请求的时间有关
s-maxage=<seconds>
作用同max-age
max-stale=<seconds>
指示客户端将接受过时的响应。以秒为单位的可选值表示客户端将接受的过期上限
min-fresh=<seconds>
指示客户端希望响应至少在指定的秒数内保持新鲜
stale-while-revalidate=<seconds>
指示客户端将接受过时响应,同时在后台异步检查新响应
stale-if-error=<seconds>
指示如果对新响应的检查失败,客户端将接受过时的响应
must-revalidate
指示一旦资源变为过时,如果未在源服务器上成功验证,缓存就不能使用其过时副本
proxy-revalidate
必须重新验证,但仅限于共享缓存(例如代理)
immutable
指示响应主体不会随时间而更改
no-transform
中间缓存或代理无法编辑响应正文、内容编码、内容范围或内容类型
only-if-cached
由客户端设置,以指示响应的“不使用网络”
服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间,例如
Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
浏览器将这个值和内容一起记录在缓存数据库中
下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据
缺陷:
如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用
Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。
Etag 的优先级高于 Last-Modified
强缓存优先于协商缓存
协商缓存失效,返回200,重新返回资源和缓存标识
协商缓存生效,返回304,继续使用缓存
频繁变动的资源
不常变动的资源