Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

关于模块加载方式的纠结

  模块加载这个问题是几年前开始一直在争的话题了。从 AMD 纠结到 CommonJS,然后再纠结到 ES6。认真想想其实它们的设计初衷和使用场景应该都是不同的,或者说它们本来就是为了解决不同问题而诞生的,所以不应该存在哪个比较好的问题。
  这几种东西乍看之下都是在干一件事,那就是解决模块化的问题。但如果只是针对 JavaScript 模块化这个问题,我会毫不犹豫地选择 ES6。理由很简单,JavaScript 既然语言本身有提供,为何还要搞出其它乱七八糟的东西来?
  但实际场景中会针对「JavaScript 模块化」这种问题来考虑的业务几乎没有,总是有一大坨问题需要解决。其中一个最典型的问题就是「异步」,如何处理加载模块的异步问题?好吧,更纯粹地问,如何解决浏览器上从服务器加载 js 文件模块的异步问题?为了解决这个问题,AMD 出现了。从它的名字就可以看出它是对「异步」负责的。再看 CommonJS 呢?在异步方面它就不太负责了。虽然也有人使用各种黑魔法将其直接用于浏览器,反正我难以接受这种用法的。
  看到有人问 AMD 和 CommonJS 的差别时总是有人回答「统一加载」和「就近加载」。这个答案虽然已经烂大街了,但我很不满意这个回答。「就近加载」的概念涵盖了统一加载,比如我们把所有 require 定义到顶部就变成了统一加载。这个答案可能会让人误认为 CommonJS 比 AMD 好。如果只是纠结到这个层面那就说明完全没有理解模块加载。为何 AMD 无法「就近加载」?因为 AMD 不适合。仅仅是不适合而已,并不是不可以。我之前做的 predeclare.js 就是一个支持「就近加载」的 AMD (目前本博客程序在用)。
  CommonJS 和 AMD 更大的差异是在于 define 部分,而不是 require 部分。AMD 的 defined 是非常完整的 JS 语法,它只是调用一个名叫 define 的全局函数而已。这就意味着,只要引入一个库,这个库定义了 define 这个全局函数,AMD 模块就可以在浏览器上被正确定义。而 CommonJS 的模块定义方式是导出到一个全局属性上,并且把所有在模块中使用的局部变量不在全局定义,这要怎么实现?(不是说不能实现,而是需要的黑魔法太多,反正我是不会去造这个轮子了)
  CommonJS 最初本来就 Node 在用,而 Node 的模块是同步加载的,也就是说它只解决了最基本的「模块加载」的问题,而没有解决异步问题。所以它不适合在浏览器上直接使用。但其实现在直接在浏览器上引用零碎 JS 文件的用法并不多,大家都希望打包后加载。也就是说代码不会遇到异步问题,所以 CommonJS 可以用,可以后端同步加载并解析,最终生成一个打包后的 JS 文件。
  AMD 的写法确实比 CommonJS 要繁琐,所以如果通过「后端打包」把「异步」这个问题解决的话,CommonJS 还是挺有优势的。AMD 失去了「解决异步问题」的优势,它的 A 就没用了,剩下的 MD 和 CommonJS 比起来就逊色得多。
  最后来纠结一下 ES6 如何?ES6 的模块化加载其实和 CommonJS 是一个概念,只是 API 的设计理念不同而已,反正它们都没有解决异步问题。ES6 的模块加载从出生到现在无时无刻都在被喷。我也觉得它设计的不好(甚至还不如 CommonJS),但毕竟是原生提供的东西,只要不是恶心到受不了还是会作为 CommonJS 的替代方案使用的。
网名:
34.203.213.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^