http://codetunes.com/2013/we-released-rails-assets/
The solution to assets management in Rails
Rails Assets is the frictionless proxy between Bundler and Bower. It automatically converts the packaged components into gems that are easily droppable into your asset pipeline and stay up to date.
「我们都是好朋友吧,所以你的东西就是我的东西,我的东西还是我的东西。」
刚起床,一会到公司就去试试。希望可以解决 vendor/assets 引入 几十个外部 js 和 css 文件,造成无法升级第 3 方 js 库的问题。下面是简单翻了一下原文
过去的几周我们一直在整 Rails Assets 终于可以在这周给 Ruby 社区提供一个管理 assets 的解决方案了。
Tymon 在 London Ruby Users Group meeting on Monday 展示了 Rails Assets
Watch the video from the talk 或者看看我下面的简单翻译
上午没空翻,看 #7 楼
上午没空翻,看 #7 楼
上午没空翻
自从 Rails 3.1 引入 asset pipeline 之后,在 Rails 项目中管理 client-side libraries 总算脱离石器时代进入青铜时代了。再也不用手动把那些需要用到的 JavaScript/CSS frameworks 上传到 /public 文件夹了。
但是当你真正做项目的时候才发现,其中的苦楚真是:如人饮水,冷暖自知。我们仍然要管理每个项目中得 vendor/assets 文件夹,已期自己版本库中的这些第 3 方代码和它们原始库保持同步。RubyGems 上的版本通常会有几周才会更新,当然也有坑爹的数月不更新。它没有像 Ruby-China 这样的社区支持。主要是这货是纯手动的,以致于想保持 gems 和外面的最新版本一致特别繁琐。
另一面,前端开发者们完全忽略 Rubyists。他们捣鼓出来了一个叫 Bower 的玩意来管理软件包。并且解决了让我们深恶痛绝的问题,那帮熊孩子们可不关心 Gemfile。
在 Rails 应用中合理的管理 asset 好像缺了一个环节。明显的一个场景:每次手动更新特定项目中较旧的 client-side libraries 文件。然后在其它的项目中定期重复这个动作。总有一天你会掀桌子:玩儿蛋去吧,就让这些文件保持初始状态,我用它就是想实现 XXX 效果,实现效果之后我再也不想动这些文件。 technicaldebt
考虑到本帅近期是泡着温泉做着 SPAs 编代码,上述情景成了一个大问题。因为每天开发结束的时候,总要有专人手动来管理我们程序中使用的 core libraries 和 toolkits。这感觉就像回到 1999 年的时候,完全不符合我们高富帅的身份。
Rails Assets 的出现正是为了解决这个问题。在 Bower 和 Bundler 之间提供一个代理,完美调和 front-end 和 back-end 的矛盾。
感谢我们的 CTO,感谢我们的 UI 设计师,感谢天,感谢地,感谢 CCTV。
我们用了许多我们开发的,仍在运行的项目来验证了这个方案的可行性。当然我们也不能吹牛 X 说这个方案帮我们提高了工作效率,改进了工作流程,省了多少时间和客户的银子。呵呵呵,你懂得。
呦呦切克闹,这个功能将成为杀手级的说。
作者需要你的帮助和支持,有啥问题请给作者提供反馈。
rails4 可以?貌似可以,原来它就是一个 asset 的 source 源,而且每个 gem 也满足了 bower 规范?哈哈。。感觉这个可以火。。。
问题是,哪个来跟进那些小版本呢?还是靠社区? bty:网站的搜索太帅了,好快。。
首先,bower 的覆盖面不够大。( 市面上还有大量的 javascript libraries 是不支持的。
其次,没多少人写 main property. ( main 的自动引入由 sprockets 提供
再次,ignore property 更没多少人写。
而且,当没有 main property 支持的时候,用完之后会发现,整个 public 目录杂乱无章。各种 图片,README.md 都进来了。解决方案是:你只能 Fork 一份自己的来发到 bower 上,这是最痛苦的一点。
为什么不让 Javascript 跟 CSS 的引入回归本源呢?
让我为大家推荐下面这个工具:Linner
Linner 是一个功能完整的 Assets 打包工具,其功能集是 Sprockets 的超集。
最重要的,支持 Linner install 来安装 Linner 所需的各种 library bundles.
Linner 使用 config.yml 或者 Linnerfile 文件作为自己的配置文件,一份生产环境中使用的 Linnerfile 是这样的:
paths:
app: "src"
public: "public"
groups:
scripts:
paths:
- "src/scripts"
concat:
"/pokeball.js": "{src,vendor}/**/*.{js,coffee}"
order:
- vendor/jquery.js
- vendor/handlebars.js
- vendor/lodash.js
- vendor/moment.js
- vendor/moment/zh-ch.js
- vendor/pikaday.js
- vendor/jquery/jquery.ui.widget.js
- vendor/jquery/jquery.iframe-transport.js
- vendor/jquery/jquery.fileupload.js
styles:
paths:
- "src/styles"
concat:
"/pokeball.css": "{src,vendor}/**/[a-z]*.{css,scss}"
order:
- vendor/normalize.css
- ...
- src/styles/pokeball.scss
templates:
paths:
- "src/scripts"
precompile:
"../vendor/templates.js": "{src,vendor}/**/*.hbs"
modules:
wrapper: "cmd"
ignored: "vendor/**/*"
definition: "/pokeball.js"
notification: true
bundles:
jquery.js:
version: 1.10.2
url: http://code.jquery.com/jquery-1.10.2.js
jquery/jquery.ui.widget.js:
version: 1.10.3
url: https://raw.github.com/blueimp/jQuery-File-Upload/9.3.0/js/vendor/jquery.ui.widget.js
jquery/jquery.iframe-transport.js:
version: 1.8.1
url: https://raw.github.com/blueimp/jQuery-File-Upload/9.3.0/js/jquery.iframe-transport.js
jquery/jquery.fileupload.js:
version: 9.3.0
url: https://raw.github.com/blueimp/jQuery-File-Upload/9.3.0/js/jquery.fileupload.js
jquery/jquery.pagination.js:
version: 2.2.1
url: https://raw.github.com/gbirke/jquery_pagination/3a614db5fa1e02a5f568b2ac798224efb963a843/src/jquery.pagination.js
jquery/jquery.serialize-object.js:
version: 2.0.3
url: https://raw.github.com/hongymagic/jQuery.serializeObject/v2.0.3/jquery.serializeObject.js
jquery/jquery.validator.js:
version: 1.2.0
url: https://raw.github.com/TerminusHQ/validator.js/1.2.0/validator.js
lodash.js:
version: 2.2.1
url: https://raw.github.com/lodash/lodash/2.2.1/dist/lodash.underscore.js
spin.js:
version: 1.3.2
url: https://raw.github.com/fgnass/spin.js/1.3.2/spin.js
moment.js:
version: 2.4.0
url: https://raw.github.com/moment/moment/2.4.0/moment.js
moment/zh-ch.js:
version: 2.4.0
url: https://raw.github.com/moment/moment/2.4.0/lang/zh-cn.js
handlebars.js:
version: 1.0.0
url: https://raw.github.com/wycats/handlebars.js/1.0.0/dist/handlebars.runtime.js
pikaday.js:
version: 1.1.0
url: https://raw.github.com/dbushell/Pikaday/1.1.0/pikaday.js
normalize.css:
version: 1.1.3
url: https://raw.github.com/necolas/normalize.css/v1.1.3/normalize.css
而我们今天着重着墨的就是 bundles 这部分。结合 scripts
下面的 order
配置部分,我们就轻松解决了 Sprockets 关于引入顺序的问题...
让我们先来解决 Bower 所不能解决的那两个问题。
modernizr.js:
version: 2.7.1
url: https://raw.github.com/h5bp/html5-boilerplate/v4.3.0/js/vendor/modernizr-2.7.1.min.js
modernizr 就会被安装在 vendor 目录中,呈现出:
vendor
└ modernizr.js
而 modernizr.js 同时会被缓存在 ~/.linner/bundles 内部。
moment.js:
version: 2.4.0
url: https://raw.github.com/moment/moment/2.4.0/moment.js
moment/zh-ch.js:
version: 2.4.0
url: https://raw.github.com/moment/moment/2.4.0/lang/zh-cn.js
moment 就会被安装在 vendor 目录中,呈现出:
vendor
└ moment.js
└ moment
└ zh-ch.js
Linner bundles 给你最大的限度来操控自己的 vendor 目录,全面使用 Linner bundles 之后你的 vendor 目录完全可以被加入 .gitignore. 同时你可以获得最大限度的目录操控,适合有洁癖的你。
这种做法依然有很多的 JS 冗余和全局变量的污染。
我们公司用了 seajs 感谢 @edokeh
gem 'seajs-rails'
cat config/seajs_config.yml
seajs_path: seajs/seajs/2.0.0/sea.js
family: weiport
output:
relative:
- "layouts/app.js"
- "orders/new.js"
- "shops/form.js"
- "shops/show.js"
- "vcards/form.js"
- "vcards/show.js"
- "vconsume_recs/index.js"
- "friends/index.js"
- "accounts/new.js"
- "sessions/new.js"
- "articles/show.js"
- "articles/index.js"
- "articles/list.js"
- "articles/form.js"
- "base/feedback.js"
- "base/bottom_menu.js"
- "base/slide_menu.js"
- "base/common_modal.js"
- "base/select_link_modal.js"
- "base/select_image_modal.js
- "acts/rotate.js"
- "acts/scratch.js"
- "messages/edit.js"
- "accounts/my.js"
- "attachments/index.js"
- "home/index.js"
- "cases/index.js"
...
all: []
alias:
"$": "gallery/jquery/1.10.1/jquery.js"
"$9": "gallery/jquery/1.9.1/jquery.js"
"jquery.template": "gallery/jquery.template/1.0.0/jquery.template.js"
"BrowserDetect": "gallery/BrowserDetect/0.0.1/BrowserDetect.js"
"jquery.placeholder": "gallery/jquery.placeholder/0.0.1/jquery.placeholder.js"
"jquery.royalslider": "gallery/jquery.royalslider/9.4.8/jquery.royalslider.min.js"
"handlebars": "gallery/handlebars/1.0.2/runtime"
"bottom-menu": "gallery/bottom-menu/0.0.1/bottom_menu.js"
"swfobject": "gallery/swfobject/2.2.0/swfobject.js"
"iscroll": "gallery/iscroll/4.1.9/iscroll.js"
"jquery.rotate": "gallery/rotate/2.2/jQueryRotate.js"
"jquery_ujs": "gallery/jquery_ujs/1.6.0/jquery_ujs.js"
"bootstrap-modalmanager": "gallery/bootstrap-modal/2.2.0/bootstrap-modalmanager.js"
"bootstrap-modal": "gallery/bootstrap-modal/2.2.0/bootstrap-modal.js"
"bootstrap": "gallery/bootstrap/0.0.1/bootstrap.min.js"
"bootstrap3": "gallery/bootstrap/3.0.2/bootstrap.min.js"
"jquery.qeditor": "gallery/jquery.qeditor/1.6.2/jquery.qeditor.js"
"respond" : "gallery/respond/1.4.0/respond.min.js"
...
刚开始用的时候各种痛苦,各种反对意见,各种吐槽。 我硬着头皮撑下来。 现在团队习惯了,用起来爽多了。
刚开始用的时候各种痛苦,各种反对意见,各种吐槽。
说明你用错工具了。
Linner 只需要四行就写完你的好几十行了。
modules:
wrapper: "cmd"
ignored: "vendor/**/*"
definition: "/pokeball.js"
效果是一样的。
不知道部署会不会痛苦!之前用 requirejs 部署的时候出了各种问题... 后来就放弃了!
用 Linner 部署不痛苦。
开发阶段与生产阶段的区别只是:
Linner watch
与 Linner build
的区别。
我写了个 gem 来管理自己常用的前端库,具体文件使用 vendorer 更新 https://github.com/h2ocube/h2ocube_rails_assets
实际试用下来的结论是现在这玩意一点用都没有……方便倒不见得方便,比如大量不支持 bower 的没法用,即使 bower 支持,但是 gem 没人生成过的也不能用,要一个个试……然后去官网添加 gem……添加成功了还不一定能用……我添加了 3 个包,只有 angular-i18n 和 greensock 能用……
对于少部分支持的,因为只引入了 main property 里指定的文件,各种实际需要的 img,font,swf,plugin,themes 反而全部丢失,这种情况反而是不写 main property 的能手动指定路径引用……最终弄下来只删掉了 vendor 目录中的其中一个文件夹
实际上,用 assets pipeline 打包不能自动同步使用第三方库的唯一问题是在一个文件中异步载入另一个文件时,比如 css 中载入图片、字体,js 异步载入其他 js 的时候, 需要修正引用的 url ——各种手动打包成xx-rails
的 gem 里面经常会出现各种xx.css.erb
或者干脆用 scss 重写——这个修正 url 的过程理论上大概可以自动化,然而第三方库的作者不会考虑 asset pipeline,写的格式不太统一,于是很难完美自动化地修正这些 url,然后这里上面提到的所有工具没有一个现在能真正解决这个问题
#28 楼 @aptx4869 最后这个问题也是 Linner 解决过的问题,CSS 中载入其他资源可以用 Linner 的 copy 命令指定他 copy 后的相对位置。这个问题本身是不存在的。
而 js 异步载入其他 js 的问题,实际上 Linner 默认使用了 CMD 规范,所有文件都可以 require
获得,这个地址在 linner watch
的时候已经确定了,所以也没问题。
teaspoon 本身也没什么特别的,可以集成 mocha, 而且有两种模式,一种是浏览器内,一种是 phantomjs 后台运行,浏览器端的没什么特别的,这个大家都可以做。服务端的话可以后台集成,这个我已经有一个 issue 在了。后面会做。
#29 楼 @Saito 额,我说的是 用 assets pipeline 引入第三方库 的问题,你这都已经完全不用 assets pipeline 了当然没这问题……
但是我这有依赖 assets pipeline,首先需要能用.coffee.erb
,因为有关键的配置是由后端生成的,比如 routes 和 assets hosts, websockets url,api key 啥的,不可能手动抄一遍吧,然后我试过的测试框架只有 teaspoon 和 jasminerice 能识别.coffee.erb
然后是 template 的自动载入,开发测试每个 directive 或者 route controller 的时候,都需要动态地将特定的 slim 文件(这些文件有时候也包含后端生成的代码,比如用 form builder 写的 form)编译成 html 给$templateCache 载入,这是核心需求,不可能像 ui-bootstrap 等第三方库的测试那样傻乎乎的在 js 里面重复手写 html,他们可以这样做是因为作为库的 template 比较小,也不太会变的,而面向用户的 app 需求经常变,template 更大,修改频繁,在 js 里重复手写 html 简直是噩梦……我试过的有且只有 teaspoon 能用原生方法实现
@Rei 讨教个问题,在引用 jquery.xx 或者 font-awesome 这类的 assets gem 的时候,往 app.js 和 app.css 引用该怎么写呢?貌似。和 - 会在编译的时候出错。
#33 楼 @ytwman 在 https://rails-assets.org/components 这里先找到你要的 component,然后点击相应的版本号,然后就会显示出怎么在 application.js/.css 去引用
require 各种 error, 不好用。好多包看起来支持,能在 https://rails-assets.org/components 搜到,其实用不了,大半因为 bower.json 没 main entry。
也就是个 beta version,决定暂时不用了。