前端模块化开发那点历史 #588

Closed
lifesinger opened this Issue Mar 7, 2013 · 55 comments

Projects

None yet
@lifesinger
Member

最近不断有人问及,想起前些天跟 @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 等规范、以及浏览器的飞速发展,前端的模块化开发会逐步成为基础设施。一切终究都会成为历史,未来会更好。

@lifesinger lifesinger referenced this issue Mar 7, 2013
Closed

发布 Sea.js 2.0.0 #451

45 of 45 tasks complete
@lifesinger lifesinger closed this Mar 7, 2013
@hefangshi

sea.js的执行时间是在require的时候执行么?之前只是module ready?

@lifesinger
Member

@hefangshi

@mengzhiang

感谢玉伯为我们带来的Seajs。

@norfish
norfish commented Mar 7, 2013

读完这篇文章,也让我唏嘘不已,标准之争终究还是要进行下去

@hax
hax commented Mar 7, 2013

执行时机在ES6草案的讨论中也有争论。我个人倾向于静态链接,所以肯定支持early执行。

@moxuanyuan

很配服玉伯,不论代码,还是文字,都很了得

@LeoYuan LeoYuan referenced this issue in LeoYuan/leoyuan.github.io Apr 17, 2013
Open

好文章收藏 #5

@weishai
weishai commented Apr 19, 2013

star!

@anjorfu
anjorfu commented Apr 23, 2013

好文值得看,值得深思。

@realdah
realdah commented Jun 18, 2013

顶好文。。

看完最大的感觉就是, 可能lz是阳春白雪,而我们则是屌丝。。。
因为我们团队很多人喜欢less 胜过 SASS 和 sytlus, 喜欢原生javascript胜过 coffee script。

喜欢requirejs胜过 commonjs, 其实我最不喜欢的就是那个多余的exports了,要多烦有多烦。

可能因为是后台开发的背景而且做的是test driven development, 我很喜欢requirejs的一个特性,比方说
我想返回一个singleton,只需要

define(['a', 'b', 'c'], function(a,b,c) {   
    return new function() {
          this.someFunc = function() {}
     };
});

我只想返回一个构造体,则可以

define(['a', 'b', 'c'], function(a,b,c) {   
    var HelloService = function() {
          this.someFunc = function() {}
     };

   return HelloService;
});

如果语法当中非得来句exports, 我都要恶心的吐了。

其次, requirejs在支持minify,shim等等上面,至少在当时算做得最好的了吧? 这些在自动build上面都是很重要的。

@lifesinger
Member

exports 不是必须的哦,return 的写法,CMD 也支持。

@afc163
Member
afc163 commented Jun 19, 2013

为什么不喜欢 exports ,理解不能。

@realdah
realdah commented Jun 19, 2013

@lifesinger 是啊,不过个人感觉这就有点像javascript也可以不写分号, 而groovy也支持分号一样。。不是推荐使用。

requirejs的大部分官方案例上用法都是用return啊。。 (当然我也没太多的去研究,有错请忽略)

@afc163
不喜欢exports是因为我偏好一个module就返回一个reference。而且在我们的代码过程中,比方requirejs的示例

define(
    //The name of this module
    "types/Manager",

    //The array of dependencies
    ["types/Employee"],

    //The function to execute when all dependencies have loaded. The
    //arguments to this function are the array of dependencies mentioned
    //above.
    function (Employee) {
        function Manager () {
            this.reports = [];
        }

        //This will now work
        Manager.prototype = new Employee();

        //return the Manager constructor function so it can be used by
        //other modules.
        return Manager;
    }
);

你可以看到Employee是首字母大写,我们用这个表示该对象引用是需要实例化的,可以用 new Employee()来构建一个。 而如果实现的是singleton,在define引用的时候用首字母小写表示,表示是一个可用的instance。

如果用的是exports的话, 可能用的时候就得 var empolyee = new EmployeeModule.Employee();
有点inner class的感觉。

当然具体如何用有很多不同的实践并且适合不同的场景。确实更多的是个人喜好的问题把。

