开发优化
webpack-bundle-analyzer 这个插件可以查看打包之后的具体某个文件占用的体积
1,配置dllPlugin提升不明显,使用HardSourceWebpackPlugin,能让构建速度提升一大截,webpack5之后的版本内置了HardSourceWebpackPlugin
new HardSourceWebpackPlugin({
cacheDirectory: 'node_modules/.cache/hard-source/[confighash]', //写入缓存的位置。默认值将缓存存储在node_modules
configHash: function(webpackConfig) {
return require('node-object-hash')({sort: false}).hash(webpackConfig);
}, // 为不同的 webpack 配置构建不同的缓存
// configHash: function() {
// return process.env.NODE_ENV + '-' + process.env.BABEL_ENV;
//},
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock'],
},// 当加载程序、插件、其他构建时脚本或其他动态依赖项发生更改时,需要替换缓存以确保输出正确。用于确定这一点。如果哈希值与以前的版本不同,则将使用新缓存
info: {
mode: 'none', // 设置信息的其他默认值。NODE_ENV===“测试”时默认为“测试”。
level: 'debug', // 要向下报告的日志消息的级别。当模式为“无”时,默认为“调试”。当模式为“测试”时,默认为“警告”。
例如,“调试”报告所有消息,而“警告”报告警告和错误级别消息。
},
cachePrune: {
maxAge: 2 * 24 * 60 * 60 * 1000, // 超过毫秒的缓存将被视为自动删除
sizeThreshold: 50 * 1024 * 1024 // 要删除缓存,所有这些缓存的总和必须超过此阈值。
},
}),
2,使用clean-webpack-plugin,可以删除上次打包出来的dist文件夹里面的文件
3, 使用 HappyPack。运行在 Node.js 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要处理的任务需要一件件挨着做,不能多个事情一起做。
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: 5 });
module.exports = {
module: {
rules: [
{
test: /\.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: ['happypack/loader?id=babel'],
// 排除 node_modules 目录下的文件,node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换
exclude: path.resolve(__dirname, 'node_modules'),
},
{
// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['happypack/loader?id=css'],
}),
},
]
},
plugins: [
new HappyPack({
// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
id: 'babel',
// 如何处理 .js 文件,用法和 Loader 配置中一样
loaders: ['babel-loader?cacheDirectory'],
// 使用共享进程池中的子进程去处理任务
threadPool: happyThreadPool,
}),
new HappyPack({
id: 'css',
// 如何处理 .css 文件,用法和 Loader 配置中一样
loaders: ['css-loader'],
// 使用共享进程池中的子进程去处理任务
threadPool: happyThreadPool,
}),
new ExtractTextPlugin({
filename: `[name].css`,
}),
],
};
4,优化 Loader寻找文件的范围
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
loader: 'babel-loader',
// 只在 src 文件夹下查找
include: [resolve('src')],
// 不会去查找的路径
exclude: /node_modules/
}
]
}
}
5,将 Babel 编译过的文件缓存起来
loader: 'babel-loader?cacheDirectory=true'
打包优化
1,js,css,html三个插件压缩代码,能减少打包出来的代码体积。还可以使用 gzip 压缩也能明显减少打包出来的代码体积。使用loader来优化图片的体积
JavaScript:UglifyJsPlugin
CSS :optimize-css-assets-webpack-plugin
HTML:HtmlWebpackPlugin
还可以使用 gzip 压缩。可以通过向 HTTP 请求头中的 Accept-Encoding 头添加 gzip 标识来开启这一功能。当然,服务器也得支持这一功能。
webpack是使用compression-webpack-plugin 插件开启
(UglifyJsPlugin
使用单线程压缩js代码,比较慢,所以可以在开发环境将其关闭,生产环境部署时再把它打开,使用ParallelUglifyPlugin
可以开启多线程压缩,内部还是使用UglifyJsPlugin压缩。)
(HtmlWebpackPlugin,minify参数配置html压缩的配置)
(mini-css-extract-plugin
,用在生产环境,将 CSS 提取为独立的文件的插件,对每个包含 css 的 js 文件都会创建一个 CSS 文件)
使用url-loader把小于十兆的图片转换成base64码(减少体积,也能减少http请求)
使用image-webpack-loader来压缩图片的质量
{
loader: 'url-loader',
options: {
limit: 10000, /* 图片大小小于1000字节限制时会自动转成 base64 码引用*/
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
/*对图片进行压缩*/
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
}
2,momentjs,项目中全局引入后比较大,打包之后还有200多kb,开启gzip之后还有60多k,最好使用替代的轻量级的dayjs代替,开启gzip之后只有2kb,能有效减少打包之后的体积。类似的方式是可以按需引入elementui,vant,echarts,loadsh等第三方插件。
(momentjs支持多国语言,可以使用webpack内置的IgnorePlugin
插件,排除掉/./local/里面的文件,不过排除之后就需要重新引入中文才能显示中文日期)
3,在生产环境打包不生成map文件productionSourceMap:false
4,将一些第三方组件通过cdn的方式引入,从而减小打包的文件数量,可以缩短打包时间和减小最后的文件体积。下面我们将vue、vuex、vue-router、element-ui使用cdn引入。
使用方式如下:
在public/index.html中引入
<body>
<div id="app"></div>
<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
</body>
// 在vue.config.js中
configureWebpack:{
externals: {
'element-ui': 'ELEMENT',
'vue': 'Vue',
'vue-router': 'VueRouter',
}
}
5,使用字体图标而不是图片图标,字体图标的大小还是比图片小
6,优化splitChunks,webpack默认的分包配置
防止模块被重复打包,拆分过大的js文件,合并零散的js文件。最终的目的就是减少请求资源的大小和请求次数。因这两者是互相矛盾的,要以项目实际的情况去使用SplitChunks插件
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',//默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效
minSize: 30000,//合并前模块文件的体积
minChunks: 1,//最少被引用次数
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',//自动命名连接符
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
minChunks:1,//敲黑板
priority: -10//优先级更高
},
default: {
test: /[\\/]src[\\/]js[\\/]/
minChunks: 2,//一般为非第三方公共模块
priority: -20,
reuseExistingChunk: true
},
// SplitChunks操作之非入口文件
mixins: {
name: 'mixins',
test: /[\\/]mixins[\\/]/,
priority: 0,
},
},
runtimeChunk:{
name:'manifest'
}
}
}
}
7,使用svg-sprite-loader
把svg文件转换成雪碧图
8,purifycss-webpack
,有时候我们 css 写得多了或者重复了,这就造成了多余的代码,我们希望在生产环境进行去除。
9,optimize-css-assets-webpack-plugin
我们希望减小 css 打包后的体积,可以用到 optimize-css-assets-webpack-plugin。
代码优化
1,使用雪碧图(如淘宝首页头部的多个小图片可以合并成一张图片)能减少请求,使用字体图标也可以减少请求,小于十兆的图片转换成base64码也可以减少http请求,能有效优化首页的加载时间。还有cdn引入第三方的插件如element,echartsjs等。
2,懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种非常好的优化网页性能的方式。img的data-src属性
当可视区域没有滚到资源需要加载的地方时候,可视区域外的资源就不会加载。
可以减少服务器负载,常适用于图片很多,页面较长的业务场景中 v-lazy,lazyjs
3,服务端渲染SSR,渲染过程在服务器端完成,最终的渲染结果 HTML 页面通过 HTTP 协议发送给客户端,首页加载太慢可以先在服务端渲染好在返回页面。
服务端渲染(SSR)除了SEO还有很多时候用作首屏优化,加快首屏速度,提高用户体验。但是对服务器有要求,网络传输数据量大,占用部分服务器运算资源。
Vue的Nuxt.js和React的next.js都是服务端渲染的方法
4,Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
5,页面运行时,要尽量避免回流,回流的开销比重绘要大。
如:
避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的,DOM 操作不会引发回流和重绘。
避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
6,使用事件委托
7,防抖和节流
// 防抖函数
function debounce(func, wait) {
let timeout;
return function() {
let context = this; // 指向全局
let args = arguments;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
func.apply(context, args); // context.func(args)
}, wait);
};
}
// 使用
window.onscroll = debounce(function() {
console.log('debounce', 'input频繁输入');
}, 1000);
// 节流函数
function throttle(fn, delay) {
let flag = true;
return function() {
if (!flag) {
return;
}
const that = this;
flag = false;
const args = [...arguments];
setTimeOut(() => {
fn.apply(that, args);
flag = true;
})
};
}
// 使用
var throtteScroll = throttle(function() {
console.log('throtte', '滚动条');
}, 1000);
window.onscroll = throtteScroll;
8,合理使用缓存
为HTML指定Cache-Control或Expires:
为HTML内容设置Cache-Control 或 Expires可以将HTML内容缓存起来,避免频繁向服务器端发送请求,在页面中的Cache-Control 或 Expires头部有效时,浏览器将直接从缓存中读取内容,不再向服务器端发送请求
<meta http-equiv="Cache-Control" content="max-age=7200">
<meta http-equiv="Expires" content="Mon,20Jul201623:00:00GMT">
合理设置Etag和Last-Modified
合理设置Etag 和 Last-Modified使用浏览器缓存,对于未修改的文件,静态资源服务器会向浏览器端返回304,让浏览器从缓存中读取文件,减少Web资源下载的带宽消耗并降低服务器负载
<meta http-equiv="last-modified" content="Sun,05 Nov 2017 13:45:57 GMT">
使用可缓存的ajax请求
$.ajax({
url: url,
type: 'get',
cache: true, //推荐使用缓存
data: {},
success() {},
error() {}
});
9,使用http2.0
需要配置一个支持h2的web服务器,并下载安装一张TLS证书,让浏览器与服务器通过h2链接
http2.0优势:
采用二进制格式传输数据, 1.1是文本格式
对消息头采用Hpack进行压缩传输,能够节省消息头占用的网络流量,1.1每次请求,都会携带大量冗余的头信息,浪费了很多宽带资源
异步连接多路复用
Server Push,服务器端能够更快的把资源推送到客户端
保持与HTTP 1.1语义的向后兼容性也是该版本的一个关键
10,首页白屏可以做骨架屏优化
11,避免在页面的主体布局中使用table,表要在其中的内容完全下载之后才会显示出来,显示的速度比DIV+CSS布局
12,把CSS放在页面头部把 JavaScript代码放在页面底部(这样避免阻塞页面渲染而使页面出现长时间的空白)
13,当判断条件数量越来越多时,越倾向于使用 switch 而不是 if-else
14,优化动画,使用requestAnimationFrameAPI,当你准备更新动画时你应该调用此方法
把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量
requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销
const element = document.getElementById('some-element-you-want-to-animate');
let start;
function step(timestamp) {
if (start === undefined)
start = timestamp;
const elapsed = timestamp - start;
//这里使用`Math.min()`确保元素刚好停在200px的位置。
element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';
if (elapsed < 2000) { // 在两秒后停止动画
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);