Skip to content

Webpack  #50

Open
Open
@myLightLin

Description

@myLightLin

Webpack 是一个 JavaScript 及其周边的模块打包工具。它所做的工作其实很简单:将项目中的资源文件进行打包,生成一个大的 bundle 文件,然后交由服务器返回给客户端解析展现。

为什么需要 Webpack

在以前,传统的 Web 开发页面都是 DOM 直出的,客户端发起请求,服务器进行一系列逻辑处理,返回 HTML 和 CSS 资源文件。这种模式就是每次请求都要刷新一次页面来获取最新的资源文件,体验并不好。后来出现了 单页面应用(SPA),它允许我们在不刷新的情况下更新页面获取新资源,单页面应用的主要技术就是 Ajax,它的特点就是自始至终只有一个 URL,很多处理工作都是在前端完成的,也就是所谓前后端分离。这里顺便提下,由于单页面一个 url ,意味着很难感知用户的操作行为,为此又有了 前端路由 的解决方案。具体可以参考 这篇

前面说了,单页面应用很多工作都是在前端完成的,具体来说就是前端开始往工程化发展了,我们需要承担一些以前在服务端才处理的逻辑,这样项目就开始庞大起来了,这就意味着需要划分模块,而 JS 在模块化这方面支持不够,所以才涌现出了诸如立即执行函数,AMD,CMD,UMD,ES6 module 等模块化解决方案。可以说,这些模块化方案解决了代码如何组织的问题,但是它们没有解决我们项目多个模块之间的处理引入问题。于是 Webpack ,Gulp 这种模块化打包工具就出现了,它们解决了以下问题:

  • 针对不同模块标准所带来的环境兼容问题
  • 由于项目细分了很多模块,意味着网络文件请求增多,需要解决效率问题
  • 不仅 JS 代码可以模块化,HTML ,CSS 和图片资源也可以模块化

Loader 机制

Loader 简单来说就是针对不同类型资源的一个加载器,因为 webpack 默认是把所有文件当做 JS 来解析处理的,假如你需要处理 CSS 或其他资源,那就需要有辅助工具来做这项工作,loader 就是干这个的,比如针对 css,有 css-loaderstyle-loader;针对 sass,有 sass-loader。针对 ES6 转 ES5 代码,有 babel-laoder 等等。

loader 是跑在 Node.js 上的,如果你要实现一个 loader,例如 function myLoader(source) {},它会接收源码内容作为参数,然后在里面做你自定义的转换操作,一个 loader 最终必须返回 JavaScript 代码,还可以返回第二个参数,是一个可选的 source map 对象。

export default function myLoader(source) {
  source.replace('/\s/', '')

  return `export default ${JSON.stringify(source)}`
}

官方还给出了写 loader 的几个指导原则:

  • 保持简洁
  • 利用 chaining 机制
  • 返回模块化输出
  • 确保无状态 stateless
  • 使用 loader 的加载工具
  • 标记 loader 依赖
  • 解析模块依赖
  • 提取公共代码
  • 避免绝对路径
  • 使用 peer dependencies

plugin 机制

插件是 webpack 提供的另一种能力,可以让我们方便地做一些自动化处理操作。比如:

  • 打包构建前清空 dist 目录
  • 自动生成根 html 文件
  • 压缩打包的文件
  • 将静态目录下的资源文件复制到 dist 中

插件的原理:钩子。webpack 会在打包构建的过程每个环节预留一些钩子,在这些钩子里就可以执行我们自定义的逻辑了。

class Test {
  apply (compiler) {
    compiler.hooks.emit.tap('函数', compilation => {
      // 执行逻辑
    })
  }
}

webpack 打包的过程

初始化阶段

  • 由 Webpack CLI 启动打包流程,合并命令行参数和用户的配置文件
  • 载入 Webpack 核心模块,创建 Compiler 对象
  • 初始化编译环境:注入内置插件,注册各种模块工厂等

构建阶段:

  • 执行 Compiler 对象的 run 方法,创建 Compilation 对象
  • 进入 make 阶段,调用 Compilation.addEntry 方法,开始解析入口
  • 通过 Compilation 对象的 buildModule 方法进行模块构建,这个过程中,对模块调用各种 loader 进行转译处理,使用 JS 解释器 Acorn 将模块代码转换为 AST 语法树,根据 AST 判断是否还有依赖的模块,有则继续循环 build 整个模块

生成阶段:

  • 合并生成需要输出的 bundle.js 写入到 dist 目录

与 Vite 的对比

在开发阶段:
Webpack 的思路是先打包整个应用文件,然后生成 bundle 文件,再启动开发服务器。当有文件修改时,有 HMR 热替换机制,需要找到对应模块的依赖,然后依次重新编译,如果随着文件越来越多的话也需要很长时间重新编译。

而 Vite 的话,它分为依赖和源码两大模块,思路是先分析,使用 ESBuild 预构建依赖,把 CommonJS/UMD 转换为 ESM,还会使用 Cache-Control 进行强缓存请求。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions