Francis's Octopress Blog

A blogging framework for hackers.

3 招实用Asset Pipeline 加速 --by Xdite

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书上的问题,请找小蟹。