###webpack简述webpack是目前最流行的模块管理器,是前端构建体系中不可或缺的一环。
如果用一句话概括的话,webpack本身只做了一件事件:运行一个入口.js文件,分析它的依赖,最后把文件导出。如果你需要在这个过程中做点其他什么事情,请使用webpack的强大插件机制。
基本使用
第一步,初始化文件夹1
yarn init -y
第二步,添加 webpack、webpack-cli1
yarn add webpack webpack-cli -D
第三步, 添加 webpack.config.js文件,这是webpack的配置文件,可以指定其他名字,这是默认名字。虽然webpack4号称零配置,但一些重要的配置项和基本信息还是要了解一下的。1
2
3
4
5
6
7
8module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
},
plugins: [],
module: {}
}
第四步, 执行webpack进行打包
1 | npx webpack |
入口文件
webpack干的事情就是执行一个 .js文件(不一定是.js文件,node支持运行就可以),然后分析收集它依赖了(引用、import、require)了哪些模块,然后对这些代码进行处理,最后导出。
这个.js文件就是所谓的入口文件,可以在webpack.config.js文件中这么配置
1 | module.exports = { |
出口
文件导出的目录与文件夹1
2
3
4
5
6
7module.exports = {
output: {
filename: "bundle.[hash:8].js", //文件名
path: path.join(__dirname, "dist"), //文件夹
publicPath: '' // 路径前缀,比如 https://static.aliyun.com
},
}
输出管理
设置html输出
使用html-webpack-plugin为入口文件关联并解析html模版。1
yarn add html-webpack-plugin -D
配置插件1
2
3
4
5
6
7
8
9
10const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html"
})
]
}
清理dist
clean-webpack-plugin在每次构建之前清理文件夹。1
npm install --save-dev clean-webpack-plugin
1 | const CleanWebpackPlugin = require('clean-webpack-plugin') |
开发模式与发布模式
现实中对于模块的处理,在开发时和发布时是两种完全不同的方式。比如开发时并不需要压缩代码、发布时也不需要起一个简易http服务器。
在webpack4.0中可以通过 mode配置项来配置开发模式还是发布模式,通过配置它可以得到官方默认的开发或者发布模式1
2
3module.exports = {
mode: "development", //production
}
使用本地开发服务器
webpack-dev-server提供了一个简单的Web服务器和使用实时重新加载的能力。1
2
3
4
5
6
7
8module.exports = {
devServer: {
port: 8080,
contentBase: "./dist", //告诉webpack-dev-server文件路径在哪
progress: true
},
}
1 | yarn add webpack-dev-server -D |
loader
默认情况下,webpack只支持.js文件的加载,比如想加载css文件就需要使用css加载器。
你需要在 module配置项下为每种文件配置 rules。以css文件为例,使用test正则匹配文件,
然后在use数组中依次写上你要使用的loader, 它们会按数组从下到上的顺序执行1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18module.exports = {
module: {
rules: [
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top'
}
},
"css-loader",
"postcss-loader"
]
]
},
}
加载图片
file-loader插件会将文件直接复制到出口目录下,并以[hash][ext]的方式重新命名1
2
3
4
5
6{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
加载字体
1 | { |
加载本地数据(xml、json、csv)
webpack默认支持.json格式的文件,但是对于 .xml、.csv等文件还是需要安装额外的包1
yarn add csv-loader xml-loader -D
1 | { |
1 | // src/index.js |
插件
webpack本身的功能是非常有限的,如果要使用一些其他能力,就需要使用插件。loader的能力是为webpack扩张可以处理的文件类型,而 plugins则是为webpack扩张生命周期中的某一部分能力。比如我们想使用html模版就需要html-webpack-html插件,使用前请先安装插件。
1 | const HtmlWebpackPlugin = require("html-webpack-plugin"); |
Tree shaking(树摇曳)
树抖动是JavaScript上下文中常用于消除死代码的术语。举个例子:1
2
3
4
5
6
7
8// math.js
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
1 | // index.js |
默认情况下,webpack会使用树摇曳优化,打包时不会包含square函数的代码,因为它没有被使用。
tree-shaking仅在mode: 'production'模式下有效
sourceMap
webpack支持四种方式的souceMap
1 | module.exports = { |
代码分割
使用 splitChunks 之后,webpack会自动将公共代码以代码块的方式分离出来,以多个文件的方式输出到dist目录下。1
2
3
4
5
6
7
8
9
10
11
12
13
14module.exports = {
optimization: {
splitChunks: {
// 代码分割。
cacheGroups: { //缓存组
common: { //公共模块
chunks: 'initial',
minSize: 0, //大于该值的代码块抽离到公共模块
minChunks: 2, // 调用次数大于该值的抽离到公共模块
}
}
}
}
}
第三方模块分离
第三方模块大部分情况下我们不会去更新它,可以把它单独打成一个包,以高效利用缓存。
如果设置了
vendor.chunks: 'all',会将静态、动态导入的包都压缩在一起,这将导致动态导入不可用,代码报错。
1 | module.exports = { |
文件hash名的问题。
假设我们有src/index.js,node_modules/vue,node_modules/lodash。默认情况下,我们修改了src/index.js文件, 导出verdor、dynamic的文件hash名也会更新,这经常是我们不愿意看到的,因为这会让缓存失效。
解决方案,使用HashedModuleIdsPlugin1
2
3
4
5
6
7
8
9
10
const webpack = require("webpack")
module.epoxrts = {
output: {
filename: "bundle.[contenthash].js",
path: path.join(__dirname, "dist")
},
plugins: [new webpack.HashedModuleIdsPlugin()]
}
延时加载
使用延时加载的模块会被单独分割成一个.js文件。并且在代码运行时以JSONP(动态拼接script返回.js)的形式动态加载。
首先安装1
yarn add @babel/plugin-syntax-dynamic-import -D
配置webpack.config.js的 .js加载器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
'@babel/plugin-syntax-dynamic-import'
]
}
}
]
}
编写代码, import()返回一个cmd模块,所以需要module.default。1
2
3
4
5
6
7setTimeout(() => {
import(/* webpackChunkName: "Vue" */'vue').then((module) => {
console.log(module.default)
}).catch((err)=> {
console.log('module vue load fail', err)
})
}, 1000)
preload/prefetch
- prefetch:将来某些导航可能需要资源
preload:当前导航期间可能需要资源
preload的块开始与父块并行加载。父块完成加载后,将启动prefetch的块preload的块具有中等优先级并立即下载。浏览器空闲时会下载prefetch的块。- 父组块应立即请求
preload的块。未来的任何时候都可以使用prefetch的块
1 | import(/* webpackPrefetch: true */ 'LoginModal') |
优化
webpack打包结果分析
webpack支持打包结果的导出,运行以下命令会在当前目录下生成stats.json文件,该文件存储着各个模块的打包信息。1
npx webpack --mode production --profile --json > stats.json
如果觉得json文件看着太累,你可以把文件上传到这些webpack-chart、webpack-visualizer网站上,它们提供了一个可视化图表来分析你的打包结果。
或者使用webpack-bundle-analyzer。它可以在本地生成一个html服务,在你运行 npx webpack --profile --json > stats.json命令之后。
webpack打包加速
happypack
默认情况下webpack是单线程打包,在项目比较大的情况下,打包速度会很慢。happypack是一个多性程任务,它可以用多线性的方式去执行其他任务。比如处理css、js。
1 | module.exports = { |
noParse
noParse配置项可以跳过匹配的模块,不作处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19module.exports = {
module: {
noParse: /jquery/,
rules: [
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top'
}
},
"css-loader",
"postcss-loader"
]
]
},
}
exclude 和 include
在loader匹配模块的时候可以通过指定 exclude来跳过一些文件夹,从而提高匹配速度。
在loader匹配模块的时候可以通过指定 include来指定文件夹,从而提高匹配速度。
1 | module.exports = { |