建议去印象笔记中查看:https://app.yinxiang.com/fx/858b24e1-21bf-4493-8e82-51d4ae76e9e0
DNS 的预解析或者 TCP 协议的负载均衡
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
loader: 'babel-loader?cacheDirectory=true'
优点
缺点
优点
优点
缺点
优点
缺点
1.兼容性不高
chrome介绍
通过网络获取内容既速度缓慢又开销巨大。较大的响应需要在客户端与服务器之间进行多次往返通信,这会延迟浏览器获得和处理内容的时间,还会增加访问者的流量费用。因此,缓存并重复利用之前获取的资源的能力成为性能优化的一个关键方面。
强缓存是利用 http 头中的 Expires 和 Cache-Control 两个字段来控制的。强缓存中,当请求再次发出时,浏览器会根据其中的 expires 和 cache-control 判断目标资源是否“命中”强缓存,若命中则直接从缓存中获取资源,不会再与服务端发生通信。
expires
设置过期时间,由服务端返回一个具体时间,如下格式:
expires: Wed, 11 Sep 2019 16:12:18 GMT
当客户端在次发起请求的时候将判断客户端时间与该时间,如果小于该时间则不再请求服务器,直接从浏览器中拿去。
弊端为如果客户端时间与服务器时间一致性较差将导致该功能不太准确
Cache-Control
设置过期倒计时秒数,由服务端返回为具体秒数,如下格式:
cache-control: max-age=31536000
当客户端发起请求后服务端返回cache-control字段,通过max-age记录时间,客户端再起请求后会拿当前时间和浏览器记录的上次请求时间+max-age作比较,如果小于该时间则不再请求服务器。
public 与 private
public 与 private 是针对资源是否能够被代理服务缓存而存在的一组对立概念。
如果我们为资源设置了 public,那么它既可以被浏览器缓存,也可以被代理服务器缓存;如果我们设置了 private,则该资源只能被浏览器缓存。private 为默认值
noCache
no-cache 绕开了浏览器:我们为资源设置了 no-cache 后,每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期(即走我们下文即将讲解的协商缓存的路线)。
noStore
no-store 比较绝情,顾名思义就是不使用任何缓存策略。在 no-cache 的基础上,它连服务端的缓存确认也绕开了,只允许你直接向服务端发送请求、并下载完整的响应。
协商缓存依赖于服务端与浏览器之间的通信。
协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。
Last-Modified
Last-Modified是一个时间戳,如果我们开启协商缓存,那在我们第一次请求资源的时候respHeader会返回该字段,当我们下次在请求该资源的时候,会在请求头加上
If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT
值为上一次返回的Last-Modified,服务端会判断该时间戳与资源最后一次修改时间是否一致,如果一致,返回304,不一致返回新的内容,并且重新返回Last-Modified
弊端
1.如果我们编辑了文件,但是实际没有改动,依然后导致重新请求,不走缓存
2.如果我们编辑文件过快,If-Modified-Since是以秒为单位的,所以可能会存在问题
Etag
Etag 是由服务器为每个资源生成的唯一的标识字符串,这个标识字符串是基于文件内容编码的,只要文件内容不同,它们对应的 Etag 就是不同的,反之亦然。因此 Etag 能够精准地感知文件的变化。
Etag 的生成过程需要服务器额外付出开销,会影响服务端的性能,这是它的弊端。因此启用 Etag 需要我们审时度势。正如我们刚刚所提到的——Etag 并不能替代 Last-Modified,它只能作为 Last-Modified 的补充和强化存在。 Etag 在感知文件变化上比 Last-Modified 更加准确,优先级也更高。当 Etag 和 Last-Modified 同时存在时,以 Etag 为准。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RbBEf5Aw-1646194856300)(en-resource://database/519:1)]
简单理解就是我把资源copy放到各个地方的服务器,当你发出请求的时候,会找到离你最近的服务器来进行请求,这样就可以提升访问的速度。
缓存
把资源copy到cdn服务器上的操作
回源
当检查到资源过期后转头像根服务器要资源的过程
我们通常把静态资源放到cdn服务器上,例如JS,CSS,图片等内容,不需要后端进行运算的资源,
当我们在访问淘宝的时候会发现他的资源是从多个不同的域名请求过来的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0gb3FlN7-1646194856302)(en-resource://database/520:1)]
这么做带来了几个好处:
服务端渲染与之相对的就是客户端渲染,客户端渲染可以理解为用户请求html后把里面的js跑一遍,根据结果渲染页面,请求的html里面基本上是没有dom结果的,渲染的内容根据js的结果不同而不同。
服务端会把页面内容进行组装和拼接,用户拿到的html基本上就可以直接在浏览器上渲染的内容。
弊端
对服务器压力较大,如果软件使用量大,会导致服务器压力增大
浏览器内核由 渲染引擎和 JS 引擎组成
tips
css阻塞
css是会阻塞dom树的渲染的,所以哪怕我们dom树渲染完成,css树没有渲染完成的话 页面上也是不会出现内容的。
这个时候我们就想办法提前加载css和 尽快加载css于是启用了两种手法。
js阻塞
js同样会阻塞css和dom树的渲染,因为浏览器无法预料到js执行完后 会有什么改变,所以可以理解为遇到script标签后,js引擎会抢占渲染引擎,优先执行js,所以我们通常的做法是,在最后加载script除非这个script标签不得不在中间出现
正常模式
这种情况下 JS 会阻塞浏览器,浏览器必须等待 index.js 加载和执行完毕才能去做其它事情。
async 模式
async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。
defer 模式
defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行
从应用的角度来说,一般当我们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,我们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行结果时,我们会选用 defer。
减少dom的操作
执行顺序
宏任务(macroTask)=>微任务(microTask)=>更新UI=>执行webWorker
回流
当我们对DOM属性的几何尺寸(宽高,位置等信息)进行修改可能会引起回流。浏览器从新计算几何属性然后重新绘制。
重绘
当我们对DOM的非几何属性(颜色,背景色等)进行操作的时候会触发重绘。
重绘不一定导致回流,回流一定会导致重绘
如何避免回流
会导致回流的操作
tips
参考:https://juejin.cn/post/6844903455048335368
核心原理:
先用一个div占用空间,判断图片是否滚动到可是区域内,如果滚动到了替换图片资源,并且记录下标减少重复加载的几率,同时使用节流进行相关优化。
节流含义
当事件第一次触发后,在一定时间内将不会再触发。
防抖含义
当事件触发后再一定时间内如果事件在此触发则更新时间,知道时间结束时间没有被触发才开始执行。
tips
防抖和节流函数核心都是用定时器实现的
throttle节流代码实现
// fn是我们需要包装的事件回调, interval是时间间隔的阈值
function throttle(fn, interval) {
// last为上一次触发回调的时间
let last = 0
// 将throttle处理结果当作函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 记录本次触发回调的时间
let now = +new Date()
// 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
if (now - last >= interval) {
// 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
last = now;
fn.apply(context, args);
}
}
}
// 用throttle来包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
document.addEventListener('scroll', better_scroll)
debounce节流代码实现
// fn是我们需要包装的事件回调, delay是每次推迟执行的等待时间
function debounce(fn, delay) {
// 定时器
let timer = null
// 将debounce处理结果当作函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 每次事件被触发时,都去清除之前的旧定时器
if(timer) {
clearTimeout(timer)
}
// 设立新定时器
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
// 用debounce来包装scroll的回调
const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000)
document.addEventListener('scroll', better_scroll)