话说 C、java出身的程序员喜好 带 { } () 的语法, 而 python 出身 (包括以前的Fortran)可能更喜好stylus, coffescript这种语法。

@afc163
Member
afc163 commented Jun 19, 2013

如果返回的是类的话,可以用 module.exports 来代替 exports

define(function(require, exports, module) {

    function Manager () {
        this.reports = [];
    }

    module.exports = Manager;

})

目前 Arale 里大多是这么调用的。https://github.com/aralejs/overlay/blob/master/src/overlay.js#L184

module.exports 而不是 return 的好处是,一个文件里可能会出现多个 return 。

@realdah
realdah commented Jun 19, 2013

@afc163
是啊,其实用法确实都差不多。。。 就是个人偏好的问题了 : -)
我们代码基本上看起来和Arale里面也差不多的,tests看起来也其实一样了。

测试上除了我们多用了一个chai之外, mocha ,expect(我们用的是chai的expect)和sinon用的都一样。
你们工程的那个makefile看起来比较怪,看来跟spm有关对吧。

@lifesinger
Member

除了 module.exports, CMD 里也直接支持 return 返回的。

2013/6/19 Jeff notifications@github.com

@afc163 https://github.com/afc163
是啊,其实用法确实都差不多。。。 就是个人偏好的问题了 : -)
我们代码基本上看起来和Arale里面也差不多的,tests看起来也其实一样了。

测试上除了我们多用了一个chai之外, mocha ,expect(我们用的是chai的expect)和sinon用的都一样。
你们工程的那个makefile看起来比较怪,看来跟spm有关对吧。


Reply to this email directly or view it on GitHubhttps://github.com/seajs/seajs/issues/588#issuecomment-19678314
.

王保平 / 玉伯(射雕)
送人玫瑰手有余香

@realdah
realdah commented Jun 19, 2013

@lifesinger
我之前就看到你的回复了啊, 我也写了我的理解,看引用部分

在CMD的体系里面用return, 我的感觉就像下面所说的那样。。

是啊,不过个人感觉这就有点像javascript也可以不写分号, 而groovy也支持分号一样。。不是推荐使用。
而requirejs的大部分官方案例上用法都是用return啊。。 (当然我也没太多的去研究,有错请忽略)

当然这只是个人不成熟的理解。。 : )

@edokeh
Contributor
edokeh commented Jun 19, 2013

其实我也喜欢 return ,因为少写几个字,而“少写几个字”正是社会前进的动力啊

@lifesinger
Member

嗯,用 return 还是 module.exports,纯个人习惯,没有优劣。

AMD 和 CMD ,表面上的区别是书写格式不同。

AMD 下,默认推荐的模块格式是

define(['a','b'], function(a, b) {
  // do sth
})

CMD 里,默认推荐的是

define(function(require, exports, module) {
  var a = require('a')
  var b = require('b')
  // do sth
  ...
})

目前越来越意识到,默认推荐的模式书写格式表面上的不同,背后带来的差异越来越有意思。

其中一个核心差异是: 就近原则

AMD 中的依赖通过函数参数传入,带来的好处是一个模块的依赖直接在头部一目了然,非常清晰。带来的不足是,AMD 下其实默认推荐了 var 在一起的写法,比如

var a = 1, b = 2,  long long ...

// do sth A

// do sth B

CMD 书写风格下,导向的是就近原则(变量在需要它的地方之前才定义):

var a = 1
// do sth A

var b = 2
// do sth B

还有一个核心差异也跟就近原则有关,是 懒懒原则

在 AMD 里

define(['a', 'b'], function(a, b) {
   // 模块 a 和 b 在这里就都执行好并可用了
})

在 CMD 里

define(function(require, exports) {
   // ...
   var a = require('a')  // 模块 a 运行到此处才执行

   // ...

   if (false) {
      var b = require('b')   // 当某些条件为 false 时,模块 b 永远也不会执行
   }
})

CMD 更懒。

个人觉得 AMD 和 CMD 的核心差异体现在 对自然的追求上

  1. 何为自然?CMD 认为就近原则书写代码更自然,AMD 则觉得提前都写好更自然。
  2. 何为自然?CMD 认为需要时才执行更自然,AMD 觉得依赖就应该提前都执行好,这样才自然。

这两种对自然的理解,表面上看好像仅是代码风格区别,但随着 Sea.js 在实际项目中的锤炼和演进,我个人越来越觉得 CMD 规范从功能上更具有优势。比如

对打包的影响。 假设模块 a 和模块 b,打包后

define('a', [...], ....)
define('b', [...], ....)

在 AMD 下,由于 define 时模块 a 及其依赖立刻就执行了,这意味着,如果 b 依赖 a,那么在打包时,需要将模块 a 放在模块 b 前面,否则就执行有问题了。

但在 CMD 里,由于一切都是懒执行的,define 时仅仅是 meta 信息的注册,这意味着在 CMD 规范下,打包时,模块的顺序是无关的。

而包规范(Packages 规范)中很重要的一条,就是合并模块时,模块的顺序应该无关。

CMD 可以使得构建时的复杂度降低。

这个问题还可以深挖。目前 Sea.js 拥有 plugin-combo 插件,模块的合并可以放在线上动态做。有些情况下(比较容易出现),动态 combo 的地址会很长:

https://a.alipaybojects.com/??path/to/a.js,path/to/b.js..................path/to/long-url.js

当 url 地址很长时,超过 2083(好像是这个值),在 IE 以及一些服务器配置下,过长的 url 会出问题。这时经典的解决办法是将 url 拆分成多段:

https://a.alipaybojects.com/??path/to/a.js,path/to/b.js..................path/to/u.js
https://a.alipaybojects.com/??path/to/f.js,path/to/g.js..................path/to/long-url.js

拆分后,在 CMD 规范下,上面两个 url 可以并发同时请求,谁先返回都没问题。但在 AMD 下,上面的需求,就挂了,很难实现。

单个你会说 RequireJS 鼓励的是项目上线前,通过构建工具先构建好,不需要线上 combo,也就不会遇到上面的问题。

我想说的是,RequireJS 通过严格的项目开发流程的确可以解决问题。但 Sea.js 放得更宽泛,提前合并好,还是线上时才动态 combo,对 CMD 模块来说都可行。很多时候,combo 真心省事,也更自然。前端开发并非处处要像 Java 一样引入严格的构建过程。

除了对打包的影响,CMD 的懒执行策略,也更有利于页面性能。通过 CMD,可以做到页面首次可交互时间(TTI)达成前,不会执行其他脚本。CMD 为执行点提供了更多可控项。

以上理解可能有误,RequireJS 2.0 后,不少理念也在悄悄地发生着变化,现在好像也支持懒执行了,当初对 CommonJS Wrap 格式的支持,也是一种妥协。Sea.js 目前更纯粹、简单。

当然,上面都是我说的。我希望我都是错的,但期望看到有人能理性的证明我是错的。

@realdah
realdah commented Jun 19, 2013

其实我也是更偏向于seajs的lazy load策略,这样可以只加载需要的js,完全符合脚本的动态执行的特性。。。

我们目前的开发和build过程确实更像是后台开发模式, 这些都直接忽视了seajs的强项。。 (当然项目其他人都是老外,其实都不知道seajs)

比方说,开发过程中,我们使用embed jetty (有gradle 插件), 所有js和less 文件都是原始的, 开启 less.watch 后, 直接多台显示器就能检查多种样式效果。 js方面则通过测试驱动,基本上不大需要debug。

在build的时候,则自动运行所有相关的java和javascript的单元测试, 同时利用r.js合并生成单一js文件(一个app一个),同时minify/urglify 相关文件, compile 所有的 less文件到一个静态css。 带来的文件引用方面的变化则通过jsp内部的taglib来做(根据环境判断是生产还是业务环境),同时taglib还会在构建过程中使用自动生成新的cache version。
所以一般来说, sourcemap基本上对我们来说没啥作用,

combo功能看起来很酷,不过不清楚combo需要服务端做怎样的设置,有没有相关的文档可供参考?

@lifesinger
Member

combo 目前用的是 nginx 的模块:https://github.com/alibaba/nginx-http-concat
目前在阿里有成熟使用。国外,类似 YUI 的项目,也有大面积使用。Google 目前也有类似的方案。

我个人的观察:

RequireJS 比较适合独立 App 型项目,能够做统一构建的那种。但在国内环境下,经常并不这么简单。国内很多应用,是介于 Web Pages 和 Web App 之间。一个页面,很可能头部是一个团队C开发的,页面区域A是团队A开发的,区域B则是团队B开发的。统一构建并不太现实。这种项目场景下,用 RequireJS 会不是很合适,Sea.js 在这种场景下适配性则更强些。

对于独立的中小型项目,无论 RequireJS 还是 Sea.js 抑或 Component,都能搞定。但对于复杂度比较大、团队协作比较『混乱』的项目而言,Sea.js 会具备一些优势,无论是在开发模式,还是运行的性能上。

可能有些偏颇,却是我真心所想。欢迎反驳,要有力的反驳。

@HiZhaoxiaoyang

“很可能头部是一个团队C开发的,页面区域A是团队A开发的,区域B则是团队B开发的。统一构建并不太现实。”
确实很有体会,尤其对那种积累了3~5年以上还要不断迭代开发的项目。

@aui
aui commented Jun 20, 2013

“历史不是过去,历史正在上演。随着 W3C 等规范、以及浏览器的飞速发展,前端的模块化开发会逐步成为基础设施。一切终究都会成为历史,未来会更好。”——引用玉伯原文最后一段话,我个人也非常赞同。既然谈到了“未来”,我个人认为:前端 js 模块如果继续发展,其模块格式很可能会成为未来 WEB 一种标准规范,产生多种实现方式。就好比 JSON 格式一样,最终成为标准、被浏览器原生实现。

谁更有能成为未来的异步模块标准?SeaJS 遵循 CMD 规范,RequireJS 遵循 AMD 规范,先从这两种不同的格式说起。

CMD

CMD 模块依赖声明方式:

define(function (require) {
    var a = require('./a');
    var b = require('./b');
    // more code ..
})

CMD 依赖是就近声明,通过内部require方法进行声明。但是因为是异步模块,加载器需要提前加载这些模块,所以模块真正使用前需要提取模块里面所有的依赖。无论是加载器即时提取,还是通过自动化工具预先提取,CMD 的这种依赖声明格式只能通过静态分析方式实现,这也正是 CMD 的弊端所在。

CMD 规范的弊端

  1. 不能直接压缩:require是局部变量,意味着不能直接的通过压缩工具进行压缩,若require这个变量被替换,加载器与自动化工具将无法获取模块的依赖。
  2. 模块书写有额外约定:路径参数不能进行字符串运算,不能使用变量代替,否则加载器与自动化工具无法正确提取路径。

规范之外的约定意味着更多的文档说明,除非它们也是规范中的一部分。

注:SeaJS 静态分析实现是把模块包 toString() 后使用正则提取require部分得到依赖的模块路径。

AMD

AMD 模块依赖声明方式:

define(['./a', './b'], function (a, b) {
    // more code ..
})

AMD 的依赖是提前声明。这种优势的好处就是依赖无需通过静态分析,无论是加载器还是自动化工具都可以很直接的获取到依赖,规范的定义可以更简单,意味着可能产生更强大的实现,这对加载器与自动化分析工具都是有利的。

AMD 规范的弊端

  1. 依赖提前声明在代码书写上不是那么友好
  2. 模块内部与 NodeJS 的 Modules 有一定的差异

关于第二点的问题需要特别说明下。其实无论是 CMD 还是 AMD 的异步模块,都无法与同步模块规范保持一致(NodeJS 的 Modules),只有谁比谁更像同步模块而已。AMD 要转换为同步模块,除了去掉define函数的包裹外,只需要在头部使用require把依赖声明好,而 CMD 只需要去掉define函数的包裹即可。

总结

从规范上来说,AMD 更加简单且严谨,适用性更广,而在 RequireJS 强力的推动下,在国外几乎成了事实上的异步模块标准,各大类库也相继支持 AMD 规范。

但从加载器实现、功能来说,我更倾向于 SeaJS,理由:1、相对自然的依赖声明风格 2、相对精简的代码实现 3、贴心的外围功能设计 4、更好的中文社区支持。

如果有可能,我希望看到 SeaJS 也支持 AMD,与前端社区大环境保持一致最终幸福的是广大开发者。

打个小广告:ATC v1.0.3 - 可把前端模板转换成原生 CMD 或 AMD 、Modules 模块。因为 RequireJS 支持 CMD 规范,所以 atc 默认编译后的模块同时支持 SeaJS 与 RequireJS

@jsw0528
Contributor
jsw0528 commented Jun 20, 2013

@aui

CMD 的 require 是可以被混淆的

define("gallery/moment/2.0.0/moment",["./i18n/{locale}"],function(t){return t("./i18n/{locale}")})

你看, require 已经变成了 t

@aui
aui commented Jun 20, 2013

@jsw0528

理论上可以使用正则获取t的依赖,但是实际中压缩器可以让压缩后的代码产生很多类似t这样的短名称变量,这样导致静态分析依赖不准确。

@lifesinger
Member

感谢 @aui 参与讨论。

很认可你的分析。CMD 的弊端,核心就一个:要通过 factory.toString() 和正则提取依赖。这个,其实可以快速解决:

define(['a', 'b'], function(require, exports, module) {
   // ...
})

这样就解决了。从书写格式上看,CMD 其实是 AMD 的子集,可以约等于 AMD 中的 Simplified CommonJS wrapping 写法。CMD 有的问题,AMD 其实都有,AMD 的 dependencies 也是可省略的,省略时也需要通过 factory.toString() 和正则去提取依赖。理论上 AMD 的 loader 都可以直接加载 CMD 模块,但反之不行。

话说 ATC 不错,赞。

@lifesinger
Member

@aui @jsw0528 正则提取 require 是针对源码的,在压缩前完成。

@realdah
realdah commented Jun 20, 2013

为了能够正常混淆,requires 通过自有工具做dependency injection. angular jus 有同样的问题,但官方希望大家使用带dependency的宣称方式,于是就有人做了ngmin模块自动处理这种情况。毕竟代码还是希望参数格式简单点

@lifesinger
Member

@realdah 嗯,都得依赖工具,工具化是个趋势,除非某些功能被浏览器内置。

@edokeh
Contributor
edokeh commented Jun 26, 2013

我错了。。。以后再也不用 return 了。。。
最近一个项目由于场景的关系,要把 Sea.js 方案换成 browserify ,结果我得把每一个 return 改成 module.exports!每一个!!

@semious
semious commented Jun 26, 2013

突然,突起奇想,同类型的框架 是否可以采用 注入的方式,就是只要符合 cmd amd规范的类库/框架,只要进行配置注入 就可以进行即时修改框架/类库,这样 让上层的业务与模块类库/框架进行松耦合解绑

@realdah
realdah commented Jun 26, 2013

@edokeh 看你折腾的。。 如果只是替换这个,在IDEA里面很简单啊,可以通过Regex 结合参数replace, 几百上千个都不成问题。

@edokeh
Contributor
edokeh commented Jun 26, 2013

@realdah 可是 return 后面接的变量名不一样啊

@realdah
realdah commented Jun 26, 2013

@edokeh 就是变量名不一样所以可以用regex 加参数的方式替换啊。 教你一招吧,呵呵。

比如说
return (.*); <----- search this ,简而言之, 在括号里面的就是参数,你可以有多个参数
module.exports = $1; <------ replace to this

@edokeh
Contributor
edokeh commented Jun 26, 2013

@realdah
你这个我想过的,可是很多函数都会 return 而这些是不用替换的啊啊啊啊啊

@realdah
realdah commented Jun 26, 2013

@edokeh 那确实,只能结合人工干预了。哪怕match模块结尾,也无法完全cover掉所有的可能性。
其实想想看,这也是咱码农的真正价值所在啊,工具替换不了。 哦也!

@idamag
idamag commented Jul 2, 2013

不一样的思路带来完全不一样的东西。又了解了很多历史缘由。

@hax
hax commented Jul 2, 2013

@realdah 我之前刚想说 module.exports 比 return 好的原因之一就是语义清晰,你就中刀了,呵呵。

@realdah
realdah commented Jul 2, 2013

@hax 哈哈,看清楚哦,不是我中招,是edokeh中招了。。。 是他最开始很"深沉"地说了下面这句话。。

edokeh:
其实我也喜欢 return ,因为少写几个字,而“少写几个字”正是社会前进的动力啊

结果他就狂吐槽了。。。

其实我本人还是很开心的在使用return噢。。。。

@edokeh
Contributor
edokeh commented Jul 2, 2013

楼上神补刀,我已F10 E Q

@yangjc yangjc referenced this issue in yangjc/halberd-js Nov 16, 2013
Open

Halberd-JS 入门 #1

@msdlisper

CommonJS 社区还在不,在哪里啊

@afc163
Member
afc163 commented Aug 23, 2014

上面不是有链接

@sunshinebear

this is a test ,select * from

@GaoGavin

有可能是 先接触的 nodejs 吧- - 看到 前面 define(["a", "b", "c", "d", "e", "f"], 感觉 好多此一举的感觉- - 只要require 不就好了么- - 前端菜鸟,勿怪。。。

@Cleam
Cleam commented Oct 22, 2014

围观大师们的激烈对话,真的受益匪浅!

@loveqin
loveqin commented Oct 23, 2014

文好,评论更好。

@fantasyni

@semious 依赖注入同时带有异步脚本加载已经在 bearcat 中实现了,依赖的描述不使用 require 直接通过 js 对象的自描述,同时,解析完依赖,像 AMD/seajs 一样异步加载js脚本,然后再把实例对象注入进去!

@zhaomingliang

好文章

@Huxpro
Huxpro commented Jul 5, 2015

Modules/Async 不应该指的是 Modules/Async/A 么?

// async load a module
require.async()
require.ensure()
@ss7247
ss7247 commented Aug 16, 2015

分久必合,都是javascriipt 标准的先驱,致敬!

@luobotang

时至今日,再来看下上面的讨论,不禁感概技术的日新月异,潮流的难以捉摸....
既然一直不变的只有“变化”这件事,那么还是拥抱变化,敢于尝试才好。

@lwyi188
lwyi188 commented Feb 25, 2016

好文章,致敬大牛们!

@muzi131313

从接触模块化开始,开始了解requireJS/SeaJs,cmd/amd规范,MVC/MVVM前后端数据分离,grunt/gulp构建;以及fis功能模块的划分;再到webpack,react操作虚拟dom,redux;表示已经跟不上前端的脚步;
模块化,还有sail.js官网,看到里面的好多组件,这个是入门教程;还有antjs+react+babel,这是传送门;还有vuejs+webpack+npm,这是传送门

@qianniuc

以前就看过这边文章,现在再看依然回味无穷,评论也很精彩,前端飞速发展,技术日新月异,学不过来,就算草草用过,也不知精髓,不知何时才有稳定期

@herohoner

前端人员就像是前线人员,战火连天,战局动荡,一草一木都能引发蝴蝶效应,而我这个小小炮灰只能拼命的往前。。。。。编不下去了,(⊙﹏⊙)b
使用了grunt,webpack 感觉都挺好用的,把多个文件通过一个人口引入,然后只打包出一个文件,这样就算是很多个模块,通过这些的整合,在项目上只有一个请求就能搞定,这样不是很酷的一件事。
在seaJs中是解决了模块的引入了的问题,可在文件在加载却是一个模块的一个请求的,这样会不会就影响了性能能。。。
小菜鸟一枚,一上这些理解,是不是我的使用方式不对?

@BrickXu
BrickXu commented Dec 12, 2016

向前辈们致敬

@wqb2017 wqb2017 referenced this issue in wqb2017/yctabs Jan 17, 2017
Open

JavaScript 模块化历程 #6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment