前端构建工具进化史之browserify

背景

上一篇文章里面,我们简单介绍了浏览器端 AMD 模块化方案。这里我们再来看看当时存在的另外一种方案 CommonJs。CommonJs 最开始是用于 node 服务端的模块化方案,browserify 为了使 node 端的代码可以在浏览器运行,对 CommonJs 的 require 语法进行了打包处理。

browserify 简介

browserify 从名字就能看出来,这是由 browser 加后缀 ify 组成的单词,字面意思就是"使浏览器化"。把什么"使浏览器化"呢?其实就是node 代码。官网中有这么一句话

Browserify lets you require('modules') in the browser by bundling up all of your dependencies.

意思是,我们可以在浏览器端使用 node 的 require 方式编写代码,browserify 会将代码中的依赖进行打包,生成的代码就可以在浏览器运行了。

browserify 总体上来说,包括两个方面的作用:

1、使 node 特定的 api 能够通过 polyfills 的方式在浏览器运行,比如说 process.nextTick() 。

2、就是通过打包,使运用了 CommonJs 规范的代码能在浏览器端运行。

这里,我们主要看一下第二点,看看它是怎么做的?

打包

首先通过 pnpm add browserify -D 的方式安装依赖,然后在 scrpits 添加

json 复制代码
"build": "browserify index.js -o bundle.js"

新建几个测试文件

csharp 复制代码
add.js
subtract.js
calc.js
index.js

文件内容如下

js 复制代码
// add.js
module.exports = function add(a, b) {
    return a + b;
}

// subtract.js
module.exports = function sub(a, b) {
    return a - b;
}

// calc.js
const add = require('./add');
const sub = require('./subtract');

module.exports = (a, b) => {
    return add(a, b) * sub(a, b);
}

// index.js
const calc = require('./calc');

console.log(calc(1, 2));

内容比较简单,index.js 中会引入 calc.js,clac.js 中会引入 add.js 和 subtract.js。 运行 pnpm build 以后,生成了 bundle.js。

js 复制代码
// bundle.js
(function () {
  // 省略加载函数
})()(
  {
    1: [
      function (require, module, exports) {
        module.exports = function add(a, b) {
          return a + b;
        };
      },
      {},
    ],
    2: [
      function (require, module, exports) {
        const add = require("./add");
        const sub = require("./subtract");

        module.exports = (a, b) => {
          return add(a, b) * sub(a, b);
        };
      },
      { "./add": 1, "./subtract": 4 },
    ],
    3: [
      function (require, module, exports) {
        const calc = require("./calc");

        console.log(calc(1, 2));
      },
      { "./calc": 2 },
    ],
    4: [
      function (require, module, exports) {
        module.exports = function sub(a, b) {
          return a - b;
        };
      },
      {},
    ],
  },
  {},
  [3]
);

简单看一下,可以看出 browserify 将每一个模块使用函数进行了包裹,并变成了一个大对象的 value,通过这种方式将之前的 4 个文件打包变成了一个文件。

我们再来看一下早期的 webpack 怎么打包的,下面是 webpack version 1 的打包结果。

javascript 复制代码
(function (modules) {
  // 省略加载函数
})([
  /* 0 */
  function (module, exports, __webpack_require__) {
    const calc = __webpack_require__(1);

    console.log(calc(1, 2));
  },
  /* 1 */
  function (module, exports, __webpack_require__) {
    const add = __webpack_require__(2);
    const sub = __webpack_require__(3);

    module.exports = (a, b) => {
      return add(a, b) * sub(a, b);
    };
  },
  /* 2 */
  function (module, exports) {
    module.exports = function add(a, b) {
      return a + b;
    };
  },
  /* 3 */
  function (module, exports) {
    module.exports = function sub(a, b) {
      return a - b;
    };
  },
]);

可以看出 browserify 和早期的 webpack 的结果是比较类似的,webpack 将 browserify 中的大对象换成了数组。如果看文章比较多的话,会发现目前网上一些关于 webpack 的文章,依然是这种数组的形式。

番外

上面这两种打包结果都有一个不太被人关注的缺陷,就是会为每一个 module 模块包裹一个函数。这会造成两个问题。

1、性能问题。每一个 module 模块都会被函数包裹,意味着 javascript 代码运行的时候,需要为每一个模块创建一个新的函数作用域。这就意味着有更多的性能消耗。The cost of small modules 这篇文章专门探讨了这个问题,当模块数量在1000 个以上的时候,性能下降非常明显。

2、打包结果变大。这个就比较容易理解了,由于会为每一个模块包裹一个函数,那么如果将项目代码拆分的越细,模块就会越多,包裹的函数就会越多,导致最后的打包结果就会越大。

这两个问题其实就是 rich harris 在这篇采访中提到的问题,也是 rollup 诞生的背景。

所以,当有人问 webpack 和 rollup 有什么区别的时候,很多人会说 rollup 用来打包库,webpack 用来打包前端代码。但是为什么呢?原因就在这里。

后来随着打包工具的进化,webpack 在新版的时候借鉴了rollup 的功能,解决了这个问题。同时 rollup 也添加了代码分割这种前端项目需要的功能。

关于 webpack 和 rollup 的详细内容,我们后面再说吧。

总结

这里我们首先简单介绍了 browserify,然后使用 browserify 进行打包,分析了打包结果。最后对比了 browserify 和 早期 webpack 的打包结果,并揭露了这种方式的弊端。

相关推荐
我只会写Bug啊1 小时前
Vue文件预览终极方案:PNG/EXCEL/PDF/DOCX/OFD等10+格式一键渲染,开源即用!
前端·vue.js·pdf·excel·预览
扯蛋4383 小时前
LangChain的学习之路( 一 )
前端·langchain·mcp
Mr.Jessy3 小时前
Web APIs学习第一天:获取 DOM 对象
开发语言·前端·javascript·学习·html
午安~婉3 小时前
javaScript八股问题
开发语言·javascript·原型模式
西西学代码3 小时前
Flutter---个人信息(5)---持久化存储
java·javascript·flutter
芝麻开门-新起点3 小时前
flutter 生命周期管理:从 Widget 到 State 的完整解析
开发语言·javascript·ecmascript
ConardLi4 小时前
Easy Dataset 已经突破 11.5K Star,这次又带来多项功能更新!
前端·javascript·后端
冴羽4 小时前
10 个被严重低估的 JS 特性,直接少写 500 行代码
前端·javascript·性能优化
rising start4 小时前
四、CSS选择器(续)和三大特性
前端·css
一 乐4 小时前
高校后勤报修系统|物业管理|基于SprinBoot+vue的高校后勤报修系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·毕设