前端模块化开发那点历史

转载自:https://github.com/seajs/seajs/issues/588  感谢原作者分析与共享。

最近不断有人问及,想起前些天跟 @dexteryy 等人的讨论:dexteryy/OzJS#10 当时有过简单总结,重新梳理如下。

写在前面

  1. 不谈什么:传统的模块化开发方式,比如文件拆分、全局变量、命名空间,以及 YUI3 式的模块化开发方式。有兴趣的可阅读:#547
  2. 谈什么: 关于 CommonJS、AMD、Node.js、CMD 等相关的故事与未来趋势,很有意思。
  3. 不一定精准:本文是基于史实的扯淡,因此部分文字特别是时间都是模糊记忆,不一定精准。关于流派、趋势则是个人在社区的感受,不代表客观看法。(看法都是主观的,呵呵)

CommonJS 社区

大概 09 年 - 10 年期间,CommonJS 社区大牛云集。CommonJS 原来叫 ServerJS,推出 Modules/1.0 规范后,在 Node.js 等环境下取得了很不错的实践。

09年下半年这帮充满干劲的小伙子们想把 ServerJS 的成功经验进一步推广到浏览器端,于是将社区改名叫 CommonJS,同时激烈争论 Modules 的下一版规范。分歧和冲突由此诞生,逐步形成了三大流派:

  1. Modules/1.x 流派。这个观点觉得 1.x 规范已经够用,只要移植到浏览器端就好。要做的是新增 Modules/Transport 规范,即在浏览器上运行前,先通过转换工具将模块转换为符合 Transport 规范的代码。主流代表是服务端的开发人员。现在值得关注的有两个实现:越来越火的 component 和走在前沿的 es6 module transpiler
  2. Modules/Async 流派。这个观点觉得浏览器有自身的特征,不应该直接用 Modules/1.x 规范。这个观点下的典型代表是 AMD 规范及其实现 RequireJS。这个稍后再细说。
  3. Modules/2.0 流派。这个观点觉得浏览器有自身的特征,不应该直接用 Modules/1.x 规范,但应该尽可能与 Modules/1.x 规范保持一致。这个观点下的典型代表是 BravoJS 和 FlyScript 的作者。BravoJS 作者对 CommonJS 的社区的贡献很大,这份 Modules/2.0-draft 规范花了很多心思。FlyScript 的作者提出了 Modules/Wrappings 规范,这规范是 CMD 规范的前身。可惜的是 BravoJS 太学院派,FlyScript 后来做了自我阉割,将整个网站(flyscript.org)下线了。这个故事有点悲壮,下文细说。

AMD 与 RequireJS

再来说 AMD 规范。真正的 AMD 规范在这里:Modules/AsynchronousDefinition。AMD 规范一直没有被 CommonJS 社区认同,核心争议点如下:

执行时机有异议

看代码

Modules/1.0:

var a = require("./a") // 执行到此处时,a.js 才同步下载并执行

AMD:

define(["require"], function(require) {
  // 在这里,模块 a 已经下载并执行好
  // ...
  var a = require("./a") // 此处仅仅是取模块 a 的 exports

})

AMD 里提前下载 a.js 是浏览器的限制,没办法做到同步下载,这个社区都认可。

但执行,AMD 里是 Early Executing,Modules/1.0 里是第一次 require 时才执行。这个差异很多人不能接受,包括持 Modules/2.0 观点的也不能接受。

这个差异,也导致实质上 Node 的模块与 AMD 模块是无法共享的,存在潜在冲突。

模块书写风格有争议

AMD 风格下,通过参数传入依赖模块,破坏了 就近声明 原则。比如:

define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {

    // 等于在最前面申明并初始化了要用到的所有模块

   if (false) {
       // 即便压根儿没用到某个模块 b,但 b 还是提前执行了
       b.foo()
   }

})

还有就是 AMD 下 require 的用法,以及增加了全局变量 define 等细节,当时在社区被很多人不认可。

