美文网首页
Webpack 设计理念

Webpack 设计理念

作者: BingJS | 来源:发表于2023-03-22 18:38 被阅读0次

一、前言

Webpack 一直都是有些人的心魔,不清楚原理是什么,不知道怎么去配置,只会基本的 API 使用。它就像一个黑盒,让部分开发者对它望而生畏。
大家之所以认为 Webpack 复杂,很大程度上是因为它依附着一套庞大的生态系统。其实 Webpack 的核心流程远没有我们想象中那么复杂,甚至只需百来行代码就能完整复刻出来。
因此在学习过程中,我们应注重学习它本身的设计思想,不管是它的 Plugin 系统还是 Loader 系统,都是建立于这套核心思想之上。所谓万变不离其宗,一通百通。

二、基本使用

初始化项目:

npm init  //初始化一个项目
yarn add webpack //安装项目依赖

安装完依赖后,根据以下目录结构来添加对应的目录和文件:

├── node_modules
├── package-lock.json
├── package.json
├── webpack.config.js #配置文件
├── debugger.js #测试文件
└── src # 源码目录
     |── index.js
     |── name.js
     └── age.js

webpack.config.js

const path = require("path");
module.exports = {
  mode: "development", //防止代码被压缩
  entry: "./src/index.js", //入口文件
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].js",
  },
  devtool: "source-map", //防止干扰源文件
}; 

src/index.js

const name = require("./name");
const age = require("./age");
console.log("entry文件打印作者信息", name, age);

src/name.js

module.exports = "不要秃头啊";

src/age.js

module.exports = "99";

文件依赖关系:
入口文件 src/index.js
依赖文件 src/name.js src/age.js
Webpack 本质上是一个函数,它接受一个配置信息作为参数,执行后返回一个 compiler 对象,调用 compiler 对象中的 run 方法就会启动编译。run 方法接受一个回调,可以用来查看编译过程中的错误信息或编译信息。
debugger.js

// const { webpack } = require("./webpack.js"); //后面自己手写
const { webpack } = require("webpack");
const webpackOptions = require("./webpack.config.js");
const compiler = webpack(webpackOptions);

//开始编译
compiler.run((err, stats) => {
  console.log(err);
  console.log(
    stats.toJson({
      assets: true, //打印本次编译产出的资源
      chunks: true, //打印本次编译产出的代码块
      modules: true, //打印本次编译产出的模块
    })
  );
});

执行打包命令:

node ./debugger.js

得到产出文件 dist/main.js
运行该文件,得到结果:

entry文件打印作者信息 不要秃头啊 99

三、核心思想

我们先来分析一下源代码和构建产物之间的关系:
入口文件(src/index.js)被包裹在最后的立即执行函数中,而它所依赖的模块(src/name.js、src/age.js)则被放进了 modules 对象中(modules 用于存放入口文件的依赖模块,key 值为依赖模块路径,value 值为依赖模块源代码)。
require 函数是 web 环境下 加载模块的方法( require 原本是 node环境 中内置的方法,浏览器并不认识 require,所以这里需要手动实现一下),它接受模块的路径为参数,返回模块导出的内容。
要想弄清楚 Webpack 原理,那么核心问题就变成了:如何将左边的源代码转换成 dist/main.js 文件?

核心思想:

  • 第一步:首先,根据配置信息(webpack.config.js)找到入口文件(src/index.js)
  • 第二步:找到入口文件所依赖的模块,并收集关键信息:比如路径、源代码、它所依赖的模块等:
var modules = [
{
  id: "./src/name.js",//路径
  dependencies: [], //所依赖的模块
  source: 'module.exports = "不要秃头啊";', //源代码
},
{
  id: "./src/age.js",
  dependencies: [], 
  source: 'module.exports = "99";',
},
{
  id: "./src/index.js",
  dependencies: ["./src/name.js", "./src/age.js"], 
  source:
    'const name = require("./src/name.js");\n' +
    'const age = require("./src/age.js");\n' +
    'console.log("entry文件打印作者信息", name, age);',
},
];
  • 第三步:根据上一步得到的信息,生成最终输出到硬盘中的文件(dist): 包括 modules 对象、require 模版代码、入口执行文件等

在这过程中,由于浏览器并不认识除 html、js、css 以外的文件格式,所以我们还需要对源文件进行转换 —— Loader 系统
Loader 系统 本质上就是接收资源文件,并对其进行转换,最终输出转换后的文件:
除此之外,打包过程中也有一些特定的时机需要处理,比如:

  • 在打包前需要校验用户传过来的参数,判断格式是否符合要求
  • 在打包过程中,需要知道哪些模块可以忽略编译,直接引用 cdn 链接
  • 在编译完成后,需要将输出的内容插入到 html 文件中
  • 在输出到硬盘前,需要先清空 dist 文件夹

这个时候需要一个可插拔的设计,方便给社区提供可扩展的接口 —— Plugin 系统
Plugin 系统 本质上就是一种事件流的机制,到了固定的时间节点就广播特定的事件,用户可以在事件内执行特定的逻辑,类似于生命周期:
打包前的生命周期 => 打包过程中的生命周期 => 打包成功的生命周期 => 打包失败的生命周期
这些设计也都是根据使用场景来的,只有理清需求后我们才能更好的理解它的设计思想。

四、架构设计

在理清楚核心思想后,剩下的就是对其进行一步步拆解。
上面提到,我们需要建立一套事件流的机制来管控整个打包过程,大致可以分为三个阶段:

  • 打包开始前的准备工作
  • 打包过程中(也就是编译阶段)
  • 打包结束后(包含打包成功和打包失败)

这其中又以编译阶段最为复杂,另外还考虑到一个场景:watch mode(当文件变化时,将重新进行编译),因此这里最好将编译阶段(也就是下文中的compilation)单独解耦出来。

在 Webpack 源码中,compiler 就像是一个大管家,它就代表上面说的三个阶段,在它上面挂载着各种生命周期函数,而 compilation 就像专管伙食的厨师,专门负责编译相关的工作,也就是打包过程中这个阶段。画个图帮助大家理解:

相关文章

  • 设计理念

    很多顾客都会说,你看着给我剪吧,看着给我设计吧,你是专业的,怎么好看怎么给我剪吧! 但是我要说一句,设计=今天我给...

  • 设计理念

    昨天同事要写一个设计理念,有时候是真的没理念的,非要说理念,那么抽象的东西转化成文字经常就结结巴巴写不好。 同事找...

  • 设计理念

    大繁至简!挖掘事物的本质,即找到事物结构最简单的基本单元,这就是化繁为简的方法! 复杂的人性,复杂的产...

  • 【webpack进阶】你真的掌握了loader么?- loade

    往期文章:【webpack进阶】前端运行时的模块化设计与实现【webpack进阶】使用babel避免webpack...

  • 【webpack进阶】可视化展示webpack内部插件与钩子关系

    往期文章:【webpack进阶】前端运行时的模块化设计与实现【webpack进阶】使用babel避免webpack...

  • Clojure编程乐趣/Clojure之道

    Clojure设计理念

  • Skeumorphic设计理念

    Skeumorphic设计理念 苹果公司的目前的设计倾向就是Skeumorphic理念(比如 iBooks,iCa...

  • 设计理念(一)

    换个头像。 2018-6-3

  • netty设计理念

    一、初步了解Netty 有一套统一的API来处理异步和同步编程模式 使用非常灵活 简单但却强大的线程机制 业务组件...

  • 秒杀设计理念

    秒杀系统场景特点 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。秒杀一般是访问请求数量远远大于库存...

网友评论

      本文标题:Webpack 设计理念

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