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

相关推荐
gqkmiss10 分钟前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255023 小时前
前端常用算法集合
前端·算法
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2344 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1234 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~5 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语5 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport5 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap