エイチームライフスタイルWebエンジニアの@okonomiと申します。
Ateam Lifestyle Advent Calendar 2017、15日目をお送りします。
はじめに
弊社ではRails製のWebサイトを複数運用しており、最近それらのサイトに徐々にWebpackerでReactを導入し始めています。
導入自体は他のエンジニアが進めているのですが、自分でも手順などを知っておきたくて試してみました。
webpacker gemを追加してHello Reactするところまでは楽勝でしたが、そこから実際にアプリを書き始めるとつまづくポイントがいくつかありました。
もしこれからWebpackerを導入しようというときに、何か参考になれば幸いです。
また、試行錯誤の結果をgithubに公開していますのでよろしければ合わせてご覧ください。
https://github.com/okonomi/rails-webpacker-sample
環境など
- macOS High Sierra
- Ruby 2.4.2
- Node.js v8.9.0
- Rails 5.1.2
- Webpacker 3.1.1
Webpackerは現在も活発に開発が進められています。
そのため、バージョンによって挙動が食い違う可能性があります!
本記事では執筆時点で最新の3.1.1を対象にしています。
その1: FontAwesomeが読み込まれない
現象
コンポーネントの見栄えを良くしようとFontAwesomeを導入しようと思い、
react-fontawesomeのサンプルコードどおりに記述したのですがフォントが表示されません…
import React from 'react'
import FontAwesome from 'react-fontawesome'
import faStyles from 'font-awesome/css/font-awesome.css'
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {
likes: 0,
};
}
render() {
return (
<button onClick={() => {this.setState({likes: this.state.likes +1})}}>
<FontAwesome
name="thumbs-up"
cssModule={faStyles} // CSSを渡しているが効かない!
/> {this.state.likes}
</button>
);
}
}
原因・対策
Webpackerのデフォルト設定ではCSS Modulesが有効になっていませんでした。
上記コードはFontAwesome
コンポーネントに対してCSS Modulesでスタイルを指定していますが、CSS Modulesが有効になっていないためfaStyles
がnull
になってしまっていました。
CSS Modulesを有効にする方法はCHANGELOGに記載がありました。
https://github.com/rails/webpacker/blob/master/CHANGELOG.md#310---2017-12-11
const { environment } = require('@rails/webpacker')
// Enable css modules with sass loader
const sassLoader = environment.loaders.get('sass')
const cssLoader = sassLoader.use.find(loader => loader.loader === 'css-loader')
cssLoader.options = Object.assign(cssLoader.options, {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
})
module.exports = environment
environment
からCSS Loaderのオブジェクトを取り出してオプションを追加します。
environment
はWebpackerにおけるwebpackの環境設定を統括するEnvironmentクラスのインスタンスで、実体はWebpackerのpackage/environment.jsで定義されています。
CSS Loaderのオプションはpackage/rules/css.jsで指定されていますが、modules
フラグが有効化されていないので追加します。これでCSS Modulesが有効になります!
CSS Loaderに指定できるオプションの一覧はこちらで確認できます。
https://github.com/webpack-contrib/css-loader#options
また、上記設定を加えるとCSS Modulesは.scss
や .sass
に対して有効になりますので、FontAwesomeのファイルも.scss
に変更しておきます。
- import faStyles from 'font-awesome/css/font-awesome.css'
+ import faStyles from 'font-awesome/scss/font-awesome.scss'
あと最後に、viewにstylesheet_pack_tag
を書いてCSSファイルが読み込まれるようにします。
+ <%= stylesheet_pack_tag 'like_button' %>
補足
公式ドキュメントにもCSS Modulesを有効にする設定例が記載されています。
https://github.com/rails/webpacker/blob/master/docs/webpack.md#overriding-loader-options-in-webpack-3-for-css-modules-etc
こちらの設定もやっていることは同じで、違いは設定値の追加に使うのがObject.assign
かwebpack-mergeかだけです。
今回の用途ではwebpack-mergeは大げさかなと思いますが、どちらにするかは好みで良さそうです。
その2: turbolinks有効時にJSがうまく動かない
現象
Reactコンポーネントを設置したページを直接開くと問題ないのですが、トップページからReactコンポーネントを設置したページに遷移してくるとコンポーネントが起動しません。なぜ…
原因・対策
ページ読み込み完了を契機にコンポーネントを起動していたのですが、listenするイベントに問題がありました。
document.addEventListener('DOMContentLoaded', () => {
const elem = document.getElementById('like-button');
if (elem) {
ReactDOM.render(<LikeButton />, elem)
}
})
turbolinksは御存知の通りページ遷移時にコンテンツの内容だけを入れ替えることでページ遷移を高速化しますが、そのためDOMContentLoaded
イベントが発火しなくなります。
替わりにturbolinks側で用意されているturbolinks:load
イベントをlistenすることでページ遷移がキャッチできます。
- document.addEventListener('DOMContentLoaded', () => {
+ document.addEventListener('turbolinks:load', () => {
const elem = document.getElementById('like-button');
if (elem) {
ReactDOM.render(<LikeButton />, elem)
}
})
turbolinks側で用意されているイベントはこちら。
https://github.com/turbolinks/turbolinks#full-list-of-events
また、<body>
タグ内に<script>
や<style>
を置いてしまうとページ遷移時にJSが読まれてturbolinksの意味が薄れるので<head>
に移動しました。
その3: デプロイ時assets:precompile
がなぜか失敗
現象
bundle exec cap production deploy
でデプロイ実行すると、assets:precompile
の段階でwebpackのCompiling…から進まなかったり
エラー終了したりする現象が発生しました。
原因・対策
サーバのメモリ不足が原因でした。
デプロイ実行中にサーバ側のターミナルが「Cannot allocate memory」のようなメッセージを多発し始めたため、試しにdstat
でデプロイ時のメモリの推移を見てみると空きメモリがみるみる減っていました。
今回はVagrantでデプロイ対象のサーバを立ち上げていたのですが、無設定だとメモリが512MBだったので、Vagrantfile
を編集してメモリを1GBに増やしました。
Vagrant.configure("2") do |config|
# ...省略...
+ config.vm.provider "virtualbox" do |vb|
+ vb.memory = "1024"
+ end
# ...省略...
end
webpackのメモリ消費に関する情報はいくつか見かけるため、確証はないですが、webpackでビルドするときはサーバのメモリ容量に気をつけたほうがよさそうです。
また、公式ドキュメントにビルドパフォーマンスについてのページがあるので、ここを参考にすればメモリ使用量の改善ができるかもしれません。
https://webpack.js.org/guides/build-performance/
この辺り、詳しい方のご意見を伺いたいところです。
おわりに
以上、WebpackerでReactを導入するときにつまづいたことをまとめてみました。
Webpackerはフロントエンド開発の色々なややこしいことを肩代わりしくれますが、連携して動いているミドルウェアやライブラリを理解していないと、一見不可解な挙動に振り回されてしまうことも分かりました。
挙動をひとつひとつ把握しつつ、React導入を進めていこうと思います。
明日は新卒デザイナーの@xrxoxcxoxさんがAdobe XDについて書いてくれるようです。よろしくお願いします!
株式会社エイチームライフスタイルでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
http://www.a-tm.co.jp/recruit/