webpack:系统的了解webpack一些核心概念

文章目录

      • webpack 如何处理应用程序?
      • 何为webpack模块chunk?
      • 入口(entry)
      • 输出(output)
      • loader
        • 开发loader
      • 插件(plugin)
        • 简介
        • 流程
        • 插件开发:
        • Tapable类
        • 监听(watching)
        • compiler 钩子
        • compilation 钩子
        • compiler和compilation
        • 创建自定义 插件
      • loader和plugins区别
      • 模式(mode)

webpack 如何处理应用程序?

从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块(chunk)组合成一个或多个 bundles

何为webpack模块chunk?

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD define 和 require 语句
  • css/sass/less 文件中的 @import 语句。
  • stylesheet url(…) 或者 HTML 文件中的图片链接。

可以被这些引用的都算一个模块 chunk

Webpack 天生支持如下模块类型:

  • ECMAScript 模块
  • CommonJS 模块
  • AMD 模块
  • Assets
  • WebAssembly 模块

当遇到不支持的,可以编写 loader 使其支持。也就是说通过 loader 可以使 webpack 支持多种语言和预处理器语法编写的模块。
比如:

  • TypeScript【ts-loader】
  • Less/Sass【less-loader】
  • ES6【babel-loader】
  • Vue【vue-loader】
  • https://webpack.docschina.org/loaders

入口(entry)

入口默认是 ./src/index.js,可以在 entry 配置

输出(output)

默认值是 ./dist/main.js

loader

webpack 默认只能理解 JavaScript 和 JSON 文件,当需要处理其他文件时可以用loader。

在 webpack 的配置中,loader 有两个属性:

  • test 属性,识别出哪些文件会被转换。
  • use 属性,定义出在进行转换时,应该使用哪个 loader。
// webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下。
module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

loader 支持链式调用,数组里的 loader 反向执行从后往前,将其结果(也就是应用过转换后的资源)传递给下一个 loader。

开发loader

module.exports = function (content) {
  console.log(content);
  return content + '123';
};

插件(plugin)

简介

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。

流程

webpack 的整体流程可以分为 env > init > compiler > make > seal > emit > done

env 和 init 阶段负责初始化环境和激活 webpack 内部插件
compiler 阶段是编译的开始
make 阶段 webpack 会进行依赖分析和收集
seal 阶段 webpack 会生成 chunk 文件
emit 阶段 webpack 会把 chunks 写入硬盘
done 阶段,顾名思义就会 webpack 工作完成啦

插件开发:

https://webpack.docschina.org/api/plugins

Compiler 模块是 webpack 的主要引擎,它通过 CLI 或者 Node API 传递的所有选项创建出一个 compilation 实例。 它扩展(extends)自 Tapable 类,用来注册和调用插件。 大多数面向用户的插件会首先在 Compiler 上注册。https://webpack.docschina.org/api/compiler-hooks/#hooks

Tapable类

这个小型库是 webpack 的一个核心工具,提供类似的插件接口,也是一个 npm 包。有点像一个发布订阅的工具。

在 webpack 中的许多对象都扩展自 Tapable 类。 它对外暴露了 tap,tapAsync 和 tapPromise 等方法, 插件可以使用这些方法向 webpack 中注入自定义构建的步骤

https://github.com/webpack/tapable

监听(watching)

监听时,compiler 会触发诸如 watchRun, watchClose 和 invalid 等额外的事件。 通常在 开发环境 中使用, 也常常会在 webpack-dev-server 这些工具的底层调用, 由此开发人员无须每次都使用手动方式重新编译。

compiler 钩子

以下生命周期钩子函数,是由 compiler 暴露

compiler.hooks.someHook.tap('MyPlugin', (params) => {
  /* ... */
});

tap 不止是 tap,也可以在某些钩子上访问 tapAsync 和 tapPromise,具体看Tapable。

someHook 可以是以下钩子函数:
1、environment:在编译器准备环境时调用,时机就在配置文件中初始化插件之后。
2、afterEnvironment:当编译器环境设置完成后,在 environment hook 后直接调用。
3、entryOption:在 webpack 选项中的 entry 被处理过之后调用。

compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
  /* ... */
});

4、afterPlugins:在初始化内部插件集合完成设置之后调用。
5、initialize:当编译器对象被初始化时调用。
6、beforeRun:在开始执行一次构建之前调用,compiler.run 方法开始执行后立刻进行调用。
7、run:在开始读取 records 之前调用。
8、watchRun:在监听模式下,一个新的 compilation 触发之后,但在 compilation 实际开始之前执行。
9、compile:beforeCompile 之后立即调用,但在一个新的 compilation 创建之前。这个钩子 不会 被复制到子编译器。
10、compilation:compilation 创建之后执行。
11、emit:输出 asset 到 output 目录之前执行。这个钩子 不会 被复制到子编译器。
12、done:在 compilation 完成时执行。这个钩子 不会 被复制到子编译器。
13、failed:在 compilation 失败时调用。
14、shutdown:当编译器关闭时调用。

完整钩子:https://webpack.docschina.org/api/compiler-hooks/#hooks

compilation 钩子

compiler 创建的 compilation 实例。在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。

钩子函数:
1、buildModule:在模块构建开始之前触发,可以用来修改模块。

compilation.hooks.buildModule.tap(
  'SourceMapDevToolModuleOptionsPlugin',
  (module) => {
    module.useSourceMap = true;
  }
);

2、succeedModule:模块构建成功时执行。
3、optimize:优化阶段开始时触发。
4、optimizeChunks:在 chunk 优化阶段开始时调用。插件可以 tap 此钩子对 chunk 执行优化。

完整钩子:https://webpack.docschina.org/api/compilation-hooks/

compiler和compilation

Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。

webpack 编译会创建两个核心对象:

  • compiler :包含了 webpack 环境所有的 配置信息,包括了 options,loader,plugin,和 webpack 整个生命周期有关的钩子。

  • compilation: 作为 plugin 内置事件回调函数的参数,包含了当前的 模块资源,编译生成资源,变化的文件 以及 被 跟踪的文件 的状态信息,当检测到了一个文件发生了改变的时候,就会生成一个新的 Compilation 对象。

  • Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;

  • Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

创建自定义 插件

  • 插件必须是一个函数或者是一个包含了 apply 方法的对象,这样才能够访问 Compiler 对象。

  • 传给每一个插件的 compiler 和 compilation 对象都是同一个应用,因此不建议修改。

  • 异步的事件需要在插件处理完任务和调用回调函数通知 webpack 进入下一个流程,不然会卡住。

class MyPlugin {
    // Webpack 会调用 MyPlugin 实例的 apply 方法给插件实例传入 compiler 对象
  apply (compiler) {
    // 找到合适的事件钩子,实现自己的插件功能
    compiler.hooks.emit.tap('MyPlugin', compilation => {
        // compilation: 当前打包构建流程的上下文
        console.log(compilation);
        
        // do something...
    })
  }
}

在 emit 事件发生的时候,代表源文件的转换和组装已经完成,可以读取到最终将输出的 资源,代码块,模块,依赖,并且可以修改输出资源的内容。

loader和plugins区别

  • loader运行在项目打包之前;
  • plugins运行在整个项目的编译时期;

loader 即为文件加载器,操作的是文件,将文件A通过loader转换成文件B,是一个单纯的文件转化过程。比如将 A.less 转换成 B.css

plugins 基于事件机制工作,监听webpack打包过程中的某些节点,执行广泛的任务。

模式(mode)

通过选择 development, production 或 none 之中的一个

你可能感兴趣的:(#,前端工程化,webpack)