Phil
2021/05/11阅读:83主题:前端之巅同款
webpack
webpack
webpack 核心概念
-
entry
指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。
bundle: 经过Webpack 流程解析编译后最终输出的⽂件
-
output
告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。
-
loader
webpack 自身只理解 JavaScript文件。
loader 让 webpack 能够去处理那些非 JavaScript 文件。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
-
plugins
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
-
mode
development 或 production。 通过选择相应的模式,启动webpack内置的优化。
webpack 默认配置
webpack默认使用webpack.config.js作为配置文件,我们可以重写这个文件进行个性化配置
执行webpack命令时, 默认使用webpack.config.js, 通过--config 【webpackconfig.js】 来指定相应的配置文件
npx webpack
npx webpack --config [webpackconfig.js]
entry
支持单入口和多入口
-
单入口
{
entry:"./src/index.js"
}
上面为简写, 具体为:
{
entry: {
main: "./src/index.js"
}
}
-
多入口
{
entry: {
index:"./src/index.js",
login:"./src/login.js"
}
}
output
{
output: {
filename: 'main.js',
path: path.join(__dirname, './dist')
}
}
多入口处理
{
output: {
filename: '[name][chunkhash:8].js',
path: path.join(__dirname, './dist')
}
}
[name] 为占位符,即entry对象形式中的key。
[chunkhash:8] 为hash 处理后前8位。
mode
-
development
-
production
对于项目而言, 在开发环境和生产环境上, 我们希望优化的效果是不一致的。 比如说,在生产环境上, 我们希望项目尽可能的压缩, 而在开发环境上, 我们希望项目代码能保持一致, 这样出现错误容易定位。
所以,webpack会使用内置函数分别来处理开发环境和生产环境优化。

loader
示例
-
使用css-loader 来解析css文件
module: {
rules: [{
test: /\.css$/,
use: "style-loader"
}]
}
plugin
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
使用HtmlWebpackPlugin插件可以在我们打包生成dist文件时, 自动根据我们的template模板来生成index.html文件。不需要我们手动创建。
编写一个loader
-
示例: 编写替换字符串loader
/src/index.js
console.log('hello webpack')
/loaders/replaceStringLoader.js
module.exports = function(source) {
return source.replace('webpack','world!')
}
/webpack.config.js
const path = require("path")
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.join(__dirname, "./dist"),
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
use: path.resolve(__dirname, "./loaders/replaceStringLoader.js"),
},
],
},
}
运行npx webpack 后, 观察dist下的main.js代码
结果 webpack 字符串变成来world!
console.log("hello world!");
给loader配置参数
修改/webpack.config.js, 增加一个options
...
rules: [
{
test: /\.js$/,
use: [{
loader: path.resolve(__dirname, "./loaders/replaceStringLoader.js"),
options: {
name: "option world!"
}
}],
},
],
...
修改/loaders/replaceStringLoader.js, 解析配置的options, 使用loader-utils工具包帮助解析
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
return source.replace('webpack', options.name)
}
结果:
console.log("hello option world!");
通过callback来替换return
this.callback 格式
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
修改/loaders/replaceStringLoader.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
const result = source.replace('webpack', options.name)
this.callback(null, result)
}
运行, 修改和之前return形式的保持一致
通过this.async来处理异步代码
注意this.async的使用
修改/loaders/replaceStringLoader.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
// this.callback(null, result)
const callback = this.async()
setTimeout(() => {
const result = source.replace('webpack', options.name)
callback(null, result)
}, 1000)
}
如果我们直接return result 会报错, 通过this.async才能处理异步代码
const loaderUtils = require('loader-utils')
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
// this.callback(null, result)
// const callback = this.async()
setTimeout(() => {
const result = source.replace('webpack', options.name)
return result
// callback(null, result)
}, 1000)
}
报错信息

-
使用多个loader
顺序: ⾃下⽽上,⾃右到左
示例
{
test: /\.js/,
use: [
'bar-loader',
'foo-loader'
]
}
自右到左
执行顺序: foo-loader 被传入原始资源,bar-loader 将接收 foo-loader 的产出
-
loader 路径问题
在前面的使用中, 都是通过
path.resolve(__dirname, "./loaders/replaceStringLoader.js")
来引入自定义的loader, 这种方式比较繁琐。
可以通过webpack.config.js 来配置loader路径。
resolveLoader: {
modules: ["node_modules", "./loaders"],
},
默认使用node_modules
下的loader,未找到,再去 ./loaders
下查找
修改webpack.config.js
...
resolveLoader: {
modules: ["node_modules", "./loaders"],
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "replaceStringLoader",
// loader: path.resolve(__dirname, "./loaders/replaceStringLoader.js"),
options: {
name: "option world!",
},
},
],
},
],
},
...
编写一个plugin
官网指导编写一个plugin 中文官网的例子没有更新,请参考英文官网。
FileListPlugin 示例
-
FileListPlugin 基本结构
class FileListPlugin {
constructor(options) {
}
apply(compiler) {
compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback) => {
console.log('this is custom plugin')
callback()
})
}
}
module.exports = FileListPlugin
插件的核心方法为apply, 接受一个compiler对象。
compiler 对象包含很多hook(钩子)函数, 在这里使用emit(即在⽣成⽂件到output⽬录之前执⾏)函数, 使用tapAsync(异步处理, 同步处理为tap)将插件执行放到webpack执行过程中。
tapAsync 第一个参数通常我们命名为插件的名字, 第二个参数会一个函数, 接受compilation对象和callback,执行完后要调用callback。
配置webpack.config.js
const path = require("path")
const FileListPlugin = require("./plugins/fileListPlugin")
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.join(__dirname, "./dist"),
},
mode: "production",
resolveLoader: {
modules: ["node_modules", "./loaders"],
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "replaceStringLoader",
// loader: path.resolve(__dirname, "./loaders/replaceStringLoader.js"),
options: {
name: "option world!",
},
},
],
},
],
},
plugins: [
new FileListPlugin()
]
}
执行结果:

-
完善FileListPlugin功能 fileListPlugin.js
class FileListPlugin {
constructor(options) {
// 获取插件配置项
this.filename = options && options.filename
}
apply(compiler) {
compiler.hooks.emit.tapAsync("FileListPlugin", (compilation, callback) => {
// 通过 compilation.assets 获取⽂件数量
let len = Object.keys(compilation.assets).length
// 添加统计信息
let content = `# ${len} file${len > 1 ? "s" : ""} emitted \n\n`
// 通过 compilation.assets 获取⽂件名列表
for (let filename in compilation.assets) {
content += `- ${filename}\n`
}
const filename = this.filename || "fileList.md"
// 往 compilation.assets 中添加清单⽂件
compilation.assets[filename] = {
// 写⼊新⽂件的内容
source: function () {
return content
},
// 新⽂件⼤⼩(给 webapck 输出展示⽤)
size: function () {
return content.length
},
}
callback()
})
}
}
module.exports = FileListPlugin
修改webpack.config.js
...
plugins: [
new FileListPlugin({
filename: 'FileList.md'
})
]
...


参考文章
作者介绍