JavaScript
Rails
SinglePageApplication

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

More than 3 years have passed since last update.


はじめに

スマホ用の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あんまり関係ないです。

なんとなく流れで。。