加载时
加载http请求
如果服务器支持HTTP2,那么HTTP2的新特性多路复用(MultiPlexing)能够有效的解决这个问题。
Web 前端 80% 的响应时间花在图片、样式、脚本等资源下载上。
一个 HTTP 请求要经历一个复杂的流程: DNS查找 – TCP握手(三次握手) – 浏览器发出HTTP请求 – 服务器接收请求 – 服务器处理请求和响应结果 – 浏览器接收请求 – TCP断开连接(四次挥手)。
因为一个HTTP请求就要经历这上述等一系列复杂的过程,这还是在不考虑网络环境因素的情况下的,所以当请求比较多时,这就直接体现在性能的消耗上了,这也就是为什么要将多个小文件合并成一个大文件,减少HTTP请求次数的重要原因了。
减少重定向
尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验。
如果一定要使用重定向的话,如http重定向到https,要使用301永久重定向,而不是302临时重定向,因为如果使用302则每一次访问http都会重定向到https页面,而永久重定向在第一次从http重定向到https之后,每次访问http,会直接返回https的页面。
使用服务端渲染
客户端渲染经历流程:获取HTML文件 – 按需要再获取JS文件 – 运行文件 – 生成DOM – 将DOM插入HTML中 – 最后再渲染页面。
服务端渲染流程:只要获取识别解析HTML文件即可。而且服务端渲染还能有效增强站点的SEO。
静态资源使用CDN
什么是CDN:内容分发网络。它是一组分布在多个不同地理位置的Web服务器。我们都知道,当服务器离用户越远,延迟就越高,CDN就是为了解决这一问题,在多个位置布置服务器,让用户离服务器更近,缩短请求时间。
使用http缓存
缓存针对的是第二次请求,缓存主要是在内存和硬盘。
HTTP缓存主要分为强缓存和协商缓存。
- 强缓存:在过期日期未到时,不需要请求服务器,像饮料的过期日期一样,状态码是灰色的200。
- 协商缓存:浏览器和服务器通过标识进行协商,标识匹配上,就让浏览器直接读缓存,标识匹配不上就返回新资源,状态码为304。
图片优化
- 图片懒加载。原理是先不给图片设置路径,只有当图片进入可视区域内才去设置。
- 图片压缩质量。其实60%~100%质量的图片通常是看不出来区别的。
- 用CSS3里样式、动画替换一些图片效果。如小三角形、小箭头、一些简单的gif动画,因为通常代码的质量是图片质量的几分之一或者几十分之一。
- 一些图标可以使用雪碧图。
其他方式
- 第三方库可以单独提取独立文件,UI框架组件按需加载。
- 减少ES6转换成ES5的冗余代码。3
- 压缩JS与CSS代码。
- 删除无用代码console/alert/注解等。
- 启用GZIP。
运行时
避免大量的DOM元素与DOM深度嵌套
大量的DOM元素与深层镶嵌的DOM层级都会造成HTML解析过慢,我们要尽量避免不必要的DOM元素与深层镶嵌的出现。
可以通过如下API获取页面存在的DOM数量:
document.getElementsByTagName('*').length
减少回流和重绘
回流必将引起重绘,重绘不一定引起回流。
- JS修改样式时,最好不要直接写样式,而是替换class来改变样式。
- JS操作DOM时,可以把DOM元素脱离文档流,可以借助隐藏元素(display:none)或者文档碎片。
webpack相关的性能优化
打包公共代码
使用CommonChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存区中供后续使用,这回带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中抽取出来,而不是每次访问一个页面的时候,都需要去加载一个很大的文件。
webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项 optimization.splitChunks 和 optimization.runtimeChunk ,通过设置 optimization.splitChunks.chunks: “all” 来启动默认的代码分割配置项。
动态导入和按需加载
webpack提供了两种技术通过模块内联函数用来分离代码,优先选择的方式是ECMAScript提案的import()语法,第二种则是使用webpack特定的require.ensure。
删除无用的代码
tree shaking是一个术语,通常用于移除Javascripy上下文中的未引用代码,它依赖于ES2015模块系统中的静态结构特性,例如import和export,js的tree shaking主要通过uglifyjs来完成,css的tree shaking通过purify css来实现。
长缓存优化
- 将hash替换成chunkhash,这样当chunk不变的时候,缓存依然有效
- 使用Name而不是id
每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变。
下面来使用两个插件解决这个问题。第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建。
公共代码内联
使用html-webpack-inline-chunk-plugin插件将manifest.js内联到html文件中。
首屏加载优化
优化思路:
- 将页面内容尽快地展示给用户,减少页面白屏时间。
- 将用户可操作的时间尽量提前,避免用户无法操作的卡顿体验。
减少白屏时间除了我们常说的首屏加载耗时优化,还可以考虑使用一些过渡的动画,让用户感知到页面正在顺利加载,从而避免用户对于白屏页面或是静止页面产生烦躁和困惑。除了技术侧的优化,很多时候产品策略的调整,给用户带来的体验优化效果不低于技术手段优化,因此我们也需要重视。
一般来说,我们需要尽可能地降低首屏需要的代码量和执行耗时,可以通过以下方式进行:
- 对页面的内容进行分片/分屏加载
- 仅加载需要的资源,通过异步或是懒加载的方式加载剩余资源
- 使用骨架屏进行预渲染
- 使用差异化服务,比如读写分离,对于不同场景按需加载所需要的模块
- 使用服务端直出渲染,减少页面二次请求和渲染的耗时
有些时候,我们的页面也需要在客户端进行展示,此时可充分利用客户端的优势:
- 配合客户端进行资源预请求和预加载,比如使用预热 Web 容器
- 配合客户端将资源和数据进行离线,可用于下一次页面的快速渲染
- 使用秒看技术,通过生成预览图片的方式提前将页面内容提供给用户
除了首屏渲染以外,用户在浏览器页面过程中,也会触发页面的二次运算和渲染,此时需要进行渲染过程的优化。
渲染过程优化
我们可以将其理解为首屏加载完成后,用户的操作交互触发的二次渲染。
主要思路是减少用户的操作等待时间,以及通过将页面渲染帧率保持在 60FPS 左右,提升页面交互和渲染的流畅度。包括但不限于以下方案:
- 使用资源预加载,提升空闲时间的资源利用率
- 减少/合并 DOM 操作,减少浏览器渲染过程中的计算耗时
- 使用离屏渲染,在页面不可见的地方提前进行渲染(比如 Canvas 离屏渲染)
- 通过合理使用浏览器 GPU 能力,提升浏览器渲染效率(比如使用 css transform 代替 Canvas 缩放绘制)