1. 入门(一起来用这些小例子让你熟悉webpack的配置)
1.1 初始化项目
新建一个目录,初始化npm
npm init
webpack是运行在node环境中的,我们需要安装以下两个npm包
npm i -D webpack webpack-cli
- npm i -D 为npm install --save-dev的缩写
- npm i -S 为npm install --save的缩写
新建一个文件夹src
,然后新建一个文件main.js
,写一点代码测试一下
console.log('call me 老yuan')
配置package.json命令
执行
npm run build
此时如果生成了一个dist文件夹,并且内部含有main.js说明已经打包成功了
1.2 开始我们自己的配置
上面一个简单的例子只是webpack自己默认的配置,下面我们要实现更加丰富的自定义配置
新建一个build
文件夹,里面新建一个webpack.config.js
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'),
// 入口文件
output: {
filename: 'output.js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
}
}
更改我们的打包命令
执行 npm run build
会发现生成了以下目录(图片)
其中dist
文件夹中的main.js
就是我们需要在浏览器中实际运行的文件
当然实际运用中不会仅仅如此,下面让我们通过实际案例带你快速入手webpack
1.3 配置html模板
js文件打包好了,但是我们不可能每次在html
文件中手动引入打包好的js
module.exports = { // 省略其他配置
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
}
}
这时候生成的dist
目录文件如下
为了缓存,你会发现打包好的js文件的名称每次都不一样。webpack打包出来的js文件我们需要引入到html中,但是每次我们都手动修改js文件名显得很麻烦,因此我们需要一个插件来帮我们完成这件事情
npm i -D html-webpack-plugin
新建一个build
同级的文件夹public
,里面新建一个index.html
具体配置文件如下
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = {
mode: 'development', // 开发模式
entry: path.resolve(__dirname, '../src/main.js'), // 入口文件
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
plugins: [new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
})]
}
生成目录如下(图片)
可以发现打包生成的js文件已经被自动引入html文件中
1.3.1. 多入口文件如何开发
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // 开发模式
entry: {
main: path.resolve(__dirname, '../src/main.js'),
header: path.resolve(__dirname, '../src/header.js')
},
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
plugins: [new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
filename: 'index.html',
chunks: ['main'] // 与入口文件对应的模块名
}), new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/header.html'),
filename: 'header.html',
chunks: ['header'] // 与入口文件对应的模块名
}), ]
}
此时会发现生成以下目录
1.3.2 clean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = { // ...省略其他配置
plugins:[new CleanWebpackPlugin()]
}
1.4 引用CSS
我们的入口文件是js,所以我们在入口js中引入我们的css文件
同时我们也需要一些loader来解析我们的css文件
npm i -D style-loader css-loader
如果我们使用less来构建样式,则需要多安装两个
npm i -D less less-loader
配置文件如下
// webpack.config.js
module.exports = { // ...省略其他配置
module: {
rules: [{
test: /.css$/,
use: ['style-loader', 'css-loader'] // 从右向左解析原则
}, {
test: /.less$/,
use: ['style-loader', 'css-loader', 'less-loader'] // 从右向左解析原则
}]
}
}
浏览器打开html
如下
1.4.1 为css添加浏览器前缀
npm i -D postcss-loader autoprefixer
配置如下
// webpack.config.js
module.exports = {
module: {
rules: [test / .less$ / , use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'] // 从右向左解析原则
]
}
}
接下来,我们还需要引入autoprefixer
使其生效,这里有两种方式
1,在项目根目录下创建一个postcss.config.js
文件,配置如下:
module.exports = { plugins: [require('autoprefixer')] // 引用该插件即可了}
2,直接在webpack.config.js
里配置
// webpack.config.js
module.exports = { //...省略其他配置
module: {
rules: [{
test: /.less$/,
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}, 'less-loader'] // 从右向左解析原则
}]
}
}
这时候我们发现css通过style标签的方式添加到了html文件中,但是如果样式文件很多,全部添加到html中,难免显得混乱。这时候我们想用把css拆分出来用外链的形式引入css文件怎么做呢?这时候我们就需要借助插件来帮助我们
1.4.2 拆分css
npm i -D mini-css-extract-plugin
配置文件如下
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = { //...省略其他配置
module: {
rules: [{
test: /.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
}]
},
plugins: [new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[id].css",
})]
}
1.4.3 拆分多个css
npm i - D extract - text - webpack - plugin @next // webpack.config.js
const path = require('path');
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
let indexLess = new ExtractTextWebpackPlugin('index.less');
let indexCss = new ExtractTextWebpackPlugin('index.css');
module.exports = {
module: {
rules: [{
test: /.css$/,
use: indexCss.extract({
use: ['css-loader']
})
}, {
test: /.less$/,
use: indexLess.extract({
use: ['css-loader', 'less-loader']
})
}]
},
plugins: [indexLess, indexCss]
}
1.5 打包 图片、字体、媒体、等文件
file-loader
就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件url),并将文件移动到输出的目录中url-loader
一般与file-loader
搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中
// webpack.config.js
module.exports = { // 省略其它配置 ...
module: {
rules: [ // ...
{
test: /.(jpe?g|png|gif)$/i, //图片文件
use: [{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}]
}, {
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, //媒体文件
use: [{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}]
}, {
test: /.(woff2?|eot|ttf|otf)(?.*)?$/i, // 字体
use: [{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
}
}]
},
]
}
}
1.6 用babel转义js文件
为了使我们的js代码兼容更多的环境我们需要安装依赖
npm i babel-loader @babel/preset-env @babel/core
- 注意
babel-loader
与babel-core
的版本对应关系
-
babel-loader
8.x 对应babel-core
7.x -
babel-loader
7.x 对应babel-core
6.x
配置如下
// webpack.config.js
module.exports = { // 省略其它配置 ...
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
}, ]
}
}
上面的babel-loader
只会将 ES6/7/8语法转换为ES5语法,但是对新api并不会转换 例如(promise、Generator、Set、Maps、Proxy等)
此时我们需要借助babel-polyfill来帮助我们转换
npm i @babel/polyfill
// webpack.config.js
const path = require('path')module.exports = {
entry: ["@babel/polyfill,path.resolve(__dirname,'../src/index.js')"], // 入口文件}
- 手动把上面的demo敲一遍对阅读下面的文章更有益,建议入门的同学敲三遍以上
上面的实践是我们对webpack的功能有了一个初步的了解,但是要想熟练应用于开发中,我们需要一个系统的实战。让我们一起摆脱脚手架尝试自己搭建一个vue开发环境
2. 搭建vue开发环境
上面的小例子已经帮助而我们实现了打包css、图片、js、html等文件。
但是我们还需要以下几种配置
2.1 解析.vue文件
npm i -D vue-loader vue-template-compiler vue-style-loadernpm i -S vue
vue-loader
用于解析.vue
文件vue-template-compiler
用于编译模板
配置如下
const vueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [{
test: /.vue$/,
use: ['vue-loader']
}, ]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js',
' @': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
plugins: [new vueLoaderPlugin()]
}
2.2 配置webpack-dev-server进行热更新
npm i -D webpack-dev-server
配置如下
const Webpack = require('webpack')
module.exports = { // ...省略其他配置
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [new Webpack.HotModuleReplacementPlugin()]
}
完整配置如下
// webpack.config.js
const path = require('path');
const {
CleanWebpackPlugin
} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const Webpack = require('webpack')
module.exports = {
mode: 'development', // 开发模式
entry: {
main: path.resolve(__dirname, '../src/main.js'),
},
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname, '../dist') // 打包后的目录
},
module: {
rules: [{
test: /.vue$/,
use: ['vue-loader']
}, {
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}, {
test: /.css$/,
use: ['vue-style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
}, {
test: /.less$/,
use: ['vue-style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}, 'less-loader']
}]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js',
' @': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
filename: 'index.html'
}), new vueLoaderPlugin(), new Webpack.HotModuleReplacementPlugin()]
}
2.3 配置打包命令
打包文件已经配置完毕,接下来让我们测试一下
首先在src新建一个main.js
新建一个App.vue
新建一个public文件夹,里面新建一个index.html
执行npm run dev
这时候如果浏览器出现Vue开发环境运行成功,那么恭喜你,已经成功迈出了第一步
2.4 区分开发环境与生产环境
实际应用到项目中,我们需要区分开发环境与生产环境,我们在原来webpack.config.js的基础上再新增两个文件
-
webpack.dev.js
开发环境配置文件
开发环境主要实现的是热更新,不要压缩代码,完整的sourceMap
-
webpack.prod.js
生产环境配置文件
生产环境主要实现的是压缩代码、提取css文件、合理的sourceMap、分割代码需要安装以下模块:npm i -D webpack-merge copy-webpack-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
-
webpack-merge
合并配置 -
copy-webpack-plugin
拷贝静态资源 -
optimize-css-assets-webpack-plugin
压缩css -
uglifyjs-webpack-plugin
压缩js
2.4.1 webpack.config.js
const path = require('path')
const {
CleanWebpackPlugin
} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const devMode = process.argv.indexOf('--mode=production') === -1;
module.exports = {
entry: {
main: path.resolve(__dirname, '../src/main.js')
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'js/[name].[hash:8].js',
chunkFilename: 'js/[name].[hash:8].js'
},
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
}, {
test: /.vue$/,
use: ['cache-loader', 'thread-loader', {
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
}]
}, {
test: /.css$/,
use: [{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options: {
publicPath: "../dist/css/",
hmr: devMode
}
}, 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
}, {
test: /.less$/,
use: [{
loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
options: {
publicPath: "../dist/css/",
hmr: devMode
}
}, 'css-loader', 'less-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
}, {
test: /.(jep?g|png|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
}, {
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
}, {
test: /.(woff2?|eot|ttf|otf)(?.*)?$/i,
use: {
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
}]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js',
' @': path.resolve(__dirname, '../src')
},
extensions: ['*', '.js', '.json', '.vue']
},
plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html')
}), new vueLoaderPlugin(), new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
})]
}
2.4.2 webpack.dev.js
const Webpack = require('webpack')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(webpackConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
devServer: {
port: 3000,
hot: true,
contentBase: '../dist'
},
plugins: [new Webpack.HotModuleReplacementPlugin()]
})
2.4.3 webpack.prod.js
const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = WebpackMerge(webpackConfig, {
mode: 'production',
devtool: 'cheap-module-source-map',
plugins: [
new CopyWebpackPlugin([{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist')
}]),
],
optimization: {
minimizer: [new UglifyJsPlugin({ //压缩js
cache: true,
parallel: true,
sourceMap: true
}), new OptimizeCssAssetsPlugin({})],
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: "chunk-libs",
test: /[/]node_modules[/]/,
priority: 10,
chunks: "initial" // 只打包初始时依赖的第三方
}
}
}
}
})
2.5 优化webpack配置
看到这里你或许有些累了,但是要想获取更好的offer,更高的薪水,下面必须继续深入
优化配置对我们来说非常有实际意义,这实际关系到你打包出来文件的大小,打包的速度等。
具体优化可以分为以下几点:
2.5.1 优化打包速度
2.5.1.1 合理的配置mode参数与devtool参数
mode
可设置development
production
两个参数
如果没有设置,webpack4
会将 mode
的默认值设置为 production``production
模式下会进行tree shaking
(去除无用代码)和uglifyjs
(代码压缩混淆)
2.5.1.2 缩小文件的搜索范围(配置include exclude alias noParse extensions)
-
alias
: 当我们代码中出现import 'vue'
时, webpack会采用向上递归搜索的方式去node_modules
目录下找。为了减少搜索范围我们可以直接告诉webpack去哪个路径下查找。也就是别名(alias
)的配置。 -
include exclude
同样配置include exclude
也可以减少webpack loader
的搜索转换时间。 -
noParse
当我们代码中使用到import jq from 'jquery'
时,webpack
会去解析jq这个库是否有依赖其他的包。但是我们对类似jquery
这类依赖库,一般会认为不会引用其他的包(特殊除外,自行判断)。增加noParse
属性,告诉webpack
不必解析,以此增加打包速度。 -
extensions
webpack
会根据extensions
定义的后缀查找文件(频率较高的文件类型优先写在前面)
2.5.1.3 使用HappyPack开启多进程Loader转换
npm i -D happypack
2.5.1.4 使用webpack-parallel-uglify-plugin 增强代码压缩
npm i -D webpack-parallel-uglify-plugin
2.5.1.5 抽离第三方模块
这里我们使用webpack
内置的DllPlugin DllReferencePlugin
进行抽离
在与webpack
配置文件同级目录下新建webpack.dll.config.js
代码如下
// webpack.dll.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = { // 你想要打包的模块的数组
entry: {
vendor: ['vue', 'element-ui']
},
output: {
path: path.resolve(__dirname, 'static/js'), // 打包后文件输出的位置
filename: '[name].dll.js',
library: '[name]_library'
// 这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
},
plugins: [
new webpack.DllPlugin({
path: path.resolve(__dirname, '[name]-manifest.json'),
name: '[name]_library',
context: __dirname
})
]
};
在package.json
中配置如下命令
"dll": "webpack --config build/webpack.dll.config.js"
接下来在我们的webpack.config.js
中增加以下代码
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./vendor-manifest.json')
}),
new CopyWebpackPlugin([ // 拷贝生成的文件到dist目录 这样每次不必手动去cv {from: 'static', to:'static'} ]),
]
};
执行
npm run dll
会发现生成了我们需要的集合第三地方
代码的vendor.dll.js
我们需要在html
文件中手动引入这个js
文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>老yuan</title>
<script src="static/js/vendor.dll.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
这样如果我们没有更新第三方依赖包,就不必npm run dll
。直接执行npm run dev npm run build
的时候会发现我们的打包速度明显有所提升。因为我们已经通过dllPlugin
将第三方依赖包抽离出来了。
2.5.1.6 配置缓存
npm i -D cache-loader
2.5.2 优化打包文件体积
2.5.2.1 引入webpack-bundle-analyzer分析打包后的文件
webpack-bundle-analyzer
将打包后的内容束展示为方便交互的直观树状图,让我们知道我们所构建包中真正引入的内容
npm i -D webpack-bundle-analyzer
接下来在package.json
里配置启动命令
"analyz": "NODE_ENV=production npm_config_report=true npm run build"
windows请安装npm i -D cross-env
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
接下来npm run analyz
浏览器会自动打开文件依赖图的网页
2.5.2.3 externals
webpack
官网案例如下
<script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script>module.exports = {
//...
externals: { jquery: 'jQuery' }};
import $ from 'jquery';
$('.my-element').animate(/* ... */);
2.5.2.3 Tree-shaking
// .babelrc{ "presets": [ ["@babel/preset-env", { "modules": false } ] ]}
或者
// webpack.config.js
module: {
rules: [{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', {
modules: false
}]
}
},
exclude: /(node_modules)/
}]
}
3.手写webpack系列
3.1 手写webpack loader
loader
编写原则
- 单一原则: 每个
Loader
只做一件事; - 链式调用:
Webpack
会按顺序链式调用每个Loader
; - 统一原则: 遵循
Webpack
制定的设计规则和结构,输入与输出均为字符串,各个Loader
完全独立,即插即用;
在日常开发环境中,为了方便调试我们往往会加入许多console
打印。但是我们不希望在生产环境中存在打印的值。那么这里我们自己实现一个loader
去除代码中的console
npm i -D @babel/parser @babel/traverse @babel/generator @babel/types
-
@babel/parser
将源代码解析成AST
-
@babel/traverse
对AST
节点进行递归遍历,生成一个便于操作、转换的path
对象 -
@babel/generator
将AST
解码生成js
代码 -
@babel/types
通过该模块对具体的AST
节点进行进行增、删、改、查
新建 drop-console.js
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const generator = require('@babel/generator').default
const t = require('@babel/types')
module.exports = function(source) {
const ast = parser.parse(source, {
sourceType: 'module'
}) traverse(ast, {
CallExpression(path) {
if (t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, {
name: "console"
})) {
path.remove()
}
}
}) const output = generator(ast, {}, source);
return output.code
}
如何使用
const path = require('path')
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'index.js'),
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /.js$/,
use: path.resolve(__dirname, 'drop-console.js')
}]
}
}
附上官网 如何编写一个loader
3.2 手写webpack plugin
一个基本的plugin插件结构如下
class firstPlugin {
constructor(options) {
console.log('firstPlugin options', options)
}
apply(compiler) {
compiler.plugin('done', compilation => {
console.log('firstPlugin')))
}
}
module.exports = firstPlugin
-
compiler
对象包含了Webpack
环境所有的的配置信息。这个对象在启动webpack
时被一次性建立,并配置好所有可操作的设置,包括options
,loader
和plugin
。当在webpack
环境中应用一个插件时,插件将收到此compiler
对象的引用。可以使用它来访问webpack
的主环境。 -
compilation
对象包含了当前的模块资源、编译生成资源、变化的文件等。当运行webpack
开发环境中间件时,每当检测到一个文件变化,就会创建一个新的compilation
,从而生成一组新的编译资源。compilation
对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
compiler和 compilation的区别在于
- compiler代表了整个webpack从启动到关闭的生命周期,而compilation只是代表了一次新的编译过程
- compiler和compilation暴露出许多钩子,我们可以根据实际需求的场景进行自定义处理
compiler钩子文档
compilation钩子文档
下面我们手动开发一个简单的需求,在生成打包文件之前自动生成一个关于打包出文件的大小信息
新建一个webpack-firstPlugin.js
class firstPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
compiler.plugin('emit', (compilation, callback) => {
let str = ''
for (let filename in compilation.assets) {
str += `文件:${filename} 大小${compilation.assets[filename]['size']()}n`
} // 通过compilation.assets可以获取打包后静态资源信息,同样也可以写入资源
compilation.assets['fileSize.md'] = {
source: function() {
return str
},
size: function() {
return str.length
}
}
callback()
})
}
}
module.exports = firstPlugin
如何使用
const path = require('path')const firstPlugin = require('webpack-firstPlugin.js')module.exports = { // 省略其他代码 plugins:[ new firstPlugin() ]}
执行 npm run build
即可看到在dist
文件夹中生成了一个包含打包文件信息的fileSize.md
附上官网 如何编写一个plugin
3.3 手写webpack
4. webpack5.0的时代
无论是前端框架还是构建工具的更新速度远远超乎了我们的想象,前几年的jquery
一把梭的时代一去不复返。我们要拥抱的是不断更新迭代的vue、react、node、serverless、docker、k8s
....
不甘落后的webpack也已经在近日发布了 webpack 5.0.0 beta 10 版本。在之前作者也曾提过webpack5.0
旨在减少配置的复杂度,使其更容易上手(webpack4
的时候也说了这句话),以及一些性能上的提升
- 使用持久化缓存提高构建性能;
- 使用更好的算法和默认值改进长期缓存(long-term caching);
- 清理内部结构而不引入任何破坏性的变化;
- 引入一些breaking changes,以便尽可能长的使用v5版本。
目前来看,维护者的更新很频繁,相信用不了多久webpack5.0
将会拥抱大众。感兴趣的同学可以先安装beta
版本尝尝鲜。不过在此之前建议大家先对webpack4
进行一番掌握,这样后面的路才会越来越好走。