开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情
最近看到了一篇讲述前端模块化历史的文章,讲的非常好,其中提到了我的一位老朋友 browserify,在初学前端的时候,一度在 CSDN 的垃圾桶中畅游,其中有一篇文章极力推荐我使用 browserify,但当初我连 Node.js 的环境都不知道怎么装,所以对 browserify 就不了了之了,而今天这篇文章就是来复古学习一下 browserify
实现例子如下,点击实现值的平方(基于初始值),在线查看源代码
browserify 是?
browserify 的作用就是打包工具,又或者说是 require 模块的编译器,为什么这么说呢?这是因为服务端的模块化要早于浏览器的模块化,在 Node.js 诞生的 2009 年,浏览器上运行的 JavaScript 没有自己的模块化方案,而随着浏览器的功能的强大,Ajax 的出现,网站的交互能力越发重要,JavaScript 越来越复杂,传统的前端项目难以维护项目中的 JS
前端暴露问题
比如
- 没有包管理器,开源的工具或者框架总是东一个西一个,到处乱找
- 没有模块化,所有的文件都写在一个
.js文件中,导致几千行的脚本文件,维护困难 - 使用
<script>引入UI框架或者工具,由于脚本太多导致网络瓶颈,那个时候浏览器限制文件并发8个以内,这样就会导致一些静态资源无法得到响应
珠玉在前
对于第一点,在服务端的 Node.js 上先实现了,Node.js 出现前和 npm 的作者达成一致,并采用 CommonJS 的模块标准,先一步实现了包管理和对应的模块化,当时这套方案在服务端反响不错,所以就有开发者想要将这一套搬到浏览器,但是 ComoonJS 只是服务端的模块化标准并不适用于浏览器,会有一些问题,比如
在服务端
require一个模块,只会有磁盘 I/O,所以同步加载机制没什么问题;但如果是浏览器加载,一是会产生开销更大的网络 I/O,二是天然异步,就会产生时序上的错误。
// Node.js 行
const $ = require("jquery");
const _ = require("lodash");
const { square } = require("./utils/math");
// browser 不行
const $ = require("https://cdn.jsdelivr.net/npm/jquery@3.6.3/dist/jquery.min.js");
const _ = require("https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js");
const { square } = require("./utils/math");
当时就有了一些讨论和解决方案,最终产生了三种方案,比如 AMD 和文章讲述的 browserify,就是基于 CommonJS 的改进
解决方案
browserify 的方案就是源代码开发阶段采用 Node.js 的 CommonJS 标准,而在部署,找到对应的模块,打包成一个文件,保证调用顺序
至此前面关于“前端暴露问题”的第二点、第三点已经解决,模块化解决第二点、而打包解决第三点,同时异步顺序也能够解决,一石四鸟呀!
browserify 使用
目录结构
├── node_modules
├── dist - 打包文件
├── src - 项目代码
| ├── utils - 工具模块
| └── math.js
| └── index.js - 入口文件
└── index.html
模块导入导出
沿用 Node.js 那一套(有种写 express 的感觉)
// main.js - 导入
const $ = require("jquery");
const { square } = require("./utils/math");
const initialVal = 2;
(function () {
const countDOM = $(".count");
const count = $(".count").text();
console.log(count);
if (!count) {
countDOM.text(initialVal);
}
})();
$(".count-button").on("click", () => {
const countDOM = $(".count");
const count = $(".count").text();
countDOM.text(square(Number(count)));
});
// math.js
/**
* 计算 x 的平方
* @param {number} x
*/
const square = (x) => {
return x * x;
};
module.exports = {
square,
};
打包
假设你已经了解过 Node.js 环境和 package.json
npm install --save-dev browserify
执行打包命令
npx browserify src/index.js > dist/boudle.js
# 也可以在 package.json 自定义命令
npm run build
打包文件引用
在 HTML 中引用打包后的文件
<head>
<script src="dist/boudle.js" async></script>
</head>
<body>
<div>
count: <span class="count"></span>
<button class="count-button">count = count^2</button>
</div>
</body>
注意,每调用 browserify 命令时才会更新一次打包文件,对于文件热更新需要自行配置
静态资源服务器的提供就介绍了,用 IDE 或者 Node.js 的 express 都行,此处我使用 VS Code 的插件 Live Server
效果如下
打包文件将会是一个 IIFE -> IIFE,会有相应的层级和依赖关系(实现细节不过多分析)