前言
前端作为用户交互的第一门户,前端的页面性能与用户的体验息息相关。通常体验越好,用户留存率会更高。那么我们有哪些手段来优化我们的页面呢?
css 优化
避免使用行内样式,尽量使用类选择器,其次才是 id 选择器
大量使用行内样式,导致我们的文档体积变大,影响加载速度。如果多个标签属性相同,会导致重复声明 css 样式,且打包体积更大。同时行内样式无法使 css 样式和 html 结构剥离。
在 css 样式比较多的情况,我们尽量使用外链 css,缩小打包体积
合并脚本和样式表,同时优先使用外链 css 文件引用,即 link,而非导入式 @import
。
导入式引用的问题:@import
在浏览网页时是先将 html 结构呈现出来,再把外部的 css 文件加载到网页中,对用户首屏体验感影响很大。而使用 link 引入 css 时,如果我们把 css 样式放在文档头部,客户端浏览你的网页时先将外部的 css 文件加载到网页,防止首次加载样式丢失。
引入资源利用 link 标签 rel 属性提供的一定预取能力
dns-prefetch 预解析。预先解析 href 指定的 dns 域名
preconnect 预连接。不仅预先解析 dns 还会把服务器握手完成
prefetch 预获取。资源被提前加载不执行和解析只是被缓存下来
prerender 预渲染。资源被提前加载并执行
dns-prefetch 和 preconnect 的存在可以让浏览器在解析文档的同时可以预先进行 DNS 解析或者预先建立一个链接,接下来加载 CDN 的其他资源时就可以更加快速
prefetch 和 prerender 可以告诉浏览器用户下个跳转的页面的地址,浏览器可以预加载这些页面资源到缓存或者预渲染好,前端用户体验优化
如何探测一个网页是否正被 prerender?答:打开 Chrome Task Manager 可以查看
注意脚本、静态资源的引入顺序,避免使用 iframe
css 样式放在文档头部,防止首次加载样式丢失。
JS 脚本、iframe 放在尾部,防止 <script>
、<iframe>
标签在加载时,阻塞页面加载
避免使用或少用 iframe,iframe 加载会阻塞样式、dom 树的加载,如果使用 iframe,最好放在页面底部或者延迟加载插入
同时,在使用 iframe 时,会阻塞 onload 事件,影响页面加载。因此要将 onload 事件放在 onloadeddata
中。
局部 css,尽量使用css module
scoped 和 css module 的优劣
scoped:scoped 通过 webpack loaderd 配置 post-css, 转化后会给标签添加一个属性 data-a-hashString
,打包体积比 css module 大,会影响首屏速度
css module: 无需产出额外的属性。使用 css module ,在 webpack 的 loader 配置中添加 modules 规则,即可在类名后自动生成 hash
后缀
modules:{
localIdentName: "[local]_[hash:base64:5]"
}
其他手段
图片使用懒加载,列表节流、交互防抖
减少重绘重排,避免多次添加样式,添加重复样式
尽量使用 css3 动画,而不是用 JS 能力实现动画
元素批量添加事件时,尽量使用事件代理
图标不使用图片,使用体积更小的 iconfont、svg 等
减少打包体积
路由懒加载,依赖、组件按需引用,减少全局引用
component: resolve => (require(["@/components/HelloWorld"], resolve))
Or
const HelloWorld = ()=> import("@/components/HelloWorld")
压缩代码:减少资源大小可加快网页的显示速度,对代码进行压缩,并在服务器开启 gzip
vue
productionGzip改为true,开启Gzip压缩
nginx
gzip on;
gzip_types text/plain application/x-javascript application/javascript text/css application/xml
text/javascript application/x-httpd-php image/jpeg image/gif image/png;
webpack
extract-text-webpack-plugin 用于将 css 从主应用程序中分离
optimize-css-assets-webpack-plugin 压缩提取出的 css,解决 extract-text-webpack-plugin 分离 CSS重复问题
commonsChunkPlugin 将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用
uglifyjs-webpack-plugin:webpack 默认使用它压缩js代码,需要说明的是可以在上面加上去除 `console.log` 的选项,也可以有效减少包体积
使用雪碧图
使用 map 标签来显示图片
网络请求
浏览器对单一域名有请求次数限制。同一域名的合并请求,减少 http 请求次数
http 2.0 分块编码传输
建立一个持久连接,将资源以分块编码方式传输,实现 chunk 并行化,response body 分块传输。前端收到 response body 后对相应的容器渲染,提高我们的 first paint
的时间
Response Headers
Connection: keep-alive
Transfer-Encoding: chunked
异步化预请求
预测用户操作,比如在用户 hover
、click
、focus
等操作时,进行一个预测请求,并对预测结果缓存
引入 cdn,将静态资源分离
静态资源单独集群部署的好处
1、浏览器对单个域名有 http 并发请求上限,静态资源单独使用域名,在浏览器端可以增加并发连接数
2、静态资源单独域名,避免携带无用的业务 cookie,缩小请求体积,减小用户的带宽浪费,提高首屏速度
3、减小网站的流量压力,静态资源请求可能是网站的几十倍
4、接入 cdn,根据 ip 就近访问,提高加载速度
5、静态资源根据地区部署在不同地区的集群
本地缓存资源
通过添加 expires 头,设置 max-age,尽量长缓存,可以提高页面之后的加载速度
容器 + 内容分离
只需要请求我们内容渲染到容器中,而不是整个文档
预渲染
1、打包预渲染,打包时就把首页的部分静态的 html 加载在 index.html 中来提高首页加载速度
2、spa 单页面应用可以通过 webpack 中 plugin 配置,借助 prerender-spa-plugin
给指定路由生成静态页面,提高页面渲染的速度,也利于 seo
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/', '/home'],
renderer: new Renderer({
headless: false,
renderAfterDocumentEvent: 'render-event'
})
})
3、服务端渲染后返回静态 html,可以使用 php 或者 node 渲染和 JS 脚本能力。前端分别请求静态模板、静态资源、脚本。再由前端渲染静态 html 模板,最后将脚本和静态资源结合