dns
在请求任何资源前,浏览器需要知道你的目标服务器ip是什么,此时会将目标资源的域名通过dns解析出相应的ip,再进行链接获取;
日常应该很难体验到dns解析带来的阻塞,但是在弱网情况下dns解析就会有明显的延迟了,我们可以通过在link
标签上添加dns-prefetch
来进行预解析,预解析是在页面加载的同时进行的所以不会带来体验负担,解析之后的ip会存储到操作系统的缓存中相比于从路由器中获取缓存要快得多。
- 可以在html头部写link标签来实现
- 也可以通过在消息头中设置link实现
Link: ; rel=dns-prefetch
在https
协议时搭配dns-precontent
食用更加,dns-precontent
会提前建立TCP链接并且进行TLS握手,进一步减少延迟;
注意不要滥用,过度使用会加大DNS解析开销造成网络负担;
另外要注意资源域名的收敛(当dns解析开销大的弱网情况下进行收敛)与发散(浏览器有并发请求数限制,发散后可以提高并发请求能力);
CSS
在浏览器绘制页面的流程中会将html形成的dom树和css形成的CSSOM树合并之后进行下一步的渲染,所以生成CSSOM树的速度也是会影响页面渲染的原因之一;
- 可以将多媒体查询css进行拆分,形成单独的文件,并且利用
media
属性帮助浏览器识别;
// 大于480宽时会下载此mobile.css 但是不会阻塞渲染;
- 通过gpu来计算动画。
浏览器针对动画进行了优化,会将一些属性和标签进行优化使其动画转换属性在gpu中完成提升性能;包括 3D transforms (transform: translateZ()
,rotate3d()
等),animating transform 和opacity
,position: fixed
,will-change
,和filter
。一些元素,例如,
和
。tip: 在做动画时不考虑兼容性情况下最好使用
will-change
代替translate3d()或translateZ(),translate3d()或translateZ()可能会使transform动画延迟几百毫秒。但是不能滥用will-change
,除非是真的不行了;
//如果你试图将will-change和动画同时使用,它将不会给你带来优化。
//因此,建议在父元素上使用will-change,在子元素上使用动画
.animate-element-parent {
will-change: opacity;
}
.animate-element {
transition: opacity .2s linear
}
-
font-display
属性定义了浏览器如何加载和显示字体文件,允许文本在字体加载或加载失败时显示回退字体。可以通过依靠折中无样式文本闪现使文本可见替代白屏来提高性能(不过经过测试发现默认行为都是block了,目前应该没有这种情况)。
@font-face {
font-family: someFont;
src: url(/path/to/fonts/someFont.woff) format('woff');
font-weight: 400;
font-style: normal;
font-display: fallback;
}
font-display
有以下几个属性:auto、block、swap、fallback、optional,可自行理解差异;font-display
可以解决由于字体没加载出来而导致的布局混乱的问题。
- content-visibility属性
- visible: 默认值, 可见的
- hidden: 隐藏, 此时内容不可选中, 不可聚焦. 类似于display: none
- auto: 自动. 对于用户可见区域的元素,浏览器正常渲染内容,对于不可见的区域元素, 浏览器会跳过内容的绘制
实例常见的ssr列表页看下渲染时间,每页40条数据的样子;
- 。。。。
之后在
item-container
加上content-visibility
;
.item-container{
content-visibility: auto;
}
渲染时间大大减少;特别适合长列表,但是就是兼容性不咋好,所以建议搭配其他策略一起;
- css写的时候层级最好不要太深
- css内外分离,将不重要或非首屏进行异步加载
- 体验相关在点击跳转锚点时,设置该属性就会丝滑滚动到锚点而不是生硬直接跳转到
html {
scroll-behavior:smooth;
}
JS
js的加载会阻塞html的渲染这个想必大家都知道,这就是我们都会把js放到body底部的原因;另外将较大的js文件进行拆分,拆成主流程js及其他辅助功能js,适度减小js包体可优化页面加载时间;
- 在
script
标签上添加type=module
,此时的脚本会被视为js模块,并且默认会被延迟加载(默认defer异步加载)。 - 可在
script
标签上添加defer
或async
。没有这两个标签的话浏览器会立即加载并执行脚本会阻塞html的渲染。scrpit带有defer
或async
属性中的任意一个,都会使得scrpit下载和html解析并行进行,不会阻塞html解析;唯一的区别是:defer
是在html解析完成后在DOMContentLoaded
触发前执行且是顺序执行;但async
是下载完成后立即执行是乱序执行;
- 获取设置dom样式时做到
读写分离
,这样做是为了减少重绘和重排次数;
当我们频繁设置dom样式时浏览器会有优化策略,合并之后在进行重排/重绘。
const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px'; // 只会进行一次重绘
但是当我们要获取dom的布局信息(offsetLeft等)时为了保持数据准确会立刻进行重排/重绘;
const el = document.getElementById('test');
el.style.padding = '5px'; // 重排重绘
console.log(el.offsetLeft) // 重排
el.style.borderLeft = '1px'; // 重排重绘
console.log(el.offsetTop) // 重排
el.style.borderRight = '2px'; // 重排重绘
console.log(el.offsetWidth) // 重排
const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px'; // 以上一次重排重绘
console.log(el.offsetWidth)
console.log(el.offsetLeft)
console.log(el.offsetTop)
- 插入dom时可先使用
createElementFragment
创建文档碎片,将要新建的dom构建好后一次性插入dom中。
img iframe
- 可以使用属性
loading=lazy
进行懒加载,但是有一定兼容性问题;(有些坑可以度娘展开看看)
'loading' in HTMLImageElement.prototype // 可判断浏览器支持与否
可以通过原生apiIntersectionObserver
来实现
let options = {
//指定根 (root) 元素,用于检查目标的可见性。必须是目标元素的父级元素。如果未指定或者为null,则默认为浏览器视窗
root: document.querySelector('#scrollArea'),
// 根 (root) 元素的外边距。
// 类似于margin属性。如果有指定 root 参数,则 rootMargin 也可以使用百分比来取值。该属性值是用作 root 元素和 target 发生交集时候的计算交集的区域范围,使用该属性可以控制 root 元素每一边的收缩或者扩张。默认值为 0
rootMargin: '0px',
// 可以是单一的 number 也可以是 number 数组,target 元素和 root 元素相交程度达到该值的时候 IntersectionObserver 注册的回调函数将会被执行。如果你只是想要探测当 target 元素的在 root 元素中的可见性超过 50% 的时候,你可以指定该属性值为 0.5。如果你想要 target 元素在 root 元素的可见程度每多 25% 就执行一次回调,那么你可以指定一个数组 [0, 0.25, 0.5, 0.75, 1]。默认值是 0 (意味着只要有一个 target 像素出现在 root 元素中,回调函数将会被执行)。该值为 1.0 含义是当 target 完全出现在 root 元素中时候 回调才会被执行。
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
阈值为 1.0 意味着目标元素完全出现在 root 选项指定的元素中可见时,回调函数将会被执行。
- 图片加载不出导致碎图
常见场景在写image
时会显示默认图,之后快到可视区域后才进行加载真正的图片;
// 通过IntersectionObserver监听是否快到可视区域之后将真实图替换到src属性
new InVisible({
isVisible: 'data-loaded',
targetKey: 'lazy_src',
callBack: (cur) => {
let defaultSrc = cur.getAttribute('src');
let src = cur.getAttribute('lazy_src');
cur.src = src;
// 替换之后监听onerror以免碎图
const fnType = cur.addEventListener ? 'addEventListener' : 'attachEvent';
cur[fnType]("error", () => {cur.src = defaultSrc;});
}
})
只有默认图不是很语义化,img
有个特殊的特性,她的伪元素在error的时候才会生效,正常情况下不会生效,利用这一特性我们可以通过css解决碎图和语义化的问题;
.img::after{
position: absolute;
content: " ";
background: url("默认图.png") no-repeat;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-size: cover;
}
.img::before{
position: absolute;
z-index:1;
bottom: 0;
left: 0;
background: rgba(0,0,0,0.5);
// 可以使用attr动态设置 eg: attr(tittle)
content: "前海人寿金融中心楼盘图片";
width: 100%;
text-align: center;
color: #fff;
}
以上