加载优化
- webpack压缩合并
CommonsChunkPlugin主要是用来提取第三方库和公共模块,避免首屏加载的bundle文件或者按需加载的bundle文件体积过大 - 代码分割,基于路由或者模块分割
- 第三方模块使用cdn方式
- 大模块异步加载,require.ensure
mapBtn.click(function() {
require.ensure([], function() {
var baidumap = require('./baidumap.js') //baidumap.js放在我们当前目录下
})
})
//等价于
mapBtn.click(function() {
//获取 文档head对象
var head = document.getElementsByTagName('head')[0];
//构建 <script>
var script = document.createElement('script');
//设置src属性
script.async = true;
script.src = "http://map.baidu.com/.js"
//加入到head对象中
head.appendChild(script);
})
//第一个参数用于依赖优先下载,如果A和B都要异步下载,B依赖于A,则需要先下载A
//也是请求下载的模块,如果想加载A require.ensure(['A.js'],function) 即可
- 零散的小模块合并一起加载
使用entry 指定文件入口,或者头部import/require建立依赖关系 - 使用预加载prefetch,适用于分步场景
图片优化
- 小图片都是用sprite图片整合在一张图片,减少并发的次数,base64内联,
- 图片是用懒加载
- 使用webp格式
- 图片要压缩
- 使用srcset,配置不一样的分辨率对应不同的图片大小,能精准对应不同的尺寸用户,节省流量。
css优化
- css写在头部
为了尽快在屏幕上显示内容,浏览器不会等到所有的HTMl元素解析之后在构建和布局dom树
但是如果css写在底部,浏览器会一直等待 css全部加载完,才开始解析。
而且下载并解析css完毕后,已经呈现的文字和图片就要需要根据新的样式重绘 - 避免css表达式
background-color: expression((new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
- 移除没用的css定义规则
- 减少行内style样式
样式分离,具有更好的易读性和方便维护
js优化
1. js放在body底部
这样处理的好处是无需担心因页面未完成加载,造成DOM节点获取不到,而且能避免因脚本运行缓慢造成页面卡死的问题。
2. js用defer放在头部,提前加载,又不阻塞dom解析
script标签的3个加载方式
- 默认,按顺序加载和执行,会阻塞
- async 异步下载,并一定会在onload之前执行,不按声明的顺序,只要加载完可能就立刻执行,会影响渲染。
- defer 异步下载,并一定会在onload之后执行,在DOMContentLoaded之前执行。多个defer会按顺序执行。
3. script标签添加crossorigin ,方便收集错误,
不加只能看到error: Script error.很少信息。注意:服务端必须开启Access-Control-Allow-Origin * ,不然也是无效,
渲染优化
1.尽量减少reflow回流和repaint重绘
网页如何生成:
- 解析html绘制DOM树
- 解析css绘制CSS树
- 生成render tree(渲染树)
- flow排列,将渲染树节点合成(渲染)
- paint绘制,将排列绘制在屏幕上(渲染)
触发重排属性和方法
改变元素几何信息(大小和位置),都会引起
- DOM改变
- input 输入内容改变
- resize
- style属性改变
- margin
- 计算offsetWidth和offsetHeight
触发重绘的条件:
- 改变元素外观属性。如:color,background-color等。
2.用变量缓存dom,不要频繁操作dom读取。
比如 let a = $("#a")
下次就直接使用a,而不是再调用一次查询$("#a")
3.使用DocumentFragment或者innerHtml批量插入dom
4.使用类似虚拟dom的方式完成所有修改,最后再一次性更新
5.动画元素使用absolute,脱离稳定流,不影响其他元素。动画不使用left,top设置,该用transform和opacity,同时开启渲染层 will-change 或者 translate3d(0,0,0)
transform和left,top动画区别
- transform是视觉上的动画,不改变元素在dom里面的位置,并能开启GPU单独渲染,不影响主线程,只占用合成线程。
- left,top动画会触发整个页面的重绘,重排,占用主线程,同时也会同步信息到 合成线程进行渲染。
GPU3个加速的属性transform, opacity, filter
(1). 主线程的任务:
- 运行Javascript
- 计算HTML元素的CSS样式
- layout (relayout)
- 将页面元素绘制成一张或多张位图
- 将位图发送给合成线程
(2). 合成线程的任务: - 利用GPU将位图绘制到屏幕上
- 让主线程将可见的或即将可见的位图发给自己
- 计算哪部分页面是可见的
- 计算哪部分页面是即将可见的(当你的滚动页面的时候)
- 在你滚动时移动部分页面
6.动画尽量使用requestanimationFrame,不用定时器。
显示器有固定的刷新频率(60Hz 或 75Hz)。也就是说,每秒最多只能重绘 60 次或 75 次,1000/60 = 16.7ms ,1000ms / 75 = 13.3ms
requestAnimationFrame 的基本思想让页面重绘的频率与 这个刷新频率保持同步
注意settimeout和setinterval 是依赖event loop 有可能设置 settimeout( ()=>{},16.7)//会在18秒才触发,因为要等待前面队列的任务处理完。
requestanimationFrame
7.移动端硬件加速,触发gpu渲染,translate3d(0,0,0)
首屏优化
原则 显示快,滚动流畅,懒加载,懒执行,渐进式展示
- 代码分离,将首屏以外的代码分离掉
- 服务端渲染/ 预渲染,减少白屏时间
- DNS prefetch,pc端域名发散,移动端域名 收敛
浏览器 发起URL请求的过程:阻挡、域名解析、建立连接、发送请求、等待响应、接收数据,
(1)阻挡:浏览器处于性能考虑,相同的域名一次只能限制4-8条请求,其他均会阻塞,为了解决这个问题,可以把资源拆散到不同的二级域名下面。
(2)域名解析:由于域名解析是在请求域名时候解析,会占用一部分解析的时间,可以提前预加载dns
配置如下:meta告诉浏览器打开预dns解析,link添加rel="dns-prefetch" 告诉浏览器可以把对应的href进行提前解析,不用等到加载的时候。
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//www.zhix.net">
- 通过第三方工具分析当前页面关键路径css,把关键css改成内联。有效减少加载与渲染时间。
每次页面加载都需要等待所有css加载执行完毕,才开始渲染,如果css体积过大可能会出现上时间白屏,可以把当前页面所需要的所有关键的css提取出来。直接内联到当前页面,那么css所需要的时间可以有效缩短。可以通过第三方工具分析提取有效的css进行内联。
打包优化
- 拆包DllPlugin
- 合并公用文件 CommonsChunkPlugin
- 缩小打包工具搜索范围 include exclude
- 开启缓存 loader启动cache
- 多线程加速打包 thead-loader
- tree-shaking开启摇树,移动无用的死代码
- scope hoisting es6 模块分析,(作用域提升)将多模块合并到一个函数,减少内存招用,减小体积,提高运行速度。
vue 性能优化
主要探讨Vue代码层面的优化
- 路由懒加载
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import('./Foo.vue') } ]
})
- keep-live 缓存组件
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
- 列表添加key,确保唯一性
- 列表绑定事件使用事件代理(v-for)
- v-if 和 v-for不同时使用,因为for优先,可以提取在外层先v-if判断
- 展示不变的大列表数据object.freeze冻结,不触发响应逻辑。
let list = Object.freeze(res.data);
- 在需要复用大量数据显示的场景,使用v-show 代替v-if
- 对于大数据展示,可以使用虚拟滚动,先渲染可视力=范围 vue-virtual-scroller,vue-virtual-scroll-list
- 组件销毁时,只会自动解绑全部指令和事件绑定,仅仅限于组件本身,像setInterval需要手动解除
created() {
this.timer = setInterval(this.refresh, 2000) },
beforeDestroy() {
clearInterval(this.timer)
}
10.图片懒加载 vue-lazyload
<img v-lazy="/static/img/1.png">
react优化
- 路由懒加载 react-loadable
- 类组件添加 shouldComponent或PureCompoent
- 函数组件添加React.memo
- 列表添加key
- 函数组件使用hook 优化,useMeno useCallback
SEO优化
- 添加各种meta 信息 key 关键字
- 预渲染
构建阶段生成匹配预渲染路径的 html 文件 - 服务端渲染