我们常将网站性能优化措施归结为四大方面:
1、css优化;
2、js优化;
3、合理利用缓存,提升网络传输速率;
4、减小请求资源体积;
5、其它
1 css优化
1.1使用雪碧图,减少http请求,推荐一个自动化生成雪碧图的工具:https://www.toptal.com/developers/css/sprite-generator
1.2 使用字体图标,推荐使用阿里巴巴矢量图标库:www.iconfont.cn/。往html里面插入字符和样式,和图片相比,占用内存更小。
1.3 多个css合并,减少http请求,提取公共样式,减少代码量。
1.4 选择器优化嵌套,尽量避免层级过深
2 js优化
1.1 javascript会阻塞dom的解析。当解析过程中遇到<script>标签的时候,便会停止解析过程,转而去处理脚本,如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后执行。在处理完脚本之后,浏览器才继续解析HTML文档,所以把js放底部。
1.2 函数节流、防抖详情见https://editor.csdn.net/md/?articleId=84943516
1.3 长列表做翻页处理,图片懒加载(data-src)
1.4 批量绑定事件,使用事件委托绑定父节点实现
1.5 在dom操作时尽量addClass,而不是通过style操作样式,以减少重排
3 浏览器缓存/HTTP缓存
区分一下,web存储是cookie、localStorage、sessionStorage之类。
浏览器在向服务器发起请求前,会先查询本地是否有相同的文件,如果有,就会直接拉取本地缓存,缓存策略如下图:
cache-control是http2的字段,max-age=到过期的秒数,expires是http1.0的字段,返回一个过期日期,没过期不会再次像服务器发送请求,属于强缓存,强缓存大于协商缓存,这两个字段都在response headers中。
no-cache表示是强制进行协商缓存。
no-store是表示禁止任何缓存策略。
200 ok表示无缓存,重新拉取文件。
200(from memory cache/frome disk cache)表示强缓存。
浏览器默认的缓存是放在内存中的,关闭浏览器就会清除,而存在硬盘里的缓存才能被长期保留下去,打开network,可以看到两种不同的状态:from memory cache 和 frome disk cache,前者来自内存,后者来自硬盘。
协商缓存就是强制缓存失效后,浏览器携带缓存标识
向服务器发起请求, 由服务器根据缓存标识决定是否使用缓存的过程
补充:浏览器第一次向服务器发请求,服务器返回200和last-modified(response headers服务器文件最后更新时间)。F5刷新页面发送验证请求
,请求头会带上if-Modified-Since(request headers),如果和响应头返回的last-modified的值相同,则返回304,从浏览器拉取缓存,否则发送数据请求
,返回200。
同理ETag和If-None-Match对比,ETag是一串哈希值,可以理解成版本号,如果1s内频繁修改文件,那么更适合用ETag来协商缓存。
4 资源打包压缩
减少请求资源体积,通常都需要打包工具(例如webpack)的协助,可以从以下几点来对webpack进行优化。
4.1 配置热更新插件HotModuleReplacementPlugin,热更新不刷新整个网页,只编译发生变化的模块,新模块替换老模块
。
4.2 压缩JS用UglifyJs(去掉注释、空格,缩短变量名),再用ExtractTextWebpackPlugin从js文件分离出css文件。
4.3 设置多个入口文件,提取公共代码,以利用缓存(CommonsChunkPlugin )。
通常的入口文件有pollyfills.ts、vendor.ts、main.ts。
entry: {
//填平浏览器的坑,包括es6的一些包
'polyfills': './src/polyfills.ts',
//第三供应商,包括angular,bootstrap,lodash
'vendor': './src/vendor.ts',
//应用代码
'app': './src/main.ts'
}
-----------------------------------------------------------------------------------------------------------------
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
//...
plugins:[
new CommonsChunkPlugin({
chunks:['polyfills','vendor','main'], //从哪些chunk中提取
name:'common', // 提取出的公共部分形成一个新的chunk
})
]
4.4 区分运行环境,因为第三方库中有大量根据开发环境判断的if else代码,所以构建也需要根据不同环境输出不同的代码。这里用definePlugin插件来配置。
const DefinePlugin = require('webpack/lib/DefinePlugin');
//...
plugins:[
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
-------------------------------------------------------------------------------------------------------------------
if(process.env.NODE_ENV === 'production'){
console.log('你在生产环境')
doSth();
}else{
console.log('你在开发环境')
doSthElse();
}
4.5 require.ensure()来实现按需加载
mapBtn.click(function() {
require.ensure([], function() {
var baidumap = require('./baidumap.js') //baidumap.js放在我们当前目录下
})
})
5.其它
1.必要功能加loading,长列表分页
没有优化加载时间,但是提升了用户体验。
2.减少http请求和冗余数据
前端在初始化编辑窗口时分别通过了4个接口请求了不同资源。通过和后端协商将4个接口改为一个接口,同时对返回的数据进行删减,只返回前端使用到的数据。
3.提取公共组件,路由懒加载(先加载模块,再加载组件)、组件懒加载
4.按需引入第三方库(elemet-ui)
5.keep-alive缓存组件
6.v-for遍历的时候避免使用v-if
7.CDN引入资源库
8.虚拟列表,假设后台返回1000条数据,实现虚拟列表就是处理滚动条滚动后的可见区域的变更,其他数据隐藏
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(window.clientHeight / this.itemHeight); // 取得可见区域的列表项数量
const start = Math.floor(scrollTop / this.itemHeight); // 取得可见区域的起始数据索引
const end = start + visibleCount; // 取得可见区域的结束数据索引
this.visibleData = this.data.slice(start, end); // 计算出可见区域对应的数据。
this.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`; // 把可见区域的 top 设置为起始元素在整个列表中的位置(使用 transform 是为了更好的性能)
}
9.服务器渲染ssr(首页)
由服务端node请求首屏数据,而不是客户端请求首屏数据,这是“快”的一个主要原因。服务端在内网(路由器直接连接)进行请求,数据响应速度快。客户端在不同网络环境进行数据请求,且外网(经过路由器和交换机再到外网)http请求开销大,导致时间差。
参考文章:https://segmentfault.com/a/1190000015883378
https://blog.csdn.net/DINGYANG0315/article/details/80518720