リモートワークでチーム開発する時に便利なツール・ドキュメントまとめ
Webサービスをチームで開発する時に便利だなと思ったツールや、ドキュメントをまとめてみました。 これからプロジェクトを始める時の参考にブックマークして使ってもらえると、嬉しいです。
プロジェクトの説明資料
- Googleドキュメントが多いです
- 作る目的やターゲットユーザー、機能を明記しておきましょう
ER図
- システムの全体像を把握するために必須
- 新しくエンジニアが加入した時のために必ず用意しておきましょう
MySQL Workbenchを使うと既存のデータベースをそのままER図に出力できるので、とても便利です。
ワイヤーフレーム & 画面遷移図
デザイン共有ツール
- ワイヤーフレームからデザインが固まったらデザインカンプを共有するわけですが、いちいち画像をダウンロードするのは面倒です
- そこでデザインデータをオンラインで共有できるZeplinというツールがあるのでこれを使うようにしています。
モックアップ
- これはデザインの画像同士を繋げて画面遷移を試せるサービスです
- 実際にサービスを触りながら雰囲気が掴めるので、オススメです
環境構築手順書
シードデータ
- Webサービスを最低限動かすために最初から必ず必要となるデータです
- これが無いと、自分で一つずつデータを入れる必要があるので必須です
自動テスト環境 (CI)
- 書いたコードが実行できるかを担保する上でテストは必須です
- ロジックが多めになるサーバーサイドは必ず書きましょう。
- 自動テストツールとしてはCircleCIが手軽でオススメです。
コード解析ツール
- コードの品質を保つためにコード解析ツールも必須です
- 国内だとSideciがオススメです
動作確認用サーバー
- 実際に今作っているサービスを動かして試す環境です
- HTML/CSSのみの場合と、データ以外は本番環境と全て同じのステージング環境を用意する場合があります
コード共有
チャットツール
タスク管理
開発者同士で使うなら、Trello, Waffle, Pivotal Tracker等色々とありますが、 Slackとビデオ会議で情報共有がしっかりと出来てれば、ぶっちゃけ何でもいい気がしてます笑
ActiveRecordのEnum型のデータをJavaScriptから保存する
enum(列挙型)の使い方
ActiveRecordのenumを使うと、プログラムからは文字列(名前)でアクセスでき、 DBには数値で保存される属性を作ることができます。
Modelに属性にセットできる文字列を列挙する
class Post < ActiveRecord::Base enum status: { draft: 0, published: 1 deleted: 2 } end
DBにカラムを追加する
class AddStatusToPostss < ActiveRecord::Migration def change add_column :postss, :status, :integer, default: 0 add_index :posts, :status end end
追加されるメソッド
モデル名.属性名
で現在の値を文字列で取得モデル名.文字列?
で状態チェックモデル名.文字列!
で状態変更モデル名.属性の複数形
で列挙した値を全てハッシュで取得モデル名.文字列
でその状態のレコードを検索
post = Post.new(status: :draft) post.status # => draft post.draft? # => true post.published! # statusをpublishedに変更 Post.statuses # => { draft: 0, published: 1 } # Post.statuses[:draft]のようにして数値を得られる Post.draft
JSから保存する
JSからAjaxでRails側に保存する時は文字列で送れば良い。
data: { status: 'draft' }
文字列にしておけばEnum型が自動で数値に変換してDBに保存してくれる。 JS側で数値を管理する必要がないので便利。
Reactでエンターキーのイベントを取得する方法
onEnterは存在しない
Reactがサポートしているイベントは以下の公式サイトに記載されています。
onClick
のノリで onEnter
は無いのかな?と探してみてもありません。
ただし、代わりになるキーイベントがあります。
onKeyDownを使う
それが onKeyDown
です。Propertiesを見ると、keyCodeが取得できるので
この値を判定すればエンターキーを判定できます。
https://facebook.github.io/react/docs/events.html#keyboard-events
キーイベントをJSXから受け取る
<textarea onKeyDown={ (e) => props.onKeyDown(e) } />
後はこんな感じでpropsで受け取ったイベントハンドラにイベントオブジェクトを引数で渡して実行します。
keyCode === 13で判定する
if (e.keycode === 13) { // エンターキー押した時の処理 }
あとはkeycodeの中身を判定するだけですね。簡単です。
Webアプリの新規作成の実装パターン
一覧 -> 作成画面に遷移して新規作成
フォームでPOSTして、エラーならエラーメッセージを表示、成功したら一覧画面にリダイレクトさせるというオーソドックスなパターン。
Flashメッセージの実装は必要。作成後にセッションに保存して一覧画面で取得したらすぐに消すような仕組み。大体フレームワークに用意されてることが多い。
バリデーションをどうするかも重要。 サーバーサイドでバリデーション結果をテンプレートに埋め込んで表示するタイプか、JSでバリデーションをリアルタイムに実装するか。
Vueでリアルタイムバリデーションして、システム側のエラーは例外。ユーザー起因のエラーはFlashメッセージがいいかな。
一覧からモーダルを開いて新規作成
非同期処理が完了したらJSで動的に一部の画面を切り替える
このパターンはユーザーに画面遷移をさせないで済み、変更がすぐに反映されるので一番理想。 実装方法としては以下の2つになると思う。
ただこの方法で厄介なのは、JS側でテンプレートを描写することになるため サーバーサイドのテンプレートエンジンが使えないこと。
最たる例がi18n対応。サーバーサイドで翻訳した結果を出力する形になっている場合、 JS側でもi18n用の機能を実装する必要が出てくる。
また非同期処理が完了するまで画面に何も表示されないというのも難点。 これを解決するにはローディングを出したり、取得中はDOM自体をださないとか、 gon等のライブラリを使ってDOMにJSの値を吐き出しておく等の方法がある。
またこの方法の場合、前述のFlashメッセージではなくトースターを表示する実装が必要になる。
ということからもわかるように、この方法はJSフレームワークのコード資産があまり無い段階では 実装コストが高めなのでスピードを優先する時はオススメできない。
非同期処理が完了したら画面をリロードさせる
このパターンは非同期処理が完了したら、location.href
で画面をリロードさせることで変更を反映させる。
データ数が多くなければ、そこまで違和感が無い。テンプレートエンジンも流用できる。
ただ作成した時のトースターを表示したい時は一工夫が必要。 非同期処理が成功したという情報をどこかに持っておかないといけない。
自分はグローバル変数は汚したくないので、ローカルストレージを使って実装することが多い。 ローカルストレージの実装は必要になるが、JSで動的に切り替えるよりは実装コストは低いのでオススメ。
JavaScriptのオブジェクトとJSONは別物
JavaScriptのオブジェクトとJSONは見た目がほとんど変わらないので、 違いがよくわかっていなかったのですが、明確に違いがあるようなのでメモしておきます。
JavaScriptのオブジェクト
キーがクォーテーション無し & 文字列をダブルクォーテーションで囲む
{ id: 0, title: "title", description: "desc" }
キーがクォーテーション無し & 文字列をシングルクォーテーションで囲む
{ id: 0, title: 'title', description: 'desc' }
キーをクォーテーションで囲むことも出来るけど、囲まない方が推奨。
JSON
キーをダブルクオーテーションで囲み、文字列をダブルクォーテーションで囲むのがJSON。 JavaScriptのオブジェクトでもある。
{ "id": 0, "title": "title", "description": "desc" }
JSONのフォーマットとして正しいかどうかは以下のツールを使うと、簡単に判定できるので一度使ってみてください。
JSON.stringify()とJSON.parse()
JSON.stringify
はJavaScriptのオブジェクトをJSON文字列に変換する関数。
JSON.parse
はJSON文字列をJavaScriptオブジェクトに変換する関数。
出力を見ると、JSON文字列はキーがどちらもダブルクォーテーションで囲まれていることがわかると思います。
var jsVar = { id: 1, title: "title" }; var jsonStr = JSON.stringify(jsVar); var jsParsed = JSON.parse(jsonStr); console.log(jsVar); // {id: 1, title: "title"} console.log(jsonStr); // {"id":1,"title":"title"} console.log(jsParsed); // {id: 1, title: "title"}
WebpackでReactを本番反映する時に行う設定
webpackの各種プラグインを導入
DefinePluginでprocess.env.NODE_ENVを置換する
特定の文字を任意の文字に置き換えることのできるプラグインです。
たとえば、
//webpak.config.js plugins: [ new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV': JSON.stringify('production') } }), ],
という定義があった時に、Reactのコード内の
if process.env.NODE_ENV !== ‘production’ { //開発環境用のコード }
は
if ‘production’ !== ‘production’ { //開発環境用のコード }
に置き換えられる。
この設定単体だとダメで、後述のUglifyJSPluginと組み合わせることで、 未到達のコードは削除されてファイル容量を削減することができます。
UglifyJsPluginでJSをminifyする
minifyとは、コメントや改行,スペースを削除したり、変数名を短くして無駄な部分を取り除くことです。
//webpak.config.js plugins: [ new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV': JSON.stringify('production') } }), new webpack.optimize.UglifyJsPlugin()
とすると、
WARNING in xx.js from UglifyJs Condition always false [./~/react/lib/React.js:29,0] Dropping unreachable code [./~/react/lib/React.js:29,0] Declarations in unreachable code! [./~/react/lib/React.js:30,0] Condition always false [./~/react/lib/React.js:44,0]
のような警告文が大量にでることがあるので、
//webpak.config.js plugins: [ new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV': JSON.stringify('production') } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, comments: require("uglify-save-license") })
にして、警告文を非表示にします。
またuglify-save-license
を使うことでライセンスのコメントは残す設定も入れておきましょう。
その他
あとは環境に応じて、APIのリクエスト先を変えるような場合 以下のようにして置き換えられるようにしておくと良いと思います。
//webpak.config.js plugins: [ new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV': JSON.stringify('production'), 'API_URL': 'https://api.xxx.com' } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, comments: require("uglify-save-license") })
ReduxのProviderコンポーネントとContainerコンポーネントを理解する
Providerコンポーネント
- react-reduxが提供するReactコンポーネント
- 唯一Storeを持つことができるコンポーネント
- 配下のコンポーネントにStoreに保持するstateやdispatcherを渡す役目
- Reduxでは1つのstateでアプリケーションの状態管理をするため、アプリケーション内に存在するProviderコンポーネントは1つのみ
const App = () => { return ( <Provider store={createStore(reducers)}> <View> <Header> <PostList> <Footer> </View> </Provider> ) }
Containerコンポーネント
- connect関数でReactコンポーネントをラッピングしたもの
- Connected Componentとも呼ぶ
- Reactでは「Stateを親コンポーネントに持たせる」と表現するが、それをReduxでは名前をつけただけ
- ただし、ReduxではStateはStoreに持つので、connectしてStateをPropsとして受け取る形になる
class PostList extends React.Component { render() { //ここにstateの中身がくる console.log(this.props); } } const mapStateToProps = state => { return { posts: state.posts }; } //PostListをconnectでラッピングしたここがContainerコンポーネント export default connect(mapStateToProps)(PostList);
このようにすることで、stateの中のpostsのみをpropsで受け取ることができる