2.7 前后端共用模块!

15 阅读2分钟

这一小节是第二章的展望部分,朴灵作者在这里讨论了一个理想愿景:同一个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等)。