这一小节是第二章的展望部分,朴灵作者在这里讨论了一个理想愿景:同一个JavaScript模块代码,能同时运行在Node.js(后端)和浏览器(前端)。这在2013年书出版时是个热门话题,因为Node用CommonJS让JS有了强大模块系统,前端却还在用
原书这一小节的子结构:
- 2.7.1 模块的侧重点
- 2.7.2 AMD规范
- 2.7.3 CMD规范
- 2.7.4 兼容多种模块规范
详细讲解
2.7.1 模块的侧重点
- 后端(CommonJS):同步加载(require同步),因为文件在本地磁盘,速度快。适合服务器环境。
- 前端:不能同步加载!浏览器下载脚本会阻塞UI渲染,用户体验差。所以前端需要异步加载模块。
- 共用难点:环境差异(如window vs global、DOM vs fs)。理想是写一套代码,两端通用(isomorphic JavaScript)。
2.7.2 AMD规范(Asynchronous Module Definition)
- 前端主流异步模块规范,由RequireJS推动。
- 定义模块用define(),依赖前置:
define(['dep1', 'dep2'], function(dep1, dep2) { return { /* exports */ }; }); - 加载用require()异步。
- 优势:依赖提前声明,适合浏览器并行下载。
2.7.3 CMD规范(Common Module Definition)
- 玉伯(海豹)提出的,SeaJS实现。
- 类似CommonJS,但异步:
define(function(require, exports, module) { var dep1 = require('dep1'); // 依赖就近 exports.foo = 'bar'; }); - 依赖就近声明,更接近Node写法。
2.7.4 兼容多种模块规范
- 写通用模块时,用闭包检测环境,兼容CommonJS、AMD、CMD和全局:
(function(name, definition) { if (typeof exports === 'object') { // CommonJS (Node) module.exports = definition(); } else if (typeof define === 'function' && define.amd) { // AMD define(definition); } else if (typeof define === 'function' && define.cmd) { // CMD define(function(require, exports, module) { module.exports = definition(); }); } else { // 浏览器全局 window[name] = definition(); } })('hello', function() { return function() { console.log('hello world'); }; }); - 另外,工具如Browserify:把CommonJS模块打包成浏览器可用的bundle(模拟require)。
- 作者展望:未来工具会解决这些差异(现在确实如此:Webpack、Rollup、Vite等)。