webpack

Favori,

Webpack

图:Nguyen Nhut

功能

  • 代码转换 (loaders)
  • 文件优化,压缩代码,压缩图片
  • 代码分割,提取公共代码
  • 模块合并
  • 自动刷新

常见配置

  • Entry
  • output
  • mode
  • Module
  • Chunk
  • Loader
  • Plugin

原理/工作流程

  1. 参数解析:从配置文件和 shell 语句中读取与合并参数,得出最终的参数
  2. 找到入口文件:从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module,生成 map 对象
  3. 调用 Loader 编译文件,每找到一个 Module,就会根据配置的 Loader 去找出对应的转换规则
  4. 遍历 AST,收集依赖:对 Module 进行转换后,再解析出当前 Module 依赖的 Module
  5. 生成 Chunk: 这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk
  6. 输出文件:最后 Webpack 会把所有 Chunk 转换成文件输出
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
      { test: /\.js$/, use: "babel-loader" },
      {
        test: /\.css$/,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          { loader: "postcss-loader" },
        ],
      },
    ],
  },
};

Loader 工作流

  1. webpack.config.js 里配置了一个 模块 的 Loader;

  2. 遇到 相应模块 文件时,触发了 该模块的 loader;

  3. loader 接受了一个表示该 模块 文件内容的 source;

  4. loader 使用 webapck 提供的一系列 api 对 source 进行转换,得到一个 result;

  5. 将 result 返回或者传递给下一个 Loader,直到处理完毕。

Plugin

插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在编译过代码程中,会触发一系列 Tapable 钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当 webpack 构建的时候,插件注册的事件就会随着钩子的触发而执行了。

webpack 插件由以下组成:

  • 一个 JavaScript 命名函数。
  • 在插件函数的 prototype 上定义一个 apply 方法。
  • 指定一个绑定到 webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调

插件示例:

// 一个 JavaScript 命名函数。
function MyExampleWebpackPlugin() {}
// 在插件函数的 prototype 上定义一个 apply 方法。
MyExampleWebpackPlugin.prototype.apply = function (compiler) {
  // 指定一个挂载到 webpack 自身的事件钩子。
  compiler.plugin(
    "webpacksEventHook",
    function (compilation /* 处理 webpack 内部实例的特定数据。*/, callback) {
      console.log("This is an example plugin!!!");
      // 功能完成后调用 webpack 提供的回调。
      callback();
      复制代码;
    }
  );
};

webpack 如何优化前端性能

  1. 第三方库按需加载、路由懒加载
  2. 代码分割

提取第三方库「vendor」

依赖库分离「splitChunks」

 
optimization: {
  splitChunks: {
    chunks: "async", // 必须三选一: "initial" | "all"(推荐) | "async" (默认就是async)
    minSize: 30000, // 最小尺寸,30000
    minChunks: 1, // 最小 chunk ,默认1
    maxAsyncRequests: 5, // 最大异步请求数, 默认5
    maxInitialRequests : 3, // 最大初始化请求书,默认3
    automaticNameDelimiter: '~',// 打包分隔符
    name: function(){}, // 打包后的名称,此选项可接收 function
    cacheGroups:{ // 这里开始设置缓存的 chunks
        priority: 0, // 缓存组优先级
        vendor: { // key 为entry中定义的 入口名称
            chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是async)
            test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
            name: "vendor", // 要缓存的 分隔出来的 chunk 名称
            minSize: 30000,
            minChunks: 1,
            enforce: true,
            maxAsyncRequests: 5, // 最大异步请求数, 默认1
            maxInitialRequests : 3, // 最大初始化请求书,默认1
            reuseExistingChunk: true // 可设置是否重用该chunk
        }
    }
  }
}