- 目的
- 1.css优化
- 2.图片优化
- 3.CDN加载js
- 4.Tree-shaking树摇 && Scope-Hoisting作用域提升
- 5.DllPlugin动态链接库 (一般用在开发阶段)
- DllPlugin
- DllReferencePlugin
- https://www.npmjs.com/package/add-asset-html-webpack-plugin">add-asset-html-webpack-plugin - npmhttps://www.npmjs.com/package/add-asset-html-webpack-plugin
- 6.动态加载 import语法
- 7.打包分析工具
- https://www.npmjs.com/package/webpack-bundle-analyzer">webpack-bundle-analyzer - npmhttps://www.npmjs.com/package/webpack-bundle-analyzer
- 多入口 多entry多output
- 8.splitChunks 避免重复依赖
- 9.费时更新
- https://www.npmjs.com/package/speed-measure-webpack-plugin">speed-measure-webpack-plugin - npmhttps://www.npmjs.com/package/speed-measure-webpack-plugin
- 10.noParse
- 11.resolve
- 12.include/exclude
目的
打包大小
打包速度
模块拆分
1.css优化
删除无用的css样式
使用purge-css + mini-css
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //提取css为link标签引入
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html提取插件 给模板
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const vueLoaderPlugin = require("vue-loader/lib/plugin");
const glob = require("glob");
//主要的作用时删除无意义的css 只能配合 mini-css-extract-plugin使用
const PurgeCssWebpackPlugin = require("purgecss-webpack-plugin");
{
test: /\.css$/,
use: [
//style生产环境下转为link标签引入
mode == "production" ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
],
},
2.图片优化
图片压缩插件
image-webpack-loader
{
//图片 降低分辨率 image-webpack-loader 从右向左 先优化再拷贝
//在使用file-loader之前对图片进行压缩
test: /\.(png|jpe?g|gif)$/,
use: [
"file-loader",
mode == "production1" && {
loader: "image-webpack-loader",
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
// optipng.enabled: false will disable optipng
// optipng: {
// enabled: false,
// },
// pngquant: {
// quality: [0.65, 0.9],
// speed: 4,
// },
// gifsicle: {
// interlaced: false,
// },
// // the webp option will enable WEBP
// webp: {
// quality: 75,
// },
},
},
].filter(Boolean),
},
3.CDN加载js
externals(用得很多)
外部扩展(Externals) | webpack 中文文档https://webpack.docschina.org/configuration/externals/
防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
例如,从 CDN 引入 jQuery,而不是把它打包:
外部扩展(Externals) | webpack 中文文档https://webpack.docschina.org/configuration/externals/
比如jquery 不打包而是html中直接引入
script标签里面引入的是全局的
externals:{
"jquery":"$" //不去打包代码中的jquery $是变量名 代表是从外部引入的
},
请查看上面的例子。属性名称是 jquery,表示应该排除 import $ from ‘jquery’ 中的 jquery 模块。为了替换这个模块,jQuery 的值将被用来检索一个全局的 jQuery 变量。换句话说,当设置为一个字符串时,它将被视为全局的(定义在上面和下面)。
4.Tree-shaking树摇 && Scope-Hoisting作用域提升
tree-shaking默认值支持es6静态导入语法 export import
只在生产环境下使用
optimization
开发环境下标出未使用的js
在development下
optimization: {
usedExports: true, //使用了哪个模块和我说一下
},
unused harmony export
/***/ "./src/calc.js":
/*!*********************!*\
!*** ./src/calc.js ***!
\*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"minus\": () => (/* binding */ minus)\n/* harmony export */ });\n/* unused harmony export add */\nvar add = function add(a, b) {\n return a + b + \"sum\";\n};\nvar minus = function minus(a, b) {\n return a - b + \"minus\";\n};\n\n//# sourceURL=webpack://learn-webpack-optimize/./src/calc.js?");
生产环境中剔除副作用的代码
如果引入的变量没有被使用 就应该被删除
sideEffects:true
结论
我们学到为了利用 tree shaking 的优势, 你必须…
- 使用 ES2015 模块语法(即 import 和 export)。
- 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
- 在项目的 package.json 文件中,添加 “sideEffects” 属性。
- 使用 mode 为 “production” 的配置项以启用更多优化项,包括压缩代码与 tree shaking。
你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码) 和 library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。
如果你对优化输出很感兴趣,请进入到下个指南,来了解 生产环境 构建的详细细节。
Scope-Hoisting作用域提升
每个模块都是一个函数
会减少作用域 会去做计算
会内部优化
计算好一个值 直接替换
5.DllPlugin动态链接库 (一般用在开发阶段)
DllPlugin
webpack.dll.js
const path = require("path")
const DllPlugin = require("webpack").DllPlugin
module.exports = {
mode:"development",
entry:["vue"],
output:{
library:"vue",//打包后接收自执行函数的名字叫做calc
// libraryTarget:"commonjs2",//umd this var commonjs
filename:"vue.dll.js",
path:path.resolve(__dirname,"dll")
},
// 当前这个dll.js并没有在页面中引入
plugins:[
new DllPlugin({
name:"react",
path:path.resolve(__dirname,"dll/manifest.json")
})
]
}
// 目前是为了将calc 打包成 node 可以使用的模块 commonjs
/******
* // 我本地使用了import React语法
// 需要先去manifest.json查找 找到后会加载
*
*
*
*/
DllReferencePlugin
add-asset-html-webpack-plugin - npmhttps://www.npmjs.com/package/add-asset-html-webpack-plugin
const DLLReferencePlugin = require("webpack").DllReferencePlugin;
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");
new DLLReferencePlugin({
//打包时候会配置clean-webpack-plugin 所以不放在dist里面, 而应该放到dll下
manifest: path.resolve(__dirname, "dll/manifest.json"),
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, "dll/*.dll.js"),
}),
大大提升构建速度
先将react + react-plugin打包好
manifest.json 载货单文件
怎么打包第三方库
就先抽离好 打好包 以后就不用打包了
6.动态加载 import语法
原理就是jsonp 动态导入
import语法可以实现代码分割
默认会打包成单文件
使用jsonp去加载
let button = document.createElement("button");
button.innerHTNL = "点我";
button.addEventListener("click", () => {
// 动态导入 import语法 类比路由的懒加载
//webpack默认会把这样的语法单独打包成一个文件
//点击的时候会使用jsonp动态加载这个文件
import(/* webpackChunkName:'calc' */ "./calc").then((data) => {
console.log(data);
});
console.log(add(1, 2));
});
document.body.appendChild(button);
7.打包分析工具
webpack-bundle-analyzer - npmhttps://www.npmjs.com/package/webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
devMode && new BundleAnalyzerPlugin()
].filter(Boolean)
}
88端口
当多入口都加载了某个模块时,
需要抽离第三方模块
1.不要和业务逻辑放在一起
2.增加缓存 304
多入口 多entry多output
8.splitChunks 避免重复依赖
SplitChunksPlugin | webpack 中文文档
https://webpack.docschina.org/plugins/split-chunks-plugin/
下面这个配置对象代表 SplitChunksPlugin 的默认行为。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', //异步
minSize: 20000, //20k
minRemainingSize: 0,
minChunks: 1,//最少一次
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
Wa
splitChunks.chunks
string = ‘async’ function (chunk)
这表明将选择哪些 chunk 进行优化。当提供一个字符串,有效值为 all,async 和 initial。设置为 all 可能特别强大,因为这意味着 chunk 可以在异步和非异步 chunk 之间共享。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
},
},
};
9.费时更新
speed-measure-webpack-plugin - npmhttps://www.npmjs.com/package/speed-measure-webpack-plugin
yarn add -D speed-measure-webpack-plugin
包裹
const webpackConfig = {
plugins: [new MyPlugin(), new MyOtherPlugin()],
};
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
plugins: [new MyPlugin(), new MyOtherPlugin()],
});
10.noParse
11.resolve
12.include/exclude
resolve
object
配置模块如何解析。例如,当在 ES2015 中调用 import ‘lodash’,resolve 选项能够对 webpack 查找 ‘lodash’ 的方式去做修改(查看模块)。
webpack.config.js
module.exports = { //… resolve: { // configuration options }, };
resolve.alias
object
创建 import 或 require 的别名,来确保模块引入变得更简单。例如,一些位于 src/ 文件夹下的常用模块:
webpack.config.js
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/'),
},
},
};
}, };
现在,替换“在导入时使用相对路径”这种方式,就像这样:
import Utility from '../../utilities/utility';
你可以这样使用别名:
import Utility from 'Utilities/utility';
也可以在给定对象的键后的末尾添加 $,以表示精准匹配:
webpack.config.js
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
xyz$: path.resolve(__dirname, 'path/to/file.js'),
},
},
};
这将产生以下结果:
import Test1 from 'xyz'; // 精确匹配,所以 path/to/file.js 被解析和导入
import Test2 from 'xyz/file.js'; // 非精确匹配,触发普通解析
alias: | import ‘xyz’ | import ‘xyz/file.js’ |
---|---|---|
{} | /abc/node_modules/xyz/index.js | /abc/node_modules/xyz/file.js |
{ xyz: ‘/abc/path/to/file.js’ } | /abc/path/to/file.js | error |
{ xyz$: ‘/abc/path/to/file.js’ } | /abc/path/to/file.js | /abc/node_modules/xyz/file.js |
{ xyz: ‘./dir/file.js’ } | /abc/dir/file.js | error |
{ xyz$: ‘./dir/file.js’ } | /abc/dir/file.js | /abc/node_modules/xyz/file.js |
{ xyz: ‘/some/dir’ } | /some/dir/index.js | /some/dir/file.js |
{ xyz$: ‘/some/dir’ } | /some/dir/index.js | /abc/node_modules/xyz/file.js |
{ xyz: ‘./dir’ } | /abc/dir/index.js | /abc/dir/file.js |
{ xyz: ‘modu’ } | /abc/node_modules/modu/index.js | /abc/node_modules/modu/file.js |
{ xyz$: ‘modu’ } | /abc/node_modules/modu/index.js | /abc/node_modules/xyz/file.js |
{ xyz: ‘modu/some/file.js’ } | /abc/node_modules/modu/some/file.js | error |
{ xyz: ‘modu/dir’ } | /abc/node_modules/modu/dir/index.js | /abc/node_modules/modu/dir/file.js |
{ xyz$: ‘modu/dir’ } | /abc/node_modules/modu/dir/index.js | /abc/node_modules/xyz/file.js |