3 招实用Asset Pipeline 加速 –by xdite

Asset Pipeline 最让人诟病的就是deploy 时花费速度过久。 在社群聚会时发现大家都对这个主题非常不熟。 所以把最近累积了的这方面技巧整理出来分享给大家。

1. Capistrano deployment speedup

使用capistrano 内建task 执行assets:precompie

capistrano内建了'deploy/assets'这个task。 只要在Capfile里面

 

Capfile

  1
 load 'deploy/assets'

 

deploy 就会自动执行assets precompile 的动作。 由原始档可以看到这个task实际执行的是

"cd /home/apps/APP_NAME/releases/20120708184757 && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"

而执行的时机是

after 'deploy:update_code', 'deploy:assets:precompile'

许多开发者不知道有这一个task 可以用。 手动写task 去compile,造成了两个问题:

  1. 时机执行错误。 Compile 时机错误会造成站上出现空白css。
  2. 执行compile 机器负担太重。 如果是手写的task 通常会是load 整个production 的环境去compile。与只load assets 这个group 所吃的系统资源「有可能」差得非常多。

如果没有变更到assets 时,就不compile

请把这里面的内容贴到你的deploy.rb 档里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# -*- encoding : utf-8 -*-
set :assets_dependencies, %w(app/assets lib/assets vendor/assets Gemfile.lock config/routes.rb)
namespace :deploy do
  namespace :assets do
    desc <<-DESC
      Run the asset precompilation rake task. You can specify the full path
      to the rake executable by setting the rake variable. You can also
      specify additional environment variables to pass to rake via the
      asset_env variable. The defaults are:
        set :rake, “rake”
        set :rails_env, “production”
        set :asset_env, “RAILS_GROUPS=assets”
        set :assets_dependencies, fetch(:assets_dependencies) + %w(config/locales/js)
    DESC
    task :precompile, :roles => :web, :except => { :no_release => true } do
      from = source.next_revision(current_revision)
      if capture(“cd #{latest_release} && #{source.local.log(from)} #{assets_dependencies.join ‘ ‘} | wc -l”).to_i > 0
        run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
      else
        logger.info “Skipping asset pre-compilation because there were no asset changes”
      end
    end
  end
end

这是在Railsconf 2012的Stack Smashing上学到的一招。

如果你的assets 档案没有变动的话,只要执行copy 上一版本的assets 就好了。 这段task 会侦测

  • app/assets
  • lib/assets
  • vendor/assets
  • Gemfile.lock
  • confir/routes.rb

是否有变动。 基本上已经含了所有可能assets 会变动的可能性。 有变动才会重新compile。

整体上会加速非常非常的多 。

2. use @import carefully

避免使用@import “compass”; 这种写法

compass是大家很爱用的SCSS framework。 大家写gradiant 或者css spriate 很常直接开下去。

但是你知道

 

  1
 @import "compass" ;

 

 

  1
 @import "compass/typography/links/link-colors" ;

 

这两种写法。

前者compile 的速度可能比后者慢到9 倍以上吗?

会这么慢的原因,是因为compass本身即是懒人包 , @import "compass";会把

  • “compass/utilities”;
  • “compass/typography”;
  • “compass/css3”;

下面的东西通通都挂起来(还跑directory recursive)。

所以自然慢到爆炸。 如果要用什么helper,请直接挂它单支的CSS 就好了,不要整包都挂上来。

全挂其慢无比是正常的。

避免使用partial

我知道partial 是SCSS 整理术的大绝招。 但是若非必要,也尽量避免一直单档一路@import 到底。

 

common.css.scss

  1
 2
 3
 @import "reset" ; @import "base" ; @import "product" ;
common.css.scss

  1
 2
 3
 //= require "reset" //= require "base" //= require "product"

 

这两个在asset pipeline 输出结果是一样的。 但后者会比前者快。

如果真的需要用到非得使用partial 的技巧(如需读变数用require 读​​不到,@import 才读得到)再使用即可,因为只要一牵涉到directory recursive compile 就会慢…

3. don’t require .scss & .coffee for no reason

避免使用require_tree

使用generator 产生controller 时,rails 会自动帮忙产生

  • product.css.scss
  • product.js.coffee

然后application.css 与application.js 会利用

 

application.css

  1
 //= require_tree

 

这种技巧来把这些档案挂上去。

但是你知道吗? 就算这些档案里面只写了这几行注解:

 

  1
 2
 3
 # Place all the behaviors and hooks related to the matching controller here. # All this logic will automatically be available in application.js. # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

 

而且实际执行结果也等于空输出。 compile 一支大概也要250ms。 你可以想想,多compile 10 支,就是2.5 秒了。 难怪超耗时。

可以使用.js 或.css 解决的不要用.scss 与.coffee 当结尾

 

  1
 2
 3
 4
 5
 Compiled jquery-ui-1.8.16.custom.css (0ms) (pid 19108) Compiled jquery.ui.1.8.16.ie.css (0ms) (pid 19108) Compiled jquery.js (5ms) (pid 19108) Compiled jquery_ujs.js (0ms) (pid 19108) Compiled custom.css (14ms) (pid 19108)

 

其中custom.css 的档名是custom.css.scss

这样应该知道为什么不要乱用scss 当档名了吧?

小结

为了方便大家调整,我把具体加速assets precompile 过程的步骤罗列在下面。

1. 换掉deploy.rb 的assets precompile tasks

2. 观察logs/product.log。

  1. 找出慢的assets。
  2. 拿掉直接使用import “comppass”; 的SCSS,改用功能针对性写法。
  3. 不需要使用@import 写法的改用require
  4. 拿掉require_tree,改用//=require 一行一行挂上去
  5. 删掉空的scss 与coffeescript
  6. 单纯只是CSS 的不要自作聪明帮忙加上.scss 档名。

====

如果有什么问题,欢迎各位在底下留言讨论。

也欢迎大家有空来Rails Tuesday坐坐。 我很乐意帮大家解答问题。

PS如果你是要问Rails 101书上的问题,请找小蟹。