最后,AMD 从 CommonJS 社区独立了出去,单独成为了 AMD 社区。有阵子,CommonJS 社区还要求 RequireJS 的文档里,不能再打 CommonJS 的旗帜(这个 CommonJS 社区做得有点小气)。

脱离了 CommonJS 社区的 AMD 规范,实质上演化成了 RequireJS 的附属品。比如

  1. AMD 规范里增加了对 Simplified CommonJS Wrapper 格式的支持。这个背后是因为 RequireJS 社区有很多人反馈想用 require 的方式,最后 RequireJS 作者妥协,才有了这个半残的 CJS 格式支持。(注意这个是伪支持,背后依旧是 AMD 的运行逻辑,比如提前执行。)
  2. AMD 规范的演进,离不开 RequireJS。这有点像 IE…… 可能是我的偏见。

AMD 的流行,很大程度上取决于 RequireJS 作者的推广,这有点像 less 因 Bootstrap 而火起来一样。但火起来的东西未必好,比如个人觉得 stylus 就比 less 更优雅好用。

关于 AMD 和 RequireJS,暂且按下不表。来看另一条暗流:Modules/2.0 流派。

Modules/2.0

BravoJS 的作者 Wes Garland 有很深厚的程序功底,在 CommonJS 社区也非常受人尊敬。但 BravoJS 本身非常学院派,是为了论证 Modules/2.0-draft 规范而写的一个项目。学院派的 BravoJS 在实用派的 RequireJS 面前不堪一击,现在基本上只留存了一些美好的回忆。

这时,Modules/2.0 阵营也有一个实战派:FlyScript。FlyScript 抛去了 Modules/2.0 中的学究气,提出了非常简洁的 Modules/Wrappings 规范:

module.declare(function(require, exports, module)
{
   var a = require("a"); 
   exports.foo = a.name; 
});

这个简洁的规范考虑了浏览器的特殊性,同时也尽可能兼容了 Modules/1.0 规范。悲催的是,FlyScript 在推出正式版和官网之后,RequireJS 当时正直红火。期间 FlyScript 作者 khs4473 和 RequireJS 作者 James Burke 有过一些争论。再后来,FlyScript 作者做了自我阉割,将 GitHub 上的项目和官网都清空了,官网上当时留了一句话,模糊中记得是

我会回来的,带着更好的东西。

这中间究竟发生了什么,不得而知。后来我有发邮件给 @khs4473 询问,khs 给了两点挺让我尊重的理由,大意是

  1. 我并非前端出身,RequireJS 的作者 James Burke 比我更懂浏览器。
  2. 我们应该协同起来推动一个社区的发展,即便它不是你喜欢的。

这两句话对我影响很大。也是那之后,开始仔细研究 RequireJS,并通过邮件等方式给 RequireJS 提出过不少建议。

再后来,在实际使用 RequireJS 的过程中,遇到了很多坑。那时 RequireJS 虽然很火,但真不够完善。期间也在寻思着 FlyScript 离开时的那句话:“我会回来的,带着更好的东西”

我没 FlyScript 的作者那么伟大,在不断给 RequireJS 提建议,但不断不被采纳后,开始萌生了自己写一个 loader 的念头。

这就是 Sea.js。

Sea.js 借鉴了 RequireJS 的不少东西,比如将 FlyScript 中的 module.declare 改名为 define 等。Sea.js 更多地来自 Modules/2.0 的观点,但尽可能去掉了学院派的东西,加入了不少实战派的理念。

最后

写着写着,有点沧桑感,不想写了。

历史不是过去,历史正在上演。随着 W3C 等规范、以及浏览器的飞速发展,前端的模块化开发会逐步成为基础设施。一切终究都会成为历史,未来会更好。

