以下皆为拉勾教育课件内笔记
区分打包环境
通过环境变量区分
详情:https://www.webpackjs.com/guides/environment-variables/
命令行中指定参数:
# Webpack 5
webpack --env production
# Webpack 4
webpack --env.production
配置文件中根据环境参数进行判断:
// webpack.config.js
module.exports = (env, argv) => {
const config = {
mode: 'development'
// 开发配置
}
if (env.production) {
config.mode = 'production'
// ⽣产配置
}
return config
}
通过配置文件区分
在打包命令中指定配置文件:
# 开发环境打包
webpack --config webpack.dev.conf.js
# ⽣产环境打包
webpack --config webpack.prod.conf.js
有些配置在开发环境和生产环境都需要,因此,我们需要声明⼀个公共配置文件 webpack.base.conf.js 使用时,我们可以将 base 合并到 dev 或 prod 中。
此时,我们需要⼀个合并配置的插件:webpack-merge
详情:https://www.npmjs.com/package/webpack-merge
声明通用配置:
const { resolve } = require('path')
module.exports = {
// ⼊⼝⽂件
entry: './src/index.js',
// 出⼝配置
output: {
// 输出⽬录(输出⽬录必须是绝对路径)
path: resolve(__dirname, './dist'),
// 输出⽂件名称
filename: 'bundle.js'
},
// ......
}
声明开发配置:
// webpack.dev.conf.js
const { merge } = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const webpack = require('webpack')
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
plugins: [
new webpack.DefinePlugin({
// 开发环境下的接⼝地址
// 变量后⾯的值,是⼀段代码⽚段
API_BASE_URL: JSON.stringify('http://apidev.example.com')
}),
]
// ......
})
声明生产配置:
// webpack.prod.conf.js
const { merge } = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const webpack = require('webpack')
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'production',
plugins: [
new webpack.DefinePlugin({
// ⽣产环境下的接⼝地址
// 变量后⾯的值,是⼀段代码⽚段
API_BASE_URL: JSON.stringify('http://apiprod.example.com')
}),
]
// ......
})
自定义 plugin
简介
Webpack 是基于插件机制的。
Webpack 插件是⼀个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用, 并且在整个编译生命周期都可以访问 compiler 对象。
插件原理:通过在生命周期的钩子中挂载函数,来实现功能扩展。
插件详情:https://webpack.docschina.org/concepts/plugins/
生命周期
生命周期就是整个生命过程中的关键节点。
- 人:出生 -> 入学 -> 毕业 -> 结婚 -> 生子 -> 死亡
- 程序:初始化 -> 挂载 -> 渲染 -> 展示 -> 销毁
钩子
- 钩子是提前在可能增加功能的地方,埋好(预设)⼀个函数
- 简单理解:钩子是生命周期中的函数
比如坐火车经过某个站点的时候,我们可能会做些事情。站点就是可能增加功能的地方,可以理解为钩子。
Webpack 的钩⼦详情:https://www.webpackjs.com/api/compiler-hooks/
常用钩子:
钩子 | 描述 | 类型 |
---|---|---|
environment | 环境准备好 | SyncHook |
compile | 编译开始 | SyncHook |
compilation | 编译结束 | SyncHook |
emit | 打包资源到 output 之前 | AsyncSeriesHook |
afterEmit | 打包资源到 output 之后 | AsyncSeriesHook |
done | 打包完成 | SyncHook |
钩子之间有明确的先后顺序
自定义插件
声明自定义插件
// 声明⾃定义插件
class MyPlugin {
constructor(options) {
console.log('插件配置选项', options)
this.userOptions = options || {}
}
// 必须声明 apply ⽅法
apply(compiler) {
// 在钩⼦上挂载功能
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation 是此次打包的上下⽂
for (const name in compilation.assets) {
console.log(name)
// 针对 css ⽂件,执⾏相关操作
// if (name.endsWith('.css')) {
if (name.endsWith(this.userOptions.target)) {
// 获取处理之前的内容
const contents = compilation.assets[name].source()
// 将原来的内容,通过正则表达式,删除注释
const noComments = contents.replace(/\/\*[\s\S]*?\*\//g , '')
// 将处理后的结果,替换掉
compilation.assets[name] = {
source: () => noComments,
size: () => noComments.length
}
}
}
})
}
}
module.exports = MyPlugin
使用自定义插件
// 引⼊⾃定义插件
const MyPlugin = require('./plugin/MyPlugin')
module.exports = (env, argv) => {
// ......
// 插件配置
plugins: [
// 引⼊⾃定义插件
new MyPlugin({
target: '.css'
})
]
// ......
}
自定义 loader
Loader 本质上就是⼀个 ESM 模块,它导出⼀个函数,在函数中对打包资源进行转换。
自定义loader
声明⼀个读取 markdown(.md)文件内容的 loader
- marked(将 markdown 语法转成 html)
- loader-utils(接受 loader 的配置项)
先安装上述两个包:npm i -D marked loader-utils
const marked = require('marked')
const { getOptions } = require('loader-utils')
// 导出函数 (建议使⽤普通函数)
module.exports = function(source) {
// 获取 loader 的配置项
const options = getOptions(this)
console.log('my loader', options)
// return 'my loader'
// return 'console.log("my loader")'
const html = marked(source)
// "<h1 id="关于">关于</h1><p>我是张三</p>"
// 直接返回,可能因为引号的问题,导致报错
// return `module.exports = "${html}"`
// return `module.exports = ${JSON.stringify(html)}`
// 直接返回 html,交给下⼀个 loader 进⾏处理
return html
}
如果你声明的 loader 是处理管道中的最后⼀环,则返回结果要求必须是 JavaScript 代码。如果中间有多个 loader 只需要保证最后一个 loader 的返回结果是 JavaScript 。
使用自定义 loader
module.exports = (env, argv) => {
// ......
// 模块配置
module: {
rules: [
{
test: /\.md$/i,
// use: './loader/markdown-loader'
use: [
'html-loader',
// './loader/markdown-loader'
{
loader: './loader/markdown-loader',
options: {
size: 20
}
}
]
},
]
}
// ......
}