疑问:
npx 的作用?
参考
尚硅谷前端Webpack教程(高级进阶篇)
尚硅谷2022版Webpack5入门到原理
图解 VueLoader : .vue 文件是如何被打包的?
5-4 编写 Plugin
教学视频中的环境参数
react 脚手架配置分析
学到的写法
loader: require.resolve('postcss-loader') 可以加快寻包速度,原因未知。
各个文件的作用
config/paths.js:
将很多相对路径解析成绝对路径暴露出去。
scripts/start.js:
检查入口文件。
读取 config/webpack.config.js 中的内容生成 compiler。
读取 config/webpackDevServer.config 内容创建 devServer。
scripts/build.js:
checkBrowsers 用于检查是否显式地指定了 browserslist (多个工具会用到,如 post-css, babel 等)。
webpack.config.js
cross-env XXX=false
用于设置环境变量。
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw)
用于替换 index.html 中的 %PUBLIC_URL%。在 package.json 中用 "homepage" 指定,这个值会被存入 env.raw。
new webpack.DefinePlugin(env.stringified)
用于设置环境变量,把整理好的变量挂到 process.env 上。
webpack.IgnorePlugin
用于指定某些包的某些依赖不被打包。
webpack优化(3)——IgnorePlugin
vue 脚手架配置分析 (不太清楚,视频 p9-p11)
将开发环境配置输出为 webpack.dev.js。
node 配置项用于指定哪些 node 模块不需要打包 (一些库会导入一些node模块,但在浏览器中不需要,webpack 4 中存在的问题)。
context 是入口文件所在的文件夹的绝对路径。用于解析 entry 和 loaders。默认会使用 node.js 的当前工作目录,但是推荐在配置中设定此项,可以使你的配置独立于 node.js 的当前工作目录 (执行 node xxx.js 时所处的目录)。
resolve: { alias: {} } 中的 vue$: 'vue/dist/vue.runtime.esm.js' $ 表示精确匹配。所以代码中导入的 vue 都是运行时版本,去掉了模板编译的代码。因为脚手架中的模板都由 webpack 处理,最终打包后的代码中没有模板需要处理。
resolveLoader 与 resolve 一样,但仅用于解析 loader。
vue-loader 用于处理 .vue 文件。
resourceQuery:import 模块时,路径中可以有查询参数,如 import Foo from './foo.css?inline'。此配置项用于指定需要匹配的查询参数。
如何写 loader
// babelLoader.js
const { getOptions } = require('loader-utils');
const { validate } = require('schema-utils');
const babel = require('@babel/core');
const util = require('util');
const babelSchema = require('./babelSchema.json');
// babel.transform用来编译代码的方法
// 是一个普通异步方法
// util.promisify将普通异步方法转化成基于promise的异步方法
const transform = util.promisify(babel.transform);
module.exports = function (content, map, meta) {
// 获取loader的options配置
const options = getOptions(this) || {};
// 校验babel的options的配置
validate(babelSchema, options, {
name: 'Babel Loader'
});
// 创建异步
const callback = this.async();
// 使用babel编译代码
transform(content, options)
.then(({code, map}) => callback(null, code, map, meta))
.catch((e) => callback(e))
}
如何写 plugin
这部分内容基本都来自《深入浅出 Webpack》
一个最基础的 Plugin 的代码是这样的:
class BasicPlugin {
constructor(options) {
}
// 1. webpack读取配置时,new BasicPlugin() ,会执行插件 constructor 方法
// 2. webpack创建 compiler 对象
// 3. 遍历所有插件,调用插件的 apply 方法
apply(compiler) {
compiler.plugin('compilation',function(compilation) {
})
}
}
module.exports = BasicPlugin;
在使用这个 Plugin 时,相关配置代码如下:
const BasicPlugin = require('./BasicPlugin.js');
module.export = {
plugins:[
new BasicPlugin(options),
]
}
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
Webpack 启动后,在读取配置的过程中会先执行 new BasicPlugin(options) 初始化一个 BasicPlugin 获得其实例。 在初始化 compiler 对象后,再调用 basicPlugin.apply(compiler) 给插件实例传入 compiler 对象。 插件实例在获取到 compiler 对象后,就可以通过 compiler.plugin(事件名称, 回调函数) 监听到 Webpack 广播出来的事件。 并且可以通过 compiler 对象去操作 Webpack。
Compiler 和 Compilation
- Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
- Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。
Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。
事件流
Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。