美文网首页
frontendmasters webpack-plugins

frontendmasters webpack-plugins

作者: 云峰yf | 来源:发表于2018-11-12 10:45 被阅读0次

Tapable Plugin System

  • webpack 是由插件组成的
  • Tapable 是 webpack 的基石,编写和注册插件都离不开它,之前是靠 compiler 实例
  • 继承了 Tapable 实例的类被称为 Tapable 实例
  • 在类里有若干静态钩子,改善了之前的开发体验(把方法挂载到原型上),因此,所有不同的事件都是 webpack 生命周期的不同部分。
  • .tap 就像 .plugin 的等价物,通过访问 compiler.hooks.compilation 来做一些事情

Compiler & Compilation

  • 有七个最重要的 Tapable 实例
  • 第一个是 compiler,中央调度级别的,控制 webpack 的启动、构建完成、发射资产等,要访问其他实例必须通过它。
    • 如果使用 node api,会得到 compiler.run 实例
  • 第二个是 Compilation,最复杂的部分。是 webpack 构建依赖图的起点。
  • 这是作用域提升的地方,也是我们摇树和所有启发式的地方。

Resolver & Module Factories

  • 第三个是 resolver,你给它一个部分路径,它会确保它存在,然后给你完整的绝对路径。
    • Resolver 实际上是你可以在 webpack 之外使用的东西。
  • 第四个是 Module Factories,他们所做的只是创造实例。
    • normal 模块工厂和 context 模块工厂,它们之间的区别在于支持动态 import 语句中的表达式
    • 它将从文件中提取源代码。将它存储在内存中的这个模块对象中,

Parser & Templates

  • 第五个是 Parser,将一串源代码转换为我们称之为AST或抽象语法树的代码。
    • https://astexplorer.net/ 在线预览效果
    • Webpack 默认使用 Acorn 作为解析器获得 token
    • 解析器从模块工厂获取这个模块对象,将其转换为 AST,然后开始遍历图。
  • 第六个是 Templates,做数据绑定。
    • chunk、module、dep 都有模板,会调用一个名为 render 的函数。
    • 最终得到一些 iife 函数

Compiler Walkthrough

  • 我们将配置传递给 webpack,它读取 entry 属性。
  • 我们不知道该条目是否存在,所以我们必须通过正常的模块工厂,该工厂调用解析器返回完整的路径信息,以及其他一些上下文和有用的数据,并将其传递回正常的模块工厂。
  • 然后工厂创建了这个对象,收集源信息,现在我们有了模块的源代码。
  • 接着解析器将代码解析为 AST。它遍历图形查找依赖语句,找到它时,它会附加到模块。
  • 这是一个广度优先的搜索图遍历。重复这个过程,直到没有更多的依赖关系来解决。
  • 最后进入渲染部分,依赖模板和依赖工厂把能执行在浏览器的代码生成。
  • 可以把它分成三个主要阶段,或者像三个步骤。一:构建图。二:优化图,三,渲染它,我们只使用模板,所以它是数据绑定。

Plugin System Code Walkthrough

  • tappable 实例会先调用 compiler.run,然后应用配置
  • 里面有一个巨大的 switch 语句,用于创建不同的代码
  • 接着在 Complier.hooks.entryoption.call 触发入口选项钩子,为依赖设置模板
  • 最后得到一个模块,但钩子里可以插入不同的逻辑,得到不同的模块
  • AST 的每个部分都会触发一个事件
  • 实际上就是添加依赖项实例并将它们绑定在一起
  • 创建一个解析器插件、创建一个依赖项工厂,就可以做任何事情了。每个依赖实例都有一个模板
  • 修改 AST 很慢,因此我们选择做的是使用模板进行字符串转换。

Creating a Webpack Plugin

  • 插件只是一个实例,或者它是一个具有 apply 方法的类。
  • complier.hooks + 事件 + 触发方式写回调
  • stats 是一个类,它有 Webpack compilation 实例。它告诉我们开始时间,整个包的哈希等有关构建的所有内容。
class MyFirstWebpackPlugin {
    apply(compiler) {
        compiler.hooks.done.tapAsync("MyFirstWebpackPlugin", (stats, cb) => {
            const assetNames = [];
            for (let assetName in stats.compilation.assets) {
                assetNames.push(assetName);
            }
            console.log(assetNames.join("\n"));
            cb();
        });
        compiler.hooks.compilation.tap("MyFirstWebpackPlugin", (compilation, params) => {
            new MyFirstWebpackCompilationPlugin().apply(compilation);
        });
    }
}
class MyFirstWebpackCompilationPlugin {
    apply(compilation) {
        compilation.hooks.seal.tap("MyFirstWebpackPlugin", () => {
            debugger;
        });
    }   
}
module.exports = MyFirstWebpackPlugin; 

Plugin Instance Hooks

  • 如何插入不是 compilation 的东西呢?想插入不同的实例,仍然需要通过 compilation。
  • render 之前先是 seal 阶段
  • 在 NormalModuleFactory.beforeResolve 挂钩子改变引用的模块
    • 比如 ContextReplacementPlugin,将 moment 从部分路径中解析
    • 还有 NormalModuleReplacementPlugin

Isolating Plugins

  • webpack 不调用文件系统,而是调用 MemoryFile 系统:memory-fs
  • 如此抽象,就可以将源码存进 redis 或者 mongo 了
  • 对于 resolver.resolve 的例外,不会做任何解析器调用。
    • 比如可以使用 get 虚拟文件系统并编写一个解析器,用来完全替换 webpack 的解析器
  • 一个插件可以应用另一个插件,从而隔离每个插件的功能集

Creating a Custom Loader

  • 添加一个名为 resolveLoader 的属性
  • 应用于解析模块的任意自定义行为,可以为你的加载器执行相同的操作。
  • 有同步、异步,补丁 loader
  • 如果你想在加载器之间共享公共 AST,那么传递一个抽象语法树作为第四个参数可以加快构建时间。

Configuring Babel for Webpack

  • 使用 babel 会破坏 webpack 所有对 ES 模块的优化,所以用 babel 请设置 preset 的 module 为 false

Webpack Dev Kit & Wrap Up

留下了一个示例项目:Webpack Developer Kit

相关文章

网友评论

      本文标题:frontendmasters webpack-plugins

      本文链接:https://www.haomeiwen.com/subject/obuqfqtx.html