<think>嗯,用户想了解前端模块化开发和控制流管理的最佳实践,包括工具、框架和实现方式。首先,我需要回顾一下前端模块化的发展历程,比如从IIFE到CommonJS,再到ES6模块化。这部分要确保历史背景准确,可能需要引用一些资料。 然后是模块化工具和框架,比如Webpack、Rollup这些打包工具,以及React、Vue这些框架的模块化支持。需要提到具体的工具名称和它们的作用,比如Tree Shaking、代码分割等优化手段。还要注意引用已有的资料,比如用户提供的引用[1]提到了React.js和Vue.js,可以结合这些框架的模块化特性来展开。 接下来是控制流管理,这部分可能需要解释什么是控制流,以及为什么在前端需要管理它。传统回调的问题引出Promise和async/await,然后提到状态管理库如Redux、Vuex、Pinia。需要说明它们各自的优缺和适用场景,比如Redux适合复杂应用,而Pinia更轻量。引用[2]提到了Vitest,但可能和测试相关,暂时不太相关,但如果有测试控制流的部分可以提及。 在最佳实践部分,需要分模块化和控制流两个方面。模块化方面强调按功能划分、依赖管理、代码拆分;控制流方面推荐使用现代异步语法和状态管理工具。可能还需要提到工程化实践,比如代码规范、测试(这里可以引用引用[2]的Vitest作为测试工具的例子)。 相关问题部分,用户可能需要了解模块化与组件化的区别、状态管理库的选择、异步处理的最佳方式,以及如何优化模块化项目。这些问题要确保覆盖用户可能感兴趣的扩展,并且引导他们深入探索。 最后,检查引用是否正确应用。比如引用[1]中的前端框架列表可以用来支持模块化工具部分,引用[3]可能作为资源参考。需要确保引用标记正确,没有遗漏或错误。</think>### 前端模块化开发与控制流管理实践指南 #### 一、模块化开发实现方式 1. **模块化演进过程** - **IIFE模式**:通过匿名函数隔离作用域(例:$(function(){...})()$) - **CommonJS**:Node.js 的同步加载规范(例:$require('module')$) - **AMD/CMD**:浏览器端异步加载方案(Require.js/Sea.js) - **ES Modules**:现代浏览器原生支持的模块标准(例:$import { func } from './module.js'$) 2. **模块化工具链** - **打包工具**:Webpack(支持代码分割)、Rollup(库打包优化)、Vite(基于ESM的极速构建) - **优化手段**:Tree Shaking(消除无效代码)、Scope Hoisting(作用域提升) 3. **框架级模块化** - React组件化:通过$export default function Component() {...}$导出 - Vue单文件组件:`<template>`,`<script>`,`<style>`三部分封装 - Angular模块系统:@NgModule装饰器管理依赖[^1] #### 二、控制流管理方案 1. **异步控制演进** - **回调函数**:易产生回调地狱(`$.ajax(url, function(res){...})`) - **Promise链**:`.then().catch()`链式调用 - **Async/Await**:同步化异步编程(`const data = await fetchData()`) 2. **状态管理工具** - **React生态**:Redux(单一状态树)+ Redux Toolkit简化版 - **Vue生态**:Vuex(集中式存储)与Pinia(组合式API) - **通用方案**:MobX(响应式状态管理) 3. **数据流设计模式** - 单向数据流:父->子组件props传递 - 件总线:全局EventEmitter通信 - CQRS模式:分离命令与查询操作 #### 三、最佳实践建议 1. **模块化规范** - 按功能划分模块边界(高内聚低耦合) - 显式声明依赖关系 - 使用动态导入实现代码分割:`import('./module.js')` 2. **控制流优化** ```javascript // 错误处理最佳实践 async function loadData() { try { const res = await fetch('/api/data'); return await res.json(); } catch (error) { console.error('加载失败:', error); return fallbackData; } } ``` 3. **工程化实践** - 统一代码规范(ESLint + Prettier) - 自动化测试(单元测试使用Vitest[^2]) - 模块文档化(JSDoc/TypeDoc) #### 四、推荐工具组合 1. **基础组合**:Webpack + Babel + ESLint 2. **现代方案**:Vite + TypeScript + Pinia 3. **全栈方案**:Next.js/Nuxt.js服务端渲染框架
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值