LoginSignup
38
38

More than 5 years have passed since last update.

Railsを使ってSingle Page Applicationを作るときの問題と対応

Posted at

はじめに

スマホ用のSingle Page Applicationサイトを作る上での問題点と対応SinglePageApplicationにおける問題点と対応につづく第3弾としてRailsをつかったSPA(Single Page Application)のサイトSmartFXを運用している際に気づいたことについてまとめてみました。

開発環境のリロードに時間がかかる

理由

SPAは大量にJSのファイルが作成されるので、それをひとつひとつ読み込むのに時間がかかりすぎる。

対応

development環境でもjsをconcatさせる

config/environments/development.rb
config.assets.debug = false

デプロイ途中にブラウザアクセスすると画面が真っ白

理由

縮退でWebサーバに反映(ロードバランサからWebサーバを切り離して、切り離したWebサーバにdeployし、反映した後で再度ロードバランサに繋ぎなおすことで各Webサーバを更新する方法)した際に、htmlは最新のサーバ、jsが古いWebサーバに見にいってしまい、画面が表示されなくなる瞬間がある。

対応

asset_syncを使うことにより、assets compile後のファイルをS3に置くことでjsやcssがないエラーを回避できる。
1.Gemfileにgem asset_syncを追加しbundle install
2.下記を実行

rails g asset_sync:install --provider=AWS

3.下記ファイルを修正

config/initializers/asset_sync.rb
AssetSync.configure do |config|
  config.fog_provider = 'AWS'
  config.aws_access_key_id = Access Key Id
  config.aws_secret_access_key = Secret Access Key
  config.fog_directory = Bucket Name
  config.gzip_compression  = true
  config.fog_region = "ap-northeast-1"
end

4.下記ファイルを修正

config/environments/production.rb
  config.action_controller.asset_host = "//#{Bucket Name}.s3.amazonaws.com"

参照(https://github.com/rumblelabs/asset_sync)

jsでimageのパスを書き込むとnot foundになる

理由

assetsはdigestがつくので、ファイルが更新される度にpassが変わる

対応

erbを使って、rubyの評価結果をjsの定義にする。
assets以外でも定義している定数もjsに変換することで定義箇所を一箇所にする。
ただし、定数を変更しても、definition.js.erb自体が変更していないとcompileし直されないので、その場合はコメントにrevisionなどを作って更新する。

app/assets/javascripts/definition.js.erb
//revision: 2
window.MyApp = {
  assets:{
    "no-image": "<%= asset_path("no-image.png") %>",
    "logo": "<%= asset_path("logo.png") %>"
  }
}
window.MyApp.Settings = <%= Settings.to_json.html_safe %>

画像をuploadするとCPUの負荷が100%になる

理由

サムネイル作成など、画像の圧縮処理の負荷が高い。

対応

クアイアント側で圧縮する。
html5のFile APIとcanvasを使えば、ブラウザでも画像を圧縮させることができ、通信量も減らせる。
@stomitaさんのMega pixel image rendering libraryを使えば簡単。

<form>
  <input id="afile" type="file">
  <input id="save" type="submit">
</form>
<canvas id="canvas1" style="display:none"></canvas>
class PhotoUpdateView extends Backbone.View
  events:
    "click #save":  "save"
    "change #file": "updatePhoto"

  updatePhoto: (e)->
    files = e.target.files
    if files.length == 0
      return
    @mpImg = new MegaPixImage(files[0])

  save: (e)->
    fd = new FormData()
    @mpImg.render(@$("#canvas1")[0], { maxWidth: 150, maxHeight: 150})
    ctx = canvas.getContext('2d')
    src = ctx.canvas.toDataURL('image/png', .9)
    fd.append('photo', src)
    $.ajax
      url: /photos
      type: "POST"
      contentType: false
      processData: false
      data: fd
      success: (obj) =>
        alert("success")
      error: (xmlHttpRequest, textStatus, errorThrown) =>
        alert(xmlHttpRequest.responseJSON.message)
app/models/photo.rb
class Photo < ActiveRecord::Base
  mount_uploader :image, PhotoUploader
app/models/photos_controller.rb
class PhotosController < ApplicationController
  def create
    photo = Photo.new
    file = Tempfile.new(['profile',".png"])
    file.binmode
    file.write Base64.decode64(params[:photo].sub!('data:image/png;base64,', ''))
    photo.image = file
    phto.save!
  rescue => e
    render :json => {:message => e.message}, :status => 400
  ensure
     file.close if file
     file.unlink if file  # deletes the temp file
  end
end

turbolinkをoffにしたい

理由

SPAでは必要ない

対応

Gemfileから gem 'turbolink'を消し、application.jsの//= require turbolinks を削除

まとめ

今回は、正直Single Page Applicationあんまり関係ないです。
なんとなく流れで。。

38
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
38