前端构建工具进化史之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 的打包结果,并揭露了这种方式的弊端。

相关推荐
神仙别闹12 分钟前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
aPurpleBerry36 分钟前
JS常用数组方法 reduce filter find forEach
javascript
GIS程序媛—椰子1 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0011 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x1 小时前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
木舟10091 小时前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢2 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安2 小时前
前端第二次作业
前端·css·css3