Rails Assets Pipeline 的价值
http://chloerei.com/2013/03/10/rails-assets-pipeline-s-value/
综合了之前看到的对 Assets Pipeline 的问题,整理了一篇文章。
说的不一定都对,欢迎指正。
感谢分享。Asset Pipeline 还有在 caching 上的好处。
把 controller 和 action 直接写到 js 里面,这样维护起来会很麻烦把
打包在一起有做 cache 的好处,开发也挺省事。但浏览器解析 js 的时间还是存在,所以 Rails 又有了 turbolink。不过这样会不会有点为解决问题而解决问题?
一直在琢磨着要写一篇《为什么我不喜欢 Assets Pipeline》,看到你这篇文章,看来我得抓紧了 先整理一些观点列举一下:
几年前公司的项目里发布的时候就会自己压缩 js 不习惯的是啥都是 /assets/xxx.png /assets/xxx.css
我习惯的是/images/xxx.png
#6 楼 @chenge turbolink 之后整站成了个单页应用,意味 js 逻辑不会自动释放,有内存泄漏的可能。
所以写页面逻辑除了要考虑什么时候执行,还要考虑离开的时候释放。所以会有类似代码:
$(document).on 'page:load', ->
if page is scrollPage # pseudo code
$(window).bind 'scroll', customScrollFunction
$(document).one 'page:change', ->
$(window).unbind 'scroll', customScrollFunction
https://github.com/rails/turbolinks/issues/154#issuecomment-11916707
这值得再写一篇文章了,我也是摸索中。
因为我是先接触 pipeline,然后接触 require.js,所以觉得前端方案挺复杂的,为 js 代码做了很多模块化约定,最终还是要借助服务器预编译。相信如果是纯前端出身,看法会反过来,都会先入为主。
pipeline 的写法,就好像 Ruby 代码,需要什么就先在顶部 require,命名空间不强制。如果用 CoffeeScript 的话,默认所有代码会被包在匿名空间里。模块化就交给开发人员了,pipeline 起一个支撑但不强制的作用。
pipeline 提供了工具但是没有多少文章说明怎么去做,新手掉了不少坑,所以我打算接着写这方面的看法。
#3 楼 @darkbaby123 这两个相互依赖,本来我不喜欢全部 js 打包成一个,看了 Turbolink 的原理之后发现需要全部打包成一个才能发挥作用,然后才 require_tree 了。
总结得很好,至于“区分不同页面的特定代码”,我习惯这样做:
$('body#topics-show').each(function(){
var scope = $(this);
var foo;
});
这样能即能按元素存在执行相应的代码,也能减少全局变量的产生。
#4 楼 @edokeh 我觉得你这是把两个不同的概念放在一起来对比了;require.js 解决的问题是模块化和依赖调用,assets pipeline 的主要目的是减少 http request,这两者本来就不冲突。require.js 是从语言和代码结构的角度来提升开发,而 assets pipeline 则是从非代码本身的角度来提升开发,完全可以把二者的优势结合在一起,为什么非要隔离开来,还要比个高下呢?
说到纯前端的解决方案,assets pipeline + gems 就相当于 grunt + bower(yeoman 则是一个使用了两者的整体解决方案)无论是 require.js 还是 sea.js 都有 grunt 下的 task plugin,利用 grunt 就可以达到上述两个方面的优势和好处同时获得。
那么在 rails 这个领域里,现在的状况即 assets pipeline 就是 grunt,gems 就是 bower,而且都要更加强大的多(有 ruby 作为脚本语言支撑,当然 grunt 和 bower 也有 node.js 作为支撑,不过前者发展的更为成熟一些),我们完全可以在 rails 框架里使用 require.js 或者 sea.js,在满足了依赖管理等要求之后,还能让 assets pipeline 完成合并、压缩、cache、revision 等功能,何乐而不为?
@Rei 所以我才觉得 Rails 现在有点为解决问题而解决问题。一些新东西的推出是为了解决上一个方案留下的问题。
不知道下一次会出现什么。
#19 楼 @edokeh 很遗憾,我说了 grunt 是一个纯前端的工具,它依赖的是 node.js 的环境,所以不能和 rails 一起用。
不过你不要忘记,ruby 本身就是一个很强大很强大的脚本语言,脚本语言能做的事情多了去了,并非一定要全部交给 assets pipeline。
举例说明,require.js 里有一个工具叫 r,js,它完成的工作就是将一个项目里的所有依赖关系文件打包,并且在内部做一些额外的工作(类似于你说的 SeaJS 的静态语法分析这一类的工作)。OK,那么 r.js 是怎么运行的呢?node r.js -o you_file.js
See? 它的工作是通过 node 执行的,那么我们完全可以在 rails 框架里,在 assets pipeline 执行前写个脚本调用这个命令,这样我们就可以使用 require.js 自带的打包工具了。
至于 SeaJS,因为我没用过所以不多做评价,不过我知道 SeaJS 的打包工具是和 Require.js 类似的,好像叫 spm.js?所以理论上也可以用一样的方法来完成这件事情。
明白这一点,我们也可以理解为什么 assets pipeline 没有这个功能,因为 assets pipeline 提供的是一种 common way,能够满足多数 general javascript 的打包、压缩等要求。但是对于特定的库,比如 require.js 或 sea.js 它又怎么去分别实现两者对于自己的库进行的特殊处理呢?就算它能实现(比如通过配置文件),那么你觉得有这个必要吗?你用 A,就要求 assets pipeline 支持 A 的特性,如果我用 B 呢?他用 C 呢?Rails 得多复杂才能满足所有人的胃口?
你只是看到了 Sea.JS 在 js 这一块比 assets pipeline 更多的功能,却没有看到 rails 做一个 full stack framework 又远比 Sea.JS 复杂了多少?正所谓:术业有专攻,作为开发者,我们当有能力在 Rails 以及 Ruby 给我们提供的基础上自力更生,而不是等着别人把我们七零八碎的要求一一去实现。
至于 grunt,那是纯前端领域的类似 assets pipeline 的工具,而且它不从属于任何一个框架,所以可定制性极强,且插件都是用 node.js 写的,对前端工程师天然友好。有兴趣的话直接去看看它的文档吧。
#21 楼 @darkbaby123 你的看法,又是被 require_tree .
陷进去了。
我未了解 Turbolink 之前,是这样用 pipeline 的。
application.js
//= require jquery
//= reuqire rails_ujs
//= require 其他基础库
topics.js
// topic 页面的代码
编译的时候都独立编译。
pipeline 是一个打包工具,可以按自己想法去用。我是看到了 Turbolink 的好处,才接受把所有 js 打包在一起。
这些工具的目的,都是为了让页面加载更快,Rails 工具链提供了一套方案。相比客户端 MVC,我觉得简单多了。
#23 楼 @edokeh 两者无法并存,其原因并不在 rails,而是太多的前端工具所拥有的各种特性,你没法指望 rails 全部去支持;如果你认为 rails 管前端的事情管多了,那你有没有考虑过那些不用 require.js 或者 sea.js 的开发者又该怎样处理合并、压缩、cache 等等这些事情呢?是不是还要让他们去重新学习前端的处理工具?
作为一个最终用户我们当然习惯于从自己的角度来看待问题,但是作为一个框架的作者或团队,他们要考虑的问题面就要更加广泛了,它没法做得太简单而让很多开发者觉得不爽,也不能太复杂为了照顾一些高端用户而引入了太多的功能和复杂的处理流程。如何取得一个平衡点,同时又能提供一个扩展性和定制性强的工具框架,这毕竟不是一件简单的事情。
在我看来,Rails 在努力消弭前后端的界限,让更多专们从事后台开发的开发者也可以尽可能多和轻松的使用一些前端技术,反之亦然。这种做法本身是很积极的,但是肯定会遇到众口难调的问题。不管怎么说,理应获得更多的支持和鼓励,才有可能让事情变得越来越好。
另外,技术永远是在不断进步和发展的,node.js 的诞生不也一样可以被视作“前端侵入后端”的经典案例?如果其作者事先也想:“这样做会不会不好?”,那我们今天还能看到 node.js 吗?没有什么东西是完美无缺的,但这一点恰恰是证明我们作为“开发者”的价值所在,我们不就是那一群“让不可能变成可能,让可能变成更好”的人吗?
我目前还是觉得,assets pipeline 已经干了 require.js 这些前端方案干的事,如果在文件头加注释就可以解决编译问题,那么为什么要在代码里面 require('...') 或者 define('...'),然后再用语法分析器将依赖抽出来呢?
所以我也没怎么考虑要它们融合。实际开发中二选一吧。
#30 楼 @edokeh 无论是 sea.js 还是 require.js, define 都实在是太丑了..
纯的 CommonJS 解决方案也有一些是不用写 define 的,但是会多一个 build 的过程,自动加 define 给你,这样就可以跟 node 一样只需要 require 就可以了。( component && browerify
sea.js 跟 require.js 的 sync load 基本属于伪命题,大家都还是需要用 r.js 与 spm.js 事先压缩,或者通过 nginx 等 hack 方式加载所必须的文件 ( 是不是值得,是值得商榷的,因为损失了 cache 特性。
压缩合并为整个文件/或几个小文件 ( 权衡 cache 与 性能 ) 是现阶段唯一好使的做法。
同时,这类产品都有一个问题。就是 module 缺乏,需要有额外的代码来支持才能使用。shim 是一个方案,但是不够平滑.. .. 问题多多..
#33 楼 @edokeh 另外插件如果都是 Fork + Patch 的模式的话,会跟 Rails 现在 gem 加载 javascript 陷入同一个困境,更新跟不上..
模块化方案大家貌似还是在等 ES6. 但现阶段来看,require.js 貌似已经领先了,很多应用默认支持 require.js.
另一个较为成功的库 analytics.js 默认支持 component.
还是希望标准早日落地吧..
asset pipeline 不能和 grunt、require.js、spm 共存?因为我还没有开始做单页面的应用,所以还没有尝试。但我觉得应该可以共存的。
@import
语法,完全没有问题不管怎么说,我觉得前端方案肯定是能在 rails 里面 work 的,等我做单页应用的时候玩玩
刚学完 rails 初级,发现 lz 说的我还是看不懂,更泪崩。。。 玩笑管玩笑,现在 yeoman 出 1.0 后,yo 就是 rails,bower 就是 gem,grunt 就是 rake 或者 bundle,感觉 node.js 现在的水比 rails 深多了,抄了不少 rails 东西,又有 N 多自己的东西……
链接失效了~ 作为一个才接触 Rails 的小白 我看直接把 Assets Pipeline 禁用先 虽然它压缩合并 JS、CSS 文件 减少 http request 的大小和数量的出发点非常好 但一股脑打包可能会打乱了渲染的顺序和执行的逻辑 本来给一个简单网站中各个 HTML 页面来添加正确的 JS、CSS 是件再简单不过的事了 小白中的小白都能干好的一件事 就因为在 ROR 中想用 Assets Pipeline 的压缩功能 反而要花一些 时间检查不同的 JS 是否有冲突、CSS 之间是否有覆盖 还有再花些时间 写几句代码 把这些分离出来 单独引用。。。 用 ROR 不就是为了敏捷开发么 但被这些引用的小事搞得抓耳挠腮实在没必要 所以直接禁用
#48 楼 @sunnyleo 就因为这 JS 和 CSS 的引用问题 今天在坛子已经泡了很长时间了。。。现在还是木找到方法。。。 对于网页间共用的文件 肯定是压缩好 但对于有特需的 肯定单独引用好 也看到坛子里有处理 但是主要看到有几个帖子都是同样的问题 但方法都不一样 https://ruby-china.org/topics/14374 https://ruby-china.org/topics/5324 https://ruby-china.org/topics/11455
小白我已经被搞晕了 请大侠详赐简单明了、暴力有效的方法 谢谢~
#49 楼 @sunnyleo 找到了@Rei 链接失效的博文 http://chloerei.com/2013/03/10/rails-assets-pipeline-s-value/ 写的很详实 思路清晰了不少 非常感谢!