サーバサイドなおじさんが
SPAを趣味で初めて作ってみて
わかった n のこと(仮)
2015/07/11
関西Ruby会議06
無量井 健(@muryoimpl)
https://www.flickr.com/photos/bongonian/8534960933/
ここに提供
自己紹介
• 無量井 健(むりょうい けん)
• 永和システムマネジメント 10ヶ月目
• Ruby関西, 関西Ruby会議04・05 etc
• :white_large_square: 改め :b::b::a:
サーバサイドなおじさんが
SPAを趣味で初めて作って始めてみて
わかった n のこと(仮)
ハマった n のこと
2015/07/11
関西Ruby会議06
無量井 健(@muryoimpl)
https://www.flickr.com/photos/bongonian/8534960933/
使った技術要素(server)
Rails4.2
rails-api
jwt google-api-client
active_model_serializers
puma
hamlit
foreman
bootstrap-sass
poltergeist
使った技術要素(client)
React
react-router
webpack
jsUri
reqwest
cjsx
underscore
react-draggable
https://www.flickr.com/photos/thomasletholsen/8719637939/
盛大にハマったり
わかったりしたこと
n 個を紹介します
1
とあるRubyistに
電車の中で
SPAという単語を
使うとモヒカンが現れて
炎上しますよ
((((;゚Д゚))))
https://www.flickr.com/photos/alvarez-tostado/363243449/
SPAという単語は
用法・要領を守って
使いましょう
2
使った技術要素(client)
React
react-router
webpack
jsUri
reqwest
puma
cjsx
underscore
使った技術要素 React
• sprockets との共生を選択した(時間なくて)
• npm install react ̶save-dev => package.json
• webpack で結合した js を//=require で固める
• loaders(coffeescriptの慣れでcjsx使用)
• webpack.config.js 書くだけでいけそう(安易)
• 実際foreman 実行だけで済むので楽だった
使った技術要素 webpack
• 理由はreact-jade用のloaderがすでに
あったから
• でも、開発開始当時、依存関係で入る
react のバージョンが古かったので、
react-jadeは今回お見送り🙏
{
"name": "kpton",
̶̶̶̶ 中略 ̶̶̶
},
"dependencies": {
"cjsx-loader": "^2.0.1",
"coffee-loader": "^0.7.2",
"coffee-script": "^1.9.2",
"jsuri": "^1.3.1",
"jsx-loader": "^0.13.2",
"node-libs-browser": "^0.5.0",
"react": "^0.13.3",
"react-hot-loader": "^1.2.7",
"react-router": "^0.13.3",
"reqwest": "^1.1.6",
"underscore": "^1.8.3",
"webpack": "^1.9.7"
}
}
package.json
var webpack = require('webpack');
var path = require('path');
var assetsPath = path.join(__dirname, 'app', 'assets', 'client');
module.exports = {
// the base path which will be used to resolve entry points
context: __dirname,
// the main entry point for our application's frontend js
entry: {
main: [
'webpack/hot/only-dev-server',
path.join(assetsPath, 'main.cjsx')
]
},
output: {
path: path.join(__dirname, 'app', 'assets', 'javascripts'), // for Rails Assets Pipeline
filename: "[name]-bundle.js"
},
̶̶̶̶̶̶- 中略 ̶̶̶-̶̶̶̶̶
resolve: {
extensions: ['', '.js', '.jsx', 'cjsx', '.coffee']
},
module: {
loaders: [
{test: /.jsx$/, loaders: ['jsx-loader', 'react-hot']},
{test: /.coffee$/, loaders: ['coffee']},
{test: /.cjsx$/, loaders: ['coffee', 'cjsx']},
webpack.config.js
entry point なファイルを
指定する
上が配置場所
下が固めた後のファイル名
loaderの対象とする拡張子
どの拡張子にどのloaderを
割り当てるか
entry point な js
React = require 'react'
Router = require 'react-router'
routes = require './config/routes.cjsx'
Router.run routes, Router.HistoryLocation,
(Handler) ->
React.render(<Handler/>, document.body)
app/assets/client/main.js
ここでRailsのお決まり
!!!
%html{lang: :ja}
%head
%meta…
̶̶ 中略 ̶̶
%title KPT
= javascript_include_tag client'
%body
app/views/layouts/application.html.haml
ここに main.js 等React
使った js を固めている
全く動かない❗
https://www.flickr.com/photos/alvarez-tostado/363243449/
どうも main.jsの
手前に対象のDOMが
ないと
動かないらしい…
https://www.flickr.com/photos/alvarez-tostado/363243449/
サンプルどおりの位置に配置する
!!!
%html{lang: :ja}
%head
%meta…
̶̶ 中略 ̶̶
%title KPT
%body
= javascript_include_tag client'
app/views/layouts/application.html.haml
ここに配置すると問題なく
動いた
http://facebook.github.io/react/docs/tutorial.html
Reactのサンプル
http://facebook.github.io/react/docs/tutorial.html
Reactのサンプル
位置関係、大事であった…
3
使った技術要素(server)
Rails4
rails-api
jwt google-api-client
active_model_serializers
puma
hamlit
foreman
bootstrap-sass
poltergeist
使った技術要素 poltergeist
• acceptance test を turnip でやろう
• 折角なので、wercker で CI してみよう
• thecaddy/node-ruby の boxを使っ
てみる
• ローカルで動いたし、おっしゃ push…
謎のエラーを吐く
Capybara::Poltergeist::JavascriptError:
One or more errors were raised in the Javascript code on the page. If
you don't care about these errors, you can ignore them by setting js_errors:
false in your Poltergeist configuration (see documentation for details).
TypeError: 'undefined' is not a function (
evaluating 'ReactElementValidator.createElement.bind(
null,
type
)')
TypeError: 'undefined' is not a function (
evaluating 'ReactElementValidator.createElement.bind(
null,
type
)')
ちょっと調べてみると
poltergeist 1.x系
の問題らしい
https://www.flickr.com/photos/alvarez-tostado/363243449/
https://www.flickr.com/photos/alvarez-tostado/363243449/
• https://github.com/teampoltergeist/poltergeist/
issues/292
• 確かにローカルのphantomjsは 2.0.0 だった
• phantomjs2.0に対応していない環境もある
• facebook/react がpolyfillを用意しているらしい
• https://github.com/facebook/react/issues/
945#issuecomment-39694748
phantomjs 1.9系は
bind に対応していない
ただ今認証周りの
スタブが2回目実行時
に無反応になる問題に
直面中
(原因調査中)https://www.flickr.com/photos/alvarez-tostado/363243449/
4
使った技術要素(server)
Rails4
rails-api
jwt google-api-client
active_model_serializers
puma
hamlit
foreman
bootstrap-sass
poltergeist
痛恨のコピペミス…
# 当初 rails-api を使っていなかったが導入してみた。
# API 用の親クラスを作成しようとした
class ApiController < ActionController::API
# コピペをミスってActionController::APIに…
include ActionController::Serialization
end
痛恨のコピペミス…
class BoardsController < ApiController
# client 側の js からの request があったので
# 子クラスで render json を呼び出す
def index
render json: @current_user.boards
end
end
痛恨のコピペミス…
SystemStackError (stack level too deep):
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
app/controllers/api_controller.rb:8:in `current_user'
̶̶̶̶̶̶ これが 228 行続く…… ̶̶̶̶̶̶̶
マシンのメモリ8G
食いきって
カーソルが挙動不審
になりました💦
https://www.flickr.com/photos/alvarez-tostado/363243449/
OOM killer 発動!
で
finish👼
https://www.flickr.com/photos/alvarez-tostado/363243449/
5
使った技術要素(client)
React
react-router
webpack
jsUri
reqwest
puma
cjsx
underscore
React 使ってみた所感
• getInitialState, componentWillMount,
componentDidMount などの役割は名前から

イメージつきやすい感じする。FWレベル。
• render 内に、最上位のタグが1つしか書けないのが私は
意外にひっかかった。component != partial
• やはり view とロジックは分けて書きたくなるのと、html
の閉じタグを書きたくないでござる… react-jadeは開発

始めたときに対応している react が古くて諦めた。今は?
top level は1つのみ
React = require react
module.exports = React.createClass
render: ->
(
<div className= nav >
<p>あいうえおっすおっす</p>
</div>
<form className= inline-form >
</form>
)
top level は1つのみ
React = require react
module.exports = React.createClass
render: ->
(
<div className= nav >
<p>あいうえおっすおっす</p>
</div>
<form className= inline-form >
</form>
)
先に書かれているtop level のtagが
render されない…
partial とは違う…
あと
わかりにくい
エラー・警告が
多い(気がする…)
ReactMount:
Root element has been
removed from its original
container. New container:
null が大量に並ぶ
react consoleみると、
同じ要素が2つできてる…
https://github.com/rackt/react-router/issues/600#issuecomment-73364948
同じだ❗
https://www.flickr.com/photos/alvarez-tostado/363243449/
※平日 深夜4時の出来事である…
https://github.com/rackt/react-router/issues/600#issuecomment-74795259
https://www.flickr.com/photos/alvarez-tostado/363243449/
Reactがrender
する前の関数で
transitionすると
発生するみたい😱
https://www.flickr.com/photos/alvarez-tostado/363243449/
transitionTo
辞めると
確かに黙った😷
https://www.flickr.com/photos/thomasletholsen/8719637939/
まとめ
https://www.flickr.com/photos/thomasletholsen/8719637939/
まとめ
• n番煎じでした。
• ハマってみないと穴の深さはわからない
• まだ抜け出せてない穴がある
• 涙の数だけ強くなれるよ…
https://www.flickr.com/photos/thomasletholsen/8719637939/
無駄にハマらずに楽しく
開発できる人が増えると
いいなぁ
Enjoy
Programming!

サーバサイドなおじさんがSPAを趣味で初めて作ってみてわかった n のこと(仮)