Webpack配置
# 原始案例
// index.js
var dom = document.getElementById("root");
var header = document.createElement("div");
header.innerText = "header";
dom.append(header);
var slider = document.createElement("div");
slider.innerText = "slider";
dom.append(slider);
var content = document.createElement("div");
content.innerText = "content";
dom.append("content");
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>·
<p>这是我们的网页内容</p>
<div id="root"></div>
<script src="./index.js"></script>
</body>
2
3
4
5
# 模块化改造
// header.js
var header = document.createElement("div");
header.innerText = "header";
dom.append(header);
// slider.js
var slider = document.createElement("div");
slider.innerText = "slider";
dom.append(slider);
// content.js
var content = document.createElement("div");
content.innerText = "content";
dom.append("content");
// 入口文件 index.js
var dom = document.getElementById("root");
new Header();
new Silder();
new Content();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<p>这是我们的网页内容</p>
<div id="root"></div>
<script src="./javascript/header.js"></script>
<script src="./javascript/silder.js"></script>
<script src="./javascript/content.js"></script>
<script src="./index.js"></script>
</body>
2
3
4
5
6
7
8
http 请求数增加,而且不利于维护,思考:如果使用 import呢?
// 入口文件 index.js
import Header from "/javascript/header.js";
import Silder from "/javascript/silder.js";
import Content from "/javascript/content.js";
var dom = document.getElementById("root");
new Header();
new Silder();
new Content();
2
3
4
5
6
7
8
9
10
<body>
<p>这是我们的网页内容</p>
<div id="root"></div>
<script src="./index.js"></script>
</body>
2
3
4
5
# Webpack核心
最早的时候,webpack是一个js模块打包工具,随着发展,现在webpack可以打包其他资源文件
# 1. entry入口
entry: "./src/index.js";
// 等价
entry: {
main: "./src/index.js";
}
2
3
4
5
# 2. output出口
entry:{
main:"./src/index.js",
sub:"./src/sub.js"
},
output: {
filename: "[name].js", //可以配置占位符名字
path: path.resolve(__dirname, "dist"),
publicPath:"http://www.baidu.com" //CDN 资源链接域名注入
},
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://www.baidu.com/main.js"></script>
<script src="http://www.baidu.com/sub.js"></script>
</head>
<body>
<div id="root">HTML模板</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
# 3.使用 Loader 打包静态资源
- loader运行在打包文件之前,只专注于转化文件(transform)这一个领域
- loader 有先后顺序 ,从下到上,从右到左
# 3.1. 图片资源
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,会把图片打包成 base6 输入到 bundle.js 里面,最终打包后带来的好处是:可以减少了 http 请求,但是 bundle.js 会变大, 所以最佳使用方式, 针对小文件图片进行 base64 打包
module:{
rules:[
{
test:/\.(jpg|png|gif)$/,
use:{
loader:"file-loader",
options:{
name:"[name]_[hash].[ext]", //配置占位符
outputPath: 'images/',
limit:2048 //如果图片大于2m,会打包到dist目录下, 小于2m直接打包进bundle.js
}
}
}
]
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3.2. CSS样式
# 3.2.1 css-loader
会帮我们分析出几个 css 之间的文件引用关系,最终把 css 代码合并成一个代码片段
如果在JS中导入了css,那么就需要使用 css-loader 来识别这个模块,通过特定的语法规则进行转换内容最后导出,css-loader会处理 import / require() @import / url 引入的内容。
# 3.2.2 style-loader
style-loader 是通过一个JS脚本创建一个style标签,会把css代码片段插入到 head 的 style 内
{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader']
}
//------------从下到上,从右到左---------------
use: [
{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
},
{
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
},
{
loader: "sass-loader" // 将 Sass 编译成 CSS
}
];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.2.3 添加 css 前缀(autoprefixer)
PostCSS插件 获取一个 CSS 文件,并提供一个 API 来分析和修改它的规则(通过将它们转换为一个抽象语法树)。这个 API 可以被插件用来做很多有用的事情,比如自动查找错误,插入供应商的前缀
autoprefixer 是PostCSS 插件是最流行的功能插件,用来添加供应商前缀,使用来自 Can I Use 的数据。
//postcss.config.js
module.exports = {
plugins: [
require("autoprefixer")({
//配置浏览器厂商,否则前缀无法添加
browsers: ["Android >= 4.0", "iOS >= 7"]
})
]
};
2
3
4
5
6
7
8
9
# 3.2.4 模块化css解决样式冲突
往往修改一个文件会影响另外的样式,所以我们需要模块化的 css
{
loader: 'css-loader',
options: {
importLoaders:2,
modules:true //开启css模块化
}
},
2
3
4
5
6
7
/* index.scss */
body {
.avatar{
width: 150px;
height:150px;
transform: translate(100px,100px);
}
}
2
3
4
5
6
7
8
// index.js
import style from "./index.scss";
var img = new Image();
img.src = avatar;
img.classList.add(style.avatar);
document.getElementById("root").append(img);
2
3
4
5
6
7
# 3.2.5 字体打包
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
2
3
4
5
6
# 4.Plugins 让打包更便捷
plugin 可以在 webpack 运行的某一时刻为你做某些事情
plugin是为了扩展webpack的功能,plugin 是作用于webpack本身上的,plugins在整个编译周期都起作用。而且plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务
# 4.1.htmlwebpackplugin
新增 template 模板文件,写好 root 节点之后,webpack 会把 src/index.html 打包进 dist/index.html 格式中
plugins: [
new HtmlWebpackPlugin({
template: "./index.html"
})
];
2
3
4
5
# 4.2.clean-webpack-plugin
用于删除/清理生成文件夹的网页包插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 引入
// .......
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new CleanWebpackPlugin() // 使用
];
2
3
4
5
6
7
8
# 模式
开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置
简单说 Sourcemap 构建了处理前以及处理后的代码之间的一座桥梁,方便定位生产环境中出现 bug 的位置
- 开发环境会生成 sourceMap 方便我们调试,但是线上 sourceMap 会生成.map 文件保存
- 开发环境代码不压缩,生成环境代码会压缩
- 开发环境,配合 webpack-dev-server和HMR
npm install webpack-merge --save-dev
"dev": "webpack-dev-server --config webpack.dev.js",
"prod": "webpack --config webpack.prod.js",
2
//webpack.dev.js----
const webpack = require("webpack");
const commonConfig = require("./webpack.common");
const merge = require("webpack-merge");
const devConfig = {
mode: "development", //开发环境进行打包,代码不会压缩
devtool: "cheap-module-eval-source-map", //使用scurceMap
plugins: [new webpack.HotModuleReplacementPlugin()],
// package.json中配置dev命令
devServer: {
contentBase: "./dist",
open: true, //自动打开一个localhost:8080地址的浏览器地址
port: 8080, //指定要监听请求的端口号:
hot: true, //开启 HotModuleReplacement
// hotOnly: true, //即使HRM不生效,出现问题, 浏览器也不刷新
proxy: {
"/api": "http://localhost:3000",
},
},
optimization: {
usedExports: true,
},
};
module.exports = merge(commonConfig, devConfig);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 环境变量的使用方法
# 1. 分离文件
原有的方法通过不同的配置文件进行配置,然后进行打包
// ——————————————————————————————————webpack.common.js
module.exports = {
// 公共的配置,dev和prod环境都需要
};
// ——————————————————————————————————webpack.dev.js
const commonConfig = require("./webpack.common");
const merge = require("webpack-merge");
const devConfig = {
// 开发环境的主要配置
};
module.exports = merge(commonConfig, devConfig);
// ——————————————————————————————————webpack.prod.js
const commonConfig = require("./webpack.common");
const merge = require("webpack-merge");
const prodConfig = {
// 开发环境的主要配置
};
module.exports = merge(commonConfig, prodConfig);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package.json 如下配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"dev-build": "webpack --config ./build/webpack.dev.js",
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"prod": "webpack --config ./build/webpack.prod.js",
"middleware": "node server.js"
},
2
3
4
5
6
7
8
9
# 2. 利用环境变量
现在也可以通过一个webpack.common.js文件进行配置环境变量进行打包
// webpack.dev.js-----------------------------------------
const devConfig = {
mode: "development", //开发环境进行大打包,代码不会压缩
};
module.exports = devConfig;
// webpack.prod.js---------------------------------------
const prodConfig = {
mode: "production",
};
module.exports = prodConfig;
//webpack.common.js 中进行判断----------------------------
const merge = require("webpack-merge");
const prodConfig = require("./webpack.prod.js");
const devConfig = require("./webpack.dev.js");
const commonConfig = {
//dev和prod的主要公共配置
// ....
};
module.exports = (env) => {
if (env && env.production===abcd) {
return merge(commonConfig, prodConfig);
} else {
return merge(commonConfig, devConfig);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package.json配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"dev-build": "webpack --config ./build/webpack.common.js",
"dev": "webpack-dev-server --config ./build/webpack.common.js",
"prod": "webpack --env.production=abcd --config ./build/webpack.common.js",
"middleware": "node server.js"
},
2
3
4
5
6
7
8
9
# SourceMap(映射)
sourceMap 是一个映射关系,代码出错可以映射到原本我们书写的代码上
devtool此选项控制是否生成,以及如何生成 source map。
module.exports = {
devtool: "source-map"
// ....
};
2
3
4
source-map希望代码写错的时候,我们需要直接找到源代码哪里出错,inline-source-mapmap文件不会出现在 dist 目录下,而是会被通过 base64 方式打包到 js 文件中cheap-module-eval-source-map,开发环境中可使用cheap-module-source-map,生产环境的代码,不需要 sourceMap. 但是我们如果需要定位问题的话,可以使用
# WebpackDevServer
# 配置
devServer:{
contentBase:"./dist",
open:true, //自动打开一个localhost:8080地址的浏览器地址
port: 8080, //指定要监听请求的端口号:
proxy: {
"/api": "http://localhost:3000"
}
}
2
3
4
5
6
7
8
# 扩展
比较早的时候,devServer 不稳定,有些框架作者自己写 server 服务器
const express = require("express");
const webpack = require("webpack");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackConfig = require("./webpack.config"); // 自己配置的
const complier = webpack(webpackConfig); //编译器
const app = express();
app.use(
webpackDevMiddleware(complier, {
publicPath: webpackConfig.output.output
})
);
app.listen(3000, () => {
console.log("服务器启动运行: 3000端口");
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# HMR热模块替换
在HMR之前,大多数开发体验是live reload,保存后自动刷新浏览器,
现在,HMR大幅提高了开发体验,只更新变更内容,调整样式迅速,避免了大部分的网络请求、浏览器重新渲染
我们就是期望有一种手段是当我们修改样式文件的时候,就像是我们在控制台中操作一样,当前的页面不会变,但是样式却生效。这时候我们就可以借助HMR
const webpack = require('webpack');
plugins:[
new webpack .HotModuleReplacementPlugin()
],
devServer:{
contentBase:"./dist",
open:true, //自动打开一个localhost:8080地址的浏览器地址
port: 8080, //指定要监听请求的端口号:
hot:true, //开启 HotModuleReplacement
proxy: {
"/api": "http://localhost:3000"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 对 css 的影响
修改 css 文件时,只会替换修改的 css 文件,而不会整体刷新,方便调试 css
我们修改 css,css-loader 已经帮我们编写完毕了,底层已经实现,vue-loader 里面也帮我们内置了这种 HMR 实现
# 对 js 的影响
同样是打开HMR,如果修改了Js代码是不会自动改变的。webpack为我们提供了很多关于HMR的接口,让我们自行实现热更新的逻辑。为了解决js热更新的这个问题,我们需要对文件的改变做出一个监听,同时给予监听的逻辑。
if (module.hot) {
// 如果开启了HRM,监听number文件变化,这时候重新执行
module.hot.accept("./number", () => {
console.log("哈哈哈")
// 删除老的dom节点
document.body.removeChild(document.getElementById("number"));
number();
});
}
2
3
4
5
6
7
8
9
10
在一些高级的框架中,比如Vue,vue-loader已经帮我们写好了module.hot.accept的部分了
# babel 处理 es6 语法
npm install --save-dev babel-loader @babel/core
- @babe/core 是 babel 核心库,可以让 webpack 识别 js 代码,生成抽象语法树
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}];
}
2
3
4
5
6
7
npm install @babel/preset-env --save-dev`
- babel-loader 只是 webpack 和 babel 之间的通信桥梁, 并不会将 es6 语法翻译成 es5, 我们还需要@babel/preset-env模块进行翻译
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options:{
"presets": ["@babel/preset-env"]
}
}
2
3
4
5
6
7
8
npm install --save @babel/polyfill
- promise,async等这种语法,在低版本浏览器中还是没有,我们不管需要语法转换,我们还要将这些函数等在低版本浏览器中进行补充
import "@babel/polyfill";
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
});
2
3
4
5
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage"
// 根据业务代码进行补充,否则不配置的话,打包出来的文件体积变大
}]
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Tree Shaking
通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。
tree shaking 只支持 ES Module,因为 ES Module 是静态引入代码模块的,而 CommonJS 是动态引入的
webpack的treeshking是基于 es module的静态分析,能够在编译期间就确定哪些模块用到了哪些模块没用到,并且配合解构赋值还能确定哪些export用到了,哪些export没用到。然后对用到的部分和没用到的部分进行标记,在压缩阶段就可以删除标记出的没有用到的部分,从而达到treeshking的目的。
1.development(开发模式)下是没有 Tree Shaking功能的, 如果需要 Tree Shaking我们可以添加配置optimization.usedExports来标记使用和未使用的模块
optimization: {
usedExports: true;
}
2
3
2.将文件标记为无副作用,因为有副作用的代码不能treeshking,只能对export进行treeshking。
在一个纯粹的 ESM 模块世界中,识别出哪些文件有副作用很简单。然而,我们的项目无法达到这种纯度,所以,此时有必要向 webpack 的 compiler 提供提示哪些代码是“纯粹部分”:
比如在window上挂一个变量、写本地文件等,这种代码虽然没有export一些内容,但也是不能被treeshking掉的。对于这些文件需要过滤掉,配置的方式就是在package.json中添加sideEffects字段,因为webpack的模块包括图片、字体文件、css文件等,这些模块都是需要配置的。
// package.json
"sideEffects":false // 不需要@babel/polyfill的情况下,设置为false,意味着: 正常对所有模块进行Tree Shaking处理
//或者,
"sideEffects":["@babel/polyfill"] // 忽略对"@babel/polyfill"的Tree Shaking 处理
2
3
4
5
3.注意:开发环境下即使开启了Tree Shaking,webpack 也不会把没有使用的模块代码去除掉,会保留代码,因为我们开发环境下还需要进行 SourceMap 调试,但是我们如果上线时mode:production,Tree Shaking 就会生效,需要修改配置
mode: "development", //开发环境进行大打包,代码不会压缩
devtool: "cheap-module-source-map", //使用scurceMap
optimization:{
usedExports:false // 开发环境不需要tree shaking
}
2
3
4
5
# Code Splitting
import _ from "lodash"; // 1M
console.log(_.join([1, 2, 3]));
//此处省略.....
console.log(_.join([1, 2, 3]));
2
3
4
打包文件很大,用户打开页面需要加载这么大的 js 文件,导致访问时间变长
最重要的是如果我们修改业务代码后,webpack 又会重新打包生成 main.js,用户又需要重新加载这么大的 js 文件
借助 webpack 配置,进行同步代码的分割,只需要在webpack.common.js中做 optimization的配置
// webpack.common.js
optimization: {
//做代码分割
splitChunks: {
chunks: "all";
}
}
2
3
4
5
6
7
# 1. SplitChunksPlugin
实际上 webpack 打包时,底层使用了 SplitChunksPlugin 这个插件,我们可以给他配置很多参数来完成不同的功能
魔法注释:webpack 会把分离出来的代码文件名自动设置为0.js,我们如果需要添加指定名称,那么就需要些如下注释
function getComponent() {
return import(/* webpackChunkName:"lodash"*/"lodash").then(
({ default: _ }) => {
var element = document.createElement("div");
element.innerHTML = _.join(["cc", "hh"], "-");
return element;
}
);
}
getComponent().then((element) => {
document.body.appendChild(element);
});
2
3
4
5
6
7
8
9
10
11
12
13
# 2. CSS分割
# mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin
一般在线上环境中进行使用,开发环境依然使用 style-loader,线上环境修改为这个插件提供的 loader。webpack.common.js 中将 css 配置部分剪切为两份,
- 一份是开发环境 webpack.dev.js 使用 style-loader
- 一份是生产环境 webpack.prod.js 使用 MiniCssExtractPlugin.loader 如下
// webpack.prod.js
const commonConfig = require("./webpack.common");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const merge = require("webpack-merge");
const prodConfig = {
mode: "production", //开发环境进行大打包,代码不会压缩
devtool: "cheap-module-source-map", //使用scurceMap
plugins: [],
modules: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 2,
},
},
"sass-loader",
{
loader: "postcss-loader",
options: {},
},
],
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
],
},
};
module.exports = merger(commonConfig, prodConfig);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# optimize-css-assets-webpack-plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css",
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 所有入口文件提取合并一个 css
假如页面有多个入口文件index1,index2,index3.js..,希望所有引入的 css 文件都打包生成到一个 css 文件中,
与 extract-text-webpack-plugin 类似,可以使用 optimization.splitChunks.cacheGroups 将 CSS 提取到一个 CSS 文件中。
mini-css-extract-plugin底层也是要借助 splitChunks,所以我们可以额外增加一个组,只要发现打包的文件是 css 后缀。不管是同步还是异步文件,都将打包到一个 styles 文件中去
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: "styles",
test: /\.css$/,
chunks: "all",
enforce: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
还可以把所有入口文件各自提取合并一个 css。假如页面有多个入口文件index1,index2,index3.js..,希望 index1.js 引入的 css 文件都打包生成到一个 css 文件中,index2.js 引入过的 css 文件都打包到另一 css 文件中,依次类推....
# Lazy Loading懒加载
主要是 es 中的 import 实验性质语法,懒加载和 webpack 关系不是很大,主要是 webpack 能识别这种 import 语法,然后对他引入的模块进行代码分割而已
通过 import 异步的加载某一个模块,如下,实际上是在代码真正执行的时候才载入模块
function getComponent() {
return import(/* webpackChunkName:"lodash"*/ "lodash").then(
({ default: _ }) => {
var element = document.createElement("div");
element.innerHTML = _.join(["111", "222"], "-");
return element;
}
);
}
//只有在点击页面时才会加载上面的代码模块
document.addEventListener("click", () => {
getComponent().then((element) => {
document.body.appendChild(element);
});
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
页面加载速度更快,不需要首次加载,比如 vue 等框架中的路由,页面跳转时会展示页面的不同组件
注意:import 语法返回一个 promise,所以项目必须使用 babel-polyfill 进行向下兼容 promise 语法
# 打包分析
# 1. 官方工具
- "dev-build": "webpack --config ./build/webpack.dev.js",
+ "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js",
2
npm run dev-build 就会生成 stats.json 文件,然后进入http://webpack.github.io/analyse
# 2. 第三方插件
webpack-bundle-analyzer
# 提升 webpack 打包速度的方法
# 1. 对loader作用范围做限制
{
test: /\.js$/,
exclude: /node_modules/,
use:[{
loader: "babel-loader"
},{
loader: "imports-loader?this=>window"
}]
}
2
3
4
5
6
7
8
9
10
也可以 include,只有 src 下的代码才被打包编译
{
test: /\.js$/,
include:path.resolve(__dirname,'../src'),
use:[{
loader: "babel-loader"
},{
loader: "imports-loader?this=>window"
}]
}
2
3
4
5
6
7
8
9
10
11
# 2. Plugin使用
尽可能少用 plugin 并确保 plugin 可靠性
开发环境下不需要对代码压缩,所以在开发环境下就不需要配置这种 plugin,减少打包速度
使用 webpack 官方的插件,性能有保障,或者社区认可的插件
# 3. resolve参数合理配置
在代码中通过 import 导入时可以省略文件后缀,我们上面配置时只需要配置 js,jsx 这些逻辑文件,而 css,jpg 等等静态资源最好不要配置,直接显示导入资源文件显示写后缀
extensions配置这个过程是需要Node.js尝试去读取文件实现的,这个过程是非常耗费性能的。
resolve:{
extensions:['.js','.jsx']
},
2
3
# 4. DllPlugin提高打包速度
在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,vue 我们希望能和自己的代码分离开
Dll这个概念是借鉴了Windows系统的dll,一个dll包,就是一个纯纯的依赖库,它本身不能运行,是用来给你的app引用的。
打包dll的时候,Webpack会将所有包含的库做一个索引,写在一个manifest文件中,而引用dll的代码(dll user)在打包的时候,只需要读取这个manifest文件
配置 webpack.dll.js 进行第三方模块的单独打包生一个结果, 暴露一个全局变量,然后借助 DllPlugin 创建对代码进行分析生成一个映射文件
vendors.manifest.json和第三方库的源代码集合文件,然后 build 时,就不用去 node_modules 里面去寻找,这时我们需要 DllReferencePlugin 插件结合映射文件进行分析, 就节省了很多打包时间
# 生成映射文件(构建出动态链接库文件)
//webpack.dll.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
mode: "production",
entry: {
vendors: ["lodash"],
react: ["react", "react-dom"]
},
output: {
path: path.resolve(__dirname, "../dll"),//path是manifest文件的输出路径
filename: "[name].dll.js", //name是dll暴露的对象名
library: "[name]"
},
plugins: [
new webpack.DllPlugin({
name: "[name]",
path: path.resolve(__dirname, "../dll/[name].manifest.json")
})
]
};
// webpack.common.js
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new CleanWebpackPlugin(),
new webpack.ProvidePlugin({
$: "jquery"
}),
new AddAssetHtmlPlugin({
filepath: require.resolve("../dll/vendors.dll.js")
}),
new AddAssetHtmlPlugin({
filepath: require.resolve("../dll/react.dll.js")
}),
// 哪些动态链接库
new webpack.DllReferencePlugin({
// 动态链接库的文件内容
manifest: require.resolve("../dll/vendors.manifest.json")
}),
new webpack.DllReferencePlugin({
//动态链接库的文件内容
manifest: require.resolve("../dll/react.manifest.json")
})
],
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 5. 控制包文件大小
- 用不到的包 tree-shaking 去除掉,也可以不引入
- split-chunk-plugin 将大文件拆分成多个小文件
# 6. 合理使用 sourceMap
生成 sourceMap 越详细,打包速度就越慢
需要考虑的问题:不同环境打包的时候,什么样的 sourceMap 是最合适的?