(1)作用
- 引入 yargs,对命令行进行定制
- 分析命令行参数,对各个参数进行转换,组成编译配置项
- 引用webpack,根据配置项进行编译和构建
(2) 命令行工具包 yargs
- 提供命令和分组参数
- 动态生成 help 帮助信息
(3)webpack-cli 使用 args 分析
参数分组 (config/config-args.js),将命令划分为9类:
- Config options: 配置相关参数(文件名称、运行环境等)
- Basic options: 基础参数(entry设置、debug模式设置、watch监听设置、devtool设置)
- Module options: 模块参数,给 loader 设置扩展
- Output options: 输出参数(输出路径、输出文件名称)
- Advanced options: 高级用法(记录设置、缓存设置、监听频率、bail等)
- Resolving options: 解析参数(alias 和 解析的文件后缀设置)
- Optimizing options: 优化参数
- Stats options: 统计参数
- options: 通用参数(帮助命令、版本信息等)
(4) webpack-cli 执行的结果
1、webpack-cli对配置文件和命令行参数进行转换最终生成配置选项参数 options
2、最终会根据配置参数实例化 webpack 对象,然后执行构建流程
(1)意义
Tapable 是一个类似于 Node.js 的 EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着 webpack 的插件系统。
(2)Tapable库暴露了很多 Hook(钩子)类,为插件提供挂载的钩子
(3)Tapable hooks 类型
(4)Tapable 的使用 -new Hook 新建钩子
1、Tapable 暴露出来的都是类方法,new 一个类方法获得我们需要的钩子
2、class 接受数组参数 options ,非必传。类方法会根据传参,接受同样数量的参数。
const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
(5)Tapable 的使用-钩子的绑定与执行
Tapable 提供了同步&异步绑定钩子的方法,并且他们都有绑定事件和执行事件对 应的方法。
(6)Tapable 的使用-hook 基本用法示例
- const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
- //绑定事件到webapck事件流
- hook1.tap('hook1', (arg1, arg2, arg3) => console.log(arg1, arg2, arg3)) //1,2,3
- //执行绑定的事件
- hook1.call(1,2,3)
(7)Tapable 的使用-实际例子演示
1、定义一个 Car 方法,在内部 hooks 上新建钩子。
2、分别是同步钩子 accelerate、 brake( accelerate 接受一个参数)、异步钩子 calculateRoutes
3、使用钩子对应的绑定和执行方法 calculateRoutes
4、使用 tapPromise 可以返回一个 promise 对象
- const {
- SyncHook,
- AsyncSeriesHook
- } = require('tapable');
-
- class Car {
- constructor() {
- this.hooks = {
- accelerate: new SyncHook(['newspeed']),
- brake: new SyncHook(),
- calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
- }
- }
- }
-
-
- const myCar = new Car();
-
- //绑定同步钩子
- myCar.hooks.brake.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
-
- //绑定同步钩子 并传参
- myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
-
- //绑定一个异步Promise钩子
- myCar.hooks.calculateRoutes.tapPromise("calculateRoutes tapPromise", (source, target, routesList, callback) => {
- // return a promise
- return new Promise((resolve,reject)=>{
- setTimeout(()=>{
- console.log(`tapPromise to ${source} ${target} ${routesList}`)
- resolve();
- },1000)
- })
- });
-
-
- myCar.hooks.brake.call();
- myCar.hooks.accelerate.call(10);
-
- console.time('cost');
-
- //执行异步钩子
- myCar.hooks.calculateRoutes.promise('Async', 'hook', 'demo').then(() => {
- console.timeEnd('cost');
- }, err => {
- console.error(err);
- console.timeEnd('cost');
- });
(8)Tapable 是如何和 webpack 联系起来的
1、核心对象 Compiler 继承 Tapable
2、核心对象 Compilation 继承 Tapable
3、node_modules/webpack/lib/webpack.js
- const webpack = (options, callback) => {
- const webpackOptionsValidationErrors = validateSchema(
- webpackOptionsSchema,
- options
- );
- if (webpackOptionsValidationErrors.length) {
- throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
- }
- let compiler;
- if (Array.isArray(options)) {
- compiler = new MultiCompiler(
- Array.from(options).map(options => webpack(options))
- );
- } else if (typeof options === "object") {
- //初始化默认配置等
- options = new WebpackOptionsDefaulter().process(options);
-
- compiler = new Compiler(options.context);
- compiler.options = options;
- //内部插件(插件必须有个aplly,传入参数compiler)
- new NodeEnvironmentPlugin({
- infrastructureLogging: options.infrastructureLogging
- }).apply(compiler);
- //配置或命令行的插件组装成的options(通过webpack-cli生成)
- if (options.plugins && Array.isArray(options.plugins)) {
- //遍历将compiler传递给每个插件,插件监听compiler里的hooks事件,
- //一旦触发事件,插件就会执行相应方法
- for (const plugin of options.plugins) {
- if (typeof plugin === "function") {
- plugin.call(compiler, compiler);
- } else {
- plugin.apply(compiler);
- }
- }
- }
- compiler.hooks.environment.call();
- compiler.hooks.afterEnvironment.call();
- //加入内置插件
- compiler.options = new WebpackOptionsApply().process(options, compiler);
- } else {
- throw new Error("Invalid argument: options");
- }
- if (callback) {
- if (typeof callback !== "function") {
- throw new Error("Invalid argument: callback");
- }
- if (
- options.watch === true ||
- (Array.isArray(options) && options.some(o => o.watch))
- ) {
- const watchOptions = Array.isArray(options)
- ? options.map(o => o.watchOptions || {})
- : options.watchOptions || {};
- return compiler.watch(watchOptions, callback);
- }
- compiler.run(callback);
- }
- return compiler;
- };
(9)模拟 Compiler.js
- const {
- SyncHook,
- AsyncSeriesHook
- } = require('tapable');
-
- module.exports = class Compiler {
- constructor() {
- this.hooks = {
- accelerate: new SyncHook(['newspeed']),
- brake: new SyncHook(),
- calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
- }
- }
- run(){
- this.accelerate(10)
- this.break()
- this.calculateRoutes('Async', 'hook', 'demo')
- }
- accelerate(speed) {
- this.hooks.accelerate.call(speed);
- }
- break() {
- this.hooks.brake.call();
- }
- calculateRoutes() {
- this.hooks.calculateRoutes.promise(...arguments).then(() => {
- }, err => {
- console.error(err);
- });
- }
- }
(10)插件 my-plugin.js
- const Compiler = require('./Compiler')
-
- class MyPlugin{
- constructor() {
-
- }
- apply(compiler){
- compiler.hooks.brake.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
- compiler.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
- compiler.hooks.calculateRoutes.tapPromise("calculateRoutes tapAsync", (source, target, routesList) => {
- return new Promise((resolve,reject)=>{
- setTimeout(()=>{
- console.log(`tapPromise to ${source} ${target} ${routesList}`)
- resolve();
- },1000)
- });
- });
- }
- }
(11)模拟插件执行
- const Compiler = require('./Compiler')
-
- const myPlugin = new MyPlugin();
-
- const options = {
- plugins: [myPlugin]
- }
-
- const compiler = new Compiler();
-
- for (const plugin of options.plugins) {
- if (typeof plugin === "function") {
- plugin.call(compiler, compiler);
- } else {
- plugin.apply(compiler);
- }
- }
- compiler.run();