- 投稿日:2019-03-24T20:21:49+09:00
[翻訳]JavaScriptプロジェクトでいずれは学ぶ10のこと
以下は、The Cat with a Dragon Tattoo によって2018/8/3にMediumに投稿され、現在1万Clap以上受けている記事、"10 Things You Will Eventually Learn About JavaScript Projects"=「JavaScriptプロジェクトでいずれは学ぶ10のこと」の日本語訳です。
特定のライブラリを超えた、しかしフロントエンド開発者を主眼とした学びがまとまっており、僕がまさに読みたかったものでした。著者に感謝を。
表示 - 非営利 - 継承 4.0 国際 (CC BY-NC-SA 4.0)
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ja
JavaScriptは冒険です。
様々な業界で10年近く開発してきましたが、誰だってこれには同意してくれると思います。フロントエンド開発は、プログラマーである私たちに、多くの選択の自由、柔軟性、創造の余地を与えてくれます。しかし代わりに、ちょっとした知識、計画、責任が求められます。
jQuery、require.js、Angular、React、ExtJs、その他思い出せない(または思い出したくない)何十ものプロジェクトを経験してきましたが、想像もしなかった酷いモノを見てきました。きっと皆さんもそうでしょう。
しかしどんな時代でも、まとまりのないプロジェクトをどうにか扱いやすく変えられる「共通のパターン」がありました。以下では、その中でも特に重要な10のパターンを記します。個人的な経験から導いた意見ですが、経験豊富なエンジニアの多くは頷いてくれるのではないでしょうか。これらのパターンは、どんなフレームワーク・方法論・チームの規模であっても、ドキュメンテーションやリファクタリングの必要性を減らし、エンジニアの目から零れる涙を減らし、プロジェクトの盤石な基礎となってくれるでしょう。
あなたに新しい発見があり、これらのパターンを便利だと感じ、またこれらを使って素晴らしい何かを作ってくれることを願います!
1.分割統治
聞いたことはありながら、多くの人がこのルールを過小評価しています。CommonJS、Webpack、Nodeではコードを複数のファイルに分割できます。でも、何故わざわざそんな事をするのでしょう?
一貫性
プロジェクトを1つのexportを持つファイルに分割すれば、プロジェクトが大きくなった時、検索や依存性の管理がずっと簡単になります。「ファイルの名前はそれがexportするものを示す」というルールは直感的で、脳みそを疲弊させずにアーキテクチャを概観することができます。
管理
各exportを独立ファイルに分割することで、必要なときに素早く移動できるようになり、分業が促進されます。ヘルパー関数がアプリケーションの他の場所で必要になれば、
/sharedフォルダを作ってファイルを移動すれば良いわけです。そうすれば、また他の部分からも参照できます。2.恥ずかしいくらい明確に
変数、関数、ファイルの名前は、自分の子供の名前だと思って時間をかけて決めましょう。変数
xとすれば今日の0.3秒が節約できるかもしれませんが、1ヶ月後には2日かけて意味を解明し、4日かけてリファクタすることになります。先々のことを考え、長い名前を恐れないようにしましょう。ハック的な手法や、MITに入学したくなるような難しいやり方は避けましょう。その解決法は本当にスマートなのかもしれません。未来のあなたやチームメートも、コードの解読にたっぷり時間を費やした後で「賢いね!」と同意するかもしれません。しかし、物事をシンプルに保つことに集中し、なるべくドキュメントやコメントが必要ないようなコードを目指しましょう。
3.マジックナンバーやマジック文字列を解消する
どんなにやりたくなっても、マジックナンバー・マジック文字列は使ってはいけません。小さなもの、さして重要でないものでも、意味ある名前の変数に格納して、スコープの冒頭に置きましょう。
明示的にコードに書いた値は多くの場合どこかで再利用されます。すぐに変数に格納すれば、コードの重複は減り、修正は楽になり、値に意味を与えられます。
4.ネストと戦う
コードが横に120文字/縦に500行以上になるかif文が3層以上になったら、全力で分割しましょう。
深くネストされたif文を別々の関数、Promise、Observableに分割することで、条件文の複雑性を解消することができます。非同期呼び出しが多いなら、async/awaitも大幅にコードをシンプルにしてくれます。
5.configを頑張る
グローバル変数、APIエンドポイント、フィーチャートグル、サードパーティーの認証情報を使うなら、分離したconfigファイルに置きましょう。
WebでもNodeでも、config管理を助けてくれるconfigのようなパッケージが多数あります。あなたのアプリケーションはある時点でサーバーとローカル開発環境の両方で動くことになります。configファイルの作成は遅いより早いほうが簡単で、各環境ごとに挙動・使う認証情報・その環境で動く機能などなどを調整することができます。
6.フレームワークはあなたを助けるためにある
知っているから、人気だからという理由でフレームワークが使われている事がよくあります。
自分のプロジェクトにフレームワークが必要なのか、必要であればどれなのか、じっくり考えましょう。あなたのサイトがGitHubで10万starを獲得したフレームワークを活用していようがいまいが、エンドユーザーにとっては全くどうでも良いことです。
経験から、私はフレームワークを以下のように分類しています。React
コンポーネントベースのWebアプリケーションで、アーキテクチャを完全にコントロールしたい場合に使えます。Reactエコシステムでの開発は、時間もかかり、事前に十分な計画が必要です。十分な知識がある場合に限り、Reactは多くのメリットをもたらします。
Angular / VueJS / Ember
信頼性の高いWebアプリケーションを素早く作りたい場合に使えます。引き換えに大きなブラックボックスを抱えることになります。これらのフレームワークは多くの仕事をしてくれるので、自前アーキテクチャの利点・欠点の両方がなくなります。構造が厳しく決められているため、多少間違いをおかしてもReactほど困ることは少ないでしょう。
jQuery / lodash / その類型
Webページを素早く作りたくて、何キロバイトかファイルが増えても良い場合に使えます。開発時間を劇的に短縮してくれますが、メンテナンス不能なコードも書けてしまうので注意が必要です。
Vanilla JS / フレームワークなし
WebページでもWebアプリケーションでも、開発と計画に多くの時間が使える場合に使えます。ピュアなJavaScriptは、実験的なこと...WebGL,Worker,深い最適化,ブラウザアニメーション...を行うのに合っています。最後には自分が新しいフレームワークを作ることになるでしょう。トランスパイラを使えば、より良く軽量なjQueryの代替物にもなります。
このリストは1つの提案として受け取ってください。フレームワークを使うか、何を使うか、プロジェクトにとってベストな選択をじっくり考えましょう。
7.プロトタイプでないなら、テストを書く
単体テスト、スモークテスト、EtoEテスト、ざっとした確認...。
プロジェクトがすぐに書き換えられるプロトタイプで無い限り、テストを書くべきです。コードベースが複雑になるにつれて、管理・メンテナンスは大仕事になります。テストはそれを肩代わりしてくれます。未来のいつか、あなたはバグに遭遇し、青空を見上げて、テストを書いてくれた過去の自分に感謝することになります。新機能を追加したときに色々な所をひっそり壊してしまったことを、テストがなければ気づかなかっただろうからです。
8.バージョン管理システムを使う
プロトタイプだろうが、フルスケールの企業Webアプリケーションだろうが、小さな楽しい趣味プロジェクトだろうが、1行目のコードを書く瞬間からgitなどのバージョン管理システムを使いましょう。毎日コミットし、ブランチを使い、マージ・競合の解消・コミットの巻き戻しを学びましょう。意味のあるコミットメッセージを残しましょう。
バージョン管理によって、時間を移動し、壊したものを直し、過去の変更を見ることができます。この記事から1つだけ覚えてもらえるなら、日々バージョン管理システムの基本を学ぶこと、です。なぜなら、記事の残りの部分を無視して間違った道に進んでも、バージョン管理をしていれば直せるからです!それがなければ最初からやり直す運命になります。
9.確実な状態管理
状態管理のデザインパターンかライブラリを探して、自分の命がかかっているかのように齧りつきましょう。本当に命がかかる日がくるかもしれません。
フロントエンドエンジニアとして、通常私たちの大きな挑戦は2つだけです。データを表示することと、保管することです。長期間メンテナンスするという点では保管のほうが遥かに大変で、これを無視して開発するのと楽ができます。そして、数カ月後にプロジェクトは事実上メンテナンス不可能になるでしょう。
データの保管、すなわち状態管理は難しいものです。アプリケーションでは普通、サーバーがデータベースに持つデータとクライアントの画面に表示される内容が同期している必要があります。私たちのゴールは、その中間に置くJavaScriptで余計な複雑性を加えないことです。コンポーネントはサーバーと同じデータを提供し、ユーザーによる変更を同期させ、サーバー側の変更に追従しなければなりません。どうすればこれを実現できるでしょう?
React
Reactはとてもオープンなエコシステムを持っているため、ソリューションも十分にあります。FluxアーキテクチャにはRedux、observableベースならMobXがあります。それぞれメリット・デメリットがあります。使い始める前に各ライブラリの基礎を理解するようにしましょう。
Angular、Ember、VueJS
これらはビルドインで専用の状態管理の仕組みを持っており、コンセプトはObservableを基礎にしています。必須ではありませんが、追加のライブラリとしてngRx、Akita、Vuexがあります。
他のフレームワーク、Pure JavaScript
Redux、MobX、または独自のステート管理の仕組みを使えます。主なゴールは、アプリケーション全体で1つの「source of truth」(信頼できる情報源)を持つことです。この「情報源」は、サービスでも、ライブラリでも、単純なObservableでもありえます。
10.流行を疑う
最後に、コミュニティーに耳を傾け学びつつも、全ての記事、コメント、Mediumへの猫の長い投稿(訳注:この記事。著者のハンドルが猫)、あなたのコードへのフィードバックはよく吟味し、疑ってかかりましょう。フロントエンドの世界ではたくさんの新しいアイデアが勢いよく登場してきます。これらにはオープンな態度でありつつも、お祭り騒ぎを「追いかけるために追いかける」ことのないように気をつけましょう。こうした態度のために忘れさられてしまったプロジェクトが山ほどあります。
古く成熟したフレームワークで書かれたプロジェクトが、新しいものが出たからといって2つのフレームワークを混ぜたようなプロジェクトよりはるかに優れていることは珍しくありません。新しい流行の何かがアプリケーションや開発効率を少し向上させるとしても、一貫性には敵いません。メンテナンス性を保つために自分の最初の選択を守り、必要なときにだけ流行を取り入れましょう。
以上です!
ここまで読んでくれてありがとう。コメントであなたの意見や話を聞かせてくれるのを楽しみにしています。上述の通り、これらは私がJavaSriptとフロントエンド開発に関わる中で得られた一番大事な経験に過ぎません。私たちが経験する挑戦の大海のほんの一滴です。これらのパターンがあなたの役に立ちますように。
(訳終)
- 投稿日:2019-03-24T20:21:49+09:00
[翻訳]JavaSciptプロジェクトでいずれは学ぶ10のこと
以下は、The Cat with a Dragon Tattoo によって2018/8/3にMediumに投稿され、現在1万Clap以上受けている記事、"10 Things You Will Eventually Learn About JavaScript Projects"=「JavaSciptプロジェクトでいずれは学ぶ10のこと」の日本語訳です。
特定のライブラリを超えた、しかしフロントエンド開発者を主眼とした学びがまとまっており、僕がまさに読みたかったものでした。著者に感謝を。
表示 - 非営利 - 継承 4.0 国際 (CC BY-NC-SA 4.0)
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ja
JavaScriptは冒険です。
様々な業界で10年近く開発してきましたが、誰だってこれには同意してくれると思います。フロントエンド開発は、プログラマーである私たちに、多くの選択の自由、柔軟性、創造の余地を与えてくれます。しかし代わりに、ちょっとした知識、計画、責任が求められます。
jQuery、require.js、Angular、React、ExtJs、その他思い出せない(または思い出したくない)何十ものプロジェクトを経験してきましたが、想像もしなかった酷いモノを見てきました。きっと皆さんもそうでしょう。
しかしどんな時代でも、まとまりのないプロジェクトをどうにか扱いやすく変えられる「共通のパターン」がありました。以下では、その中でも特に重要な10のパターンを記します。個人的な経験から導いた意見ですが、経験豊富なエンジニアの多くは頷いてくれるのではないでしょうか。これらのパターンは、どんなフレームワーク・方法論・チームの規模であっても、ドキュメンテーションやリファクタリングの必要性を減らし、エンジニアの目から零れる涙を減らし、プロジェクトの盤石な基礎となってくれるでしょう。
あなたに新しい発見があり、これらのパターンを便利だと感じ、またこれらを使って素晴らしい何かを作ってくれることを願います!
1.分割統治
聞いたことはありながら、多くの人がこのルールを過小評価しています。CommonJS、Webpack、Nodeではコードを複数のファイルに分割できます。でも、何故わざわざそんな事をするのでしょう?
一貫性
プロジェクトを1つのexportを持つファイルに分割れば、プロジェクトが大きくなった時、検索や依存性の管理がずっと簡単になります。「ファイルの名前はそれがexportするものを示す」というルールは直感的で、脳みそを疲弊させずにアーキテクチャを概観することができます。
管理
各exportを独立ファイルに分割することで、必要なときに素早く移動できるようになり、分業が促進されます。ヘルパー関数がアプリケーションの他の場所で必要になれば、
/sharedフォルダを作ってファイルを移動すれば良いわけです。そうすれば、また他の部分からも参照できます。2.恥ずかしいくらい明確に
変数、関数、ファイルの名前は、自分の子供の名前だと思って時間をかけて決めましょう。変数
xとすれば今日の0.3秒が節約できるかもしれませんが、1ヶ月後には2日かけて意味を解明し、4日かけてリファクタすることになります。先々のことを考え、長い名前を恐れないようにしましょう。ハック的な手法や、MITに入学したくなるような難しいやり方は避けましょう。その解決法は本当にスマートなのかもしれません。未来のあなたやチームメートも、コードの解読にたっぷり時間を費やした後で「賢いね!」と同意するかもしれません。しかし、物事をシンプルに保つことに集中し、なるべくドキュメントやコメントが必要ないようなコードを目指しましょう。
3.マジックナンバーやマジック文字列を解消する
どんなにやりたくなっても、マジックナンバー・マジック文字列は使ってはいけません。小さなもの、さして重要でないものでも、意味ある名前の変数に格納して、スコープの冒頭に置きましょう。
明示的にコードに書いた値は多くの場合どこかで再利用されます。すぐに変数に格納すれば、コードの重複は減り、修正は楽になり、値に意味を与えられます。
4.ネストと戦う
コードが横に120文字/縦に500行以上になるかif文が3層以上になったら、全力で分割しましょう。
深くネストされたif文を別々の関数、Promise、Observableに分割することで、条件文の複雑性を解消することができます。非同期呼び出しが多いなら、async/awaitも大幅にコードをシンプルにしてくれます。
5.configを頑張る
グローバル変数、APIエンドポイント、フィーチャートグル、サードパティーの認証情報を使うなら、分離したconfigファイルに置きましょう。
WebでもNodeでも、config管理を助けてくれるconfigのようなパッケージが多数あります。あなたのアプリケーションはある時点でサーバーとローカル開発環境の両方で動くことになります。configファイルの作成は遅いより早いほうが簡単で、各環境ごとに挙動・使う認証情報・その環境で動く機能などなどを調整することができます。
6.フレームワークはあなたを助けるためにある
知っているから、人気だからという理由でフレームワークが使われている事がよくあります。
自分のプロジェクトにフレームワークが必要なのか、必要であればどれなのか、じっくり考えましょう。あなたのサイトがGitHubで10万starを獲得したフレームワークを活用していようがいまいが、エンドユーザーにとっては全くどうでも良いことです。
経験から、私はフレームワークを以下のように分類しています。React
コンポーネントベースのWebアプリケーションで、アーキテクチャを完全にコントロールしたい場合に使えます。Reactエコシステムでの開発は、時間もかかり、事前に十分な計画が必要です。十分な知識がある場合に限り、Reactは多くのメリットをもたらします。
Angular / VueJS / Ember
信頼性の高いWebアプリケーションを素早く作りたい場合に使えます。引き換えに大きなブラックボックスを抱えることになります。これらのフレームワークは多くの仕事をしてくれるので、自前アーキテクチャの利点・欠点の両方がなくなります。構造が厳しく決められているため、多少間違いをおかしてもReactほど困ることは少ないでしょう。
jQuery / lodash / その類型
Webページを素早く作りたくて、何キロバイトかファイルが増えても良い場合に使えます。開発時間を劇的に短縮してくれますが、メンテナンス不能なコードも書けてしまうので注意が必要です。
Vanilla JS / フレームワークなし
WebページでもWebアプリケーションでも、開発と計画に多くの時間が使える場合に使えます。ピュアなJavaScriptは、実験的なこと...WebGL,Worker,深い最適化,ブラウザアニメーション...を行うのに合っています。最後には自分が新しいフレームワークを作ることになるでしょう。トランスパイラを使えば、よく良く軽量なjQueryの代替物にもなります。
このリストは1つの提案として受け取ってください。フレームワークを使うか、何を使うか、プロジェクトにとってベストな選択をじっくり考えましょう。
7.プロトタイプでないなら、テストを書く
単体テスト、スモークテスト、EtoEテスト、ざっとした確認...。
プロジェクトがすぐに書き換えられるプロトタイプで無い限り、テストを書くべきです。コードベースが複雑になるにつれて、管理・メンテナンスは大仕事になります。テストはそれを肩代わりしてくれます。未来のいつか、あなたはバグに遭遇し、青空を見上げて、テストを書いてくれた過去の自分に感謝することになります。新機能を追加したときに色々な所をひっそり壊してしまったことを、テストがなければ気づかなかっただろうからです。
8.バージョン管理システムを使う
プロトタイプだろうが、フルスケールの企業Webアプリケーションだろうが、小さな楽しい趣味プロジェクトだろうが、1行目のコードを書く瞬間からgitなどのバージョン管理システムを使いましょう。毎日コミットし、ブランチを使い、マージ・競合の解消・コミットの巻き戻しを学びましょう。意味のあるコミットメッセージを残しましょう。
バージョン管理によって、時間を移動し、壊したものを直し、過去の変更を見ることができます。この記事から1つだけ覚えてもらえるなら、日々バージョン管理システムの基本を学ぶこと、です。なぜなら、記事の残りの部分を無視して間違った道に進んでも、バージョン管理をしていれば直せるからです!それがなければ最初からやり直す運命になります。
9.確実な状態管理
状態管理のデザインパターンかライブラリを探して、自分の命がかかっているかのように齧りつきましょう。本当に命がかかる日がくるかもしれません。
フロントエンドエンジニアとして、通常私たちの大きな挑戦は2つだけです。データを表示することと、保管することです。長期間メンテナンスするという点では保管のほうが遥かに大変で、これを無視して開発するのと楽ができます。そして、数カ月後にプロジェクトは事実上メンテナンス不可能になるでしょう。
データの保管、すなわち状態管理は難しいものです。アプリケーションでは普通、サーバーがデータベースに持つデータとクライアントの画面に表示される内容が同期している必要があります。私たちのゴールは、その中間に置くJavaScriptで余計な複雑性を加えないことです。コンポーネントはサーバーと同じデータを提供し、ユーザーによる変更を同期させ、サーバー側の変更に追従しなければなりません。どうすればこれを実現できるでしょう?
React
Reactはとてもオープンなエコシステムを持っているため、ソリューションも十分にあります。FluxアーキテクチャにはRedux、observableベースならMobXがあります。それぞれメリット・デメリットがあります。使い始める前に各ライブラリの基礎を理解するようにしましょう。
Angular、Ember、VueJS
これらはビルドインで専用の状態管理の仕組みを持っており、コンセプトはObservableを基礎にしています。必須ではありませんが、追加のライブラリとしてngRx、Akita、Vuexがあります。
他のフレームワーク、Pure JavaScript
Redux、MobX、または独自のステート管理の仕組みを使えます。主なゴールは、アプリケーション全体で1つの「source of truth」(信頼できる情報源)を持つことです。この「情報源」は、サービスでも、ライブラリでも、単純なObservableでもありえます。
10.流行を疑う
最後に、コミュニティーに耳を傾け学びつつも、全ての記事、コメント、Mediumへの猫の長い投稿(訳注:この記事。著者のハンドルが猫)、あなたのコードへのフィードバックはよく吟味し、疑ってかかりましょう。フロントエンドの世界ではたくさんの新しいアイデアが勢いよく登場してきます。これらにはオープンな態度でありつつも、お祭り騒ぎを「追いかけるために追いかける」ことのないように気をつけましょう。こうした態度のために忘れさられてしまったプロジェクトが山ほどあります。
古く成熟したフレームワークで書かれたプロジェクトが、新しいものが出たからといって2つのフレームワークを混ぜたようなプロジェクトよりはるかに優れていることは珍しくありません。新しい流行の何かがアプリケーションや開発効率を少し向上させるとしても、一貫性には敵いません。メンテナンス性を保つために自分の最初の選択を守り、必要なときにだけ流行を取り入れましょう。
以上です!
ここまで読んでくれてありがとう。コメントであなたの意見や話を聞かせてくれるのを楽しみにしています。上述の通り、これらは私がJavaSriptとフロントエンド開発に関わる中で得られた一番大事な経験に過ぎません。私たちが経験する挑戦の大海のほんの一滴です。これらのパターンがあなたの役に立ちますように。
(訳終)
- 投稿日:2019-03-24T12:34:12+09:00
React Static 設定大全
Reactベースのイケてる静的サイトジェネレータ「React Static」の翻訳シリーズの続編です。
基礎概念編 https://qiita.com/IYA_UFO/items/b7946674a782374f7eb2
React Staticの殆どの設定は
static.config.jsに書くことになり、こちらにツールとして学習が必要な内容が集中しています。
逆に言うと他はほぼReactです!ということで、以下は公式ドキュメント「設定
static.config.js」編の抄訳です。MIT License
Copyright (c) 2013-present, Nozzle, Inc.
https://github.com/nozzle/react-static/blob/master/LICENSE
static.config.jsファイルの設置は任意ですが、React Staticの機能をフル活用するためには設置をおすすめします!
プロジェクトのルートに置き、1つのオブジェクトをdefault exportさせてください。
そのオブジェクトは、以下から必要なプロパティを持つことができます。訳注
利用頻度が少なそうなプロパティについては訳していません。
完全なリストは本家へ https://github.com/nozzle/react-static/blob/master/docs/config.md
- getRoutes
- route
- getSiteData
- siteRoot
- stagingSiteRoot
- basePath
- stagingBasePath
- devBasePath
- assetsPath
- Document
- webpack
- paths
getRoutes
routeオブジェクトの配列をresolveする非同期の関数です。動的なデータや情報のリクエストは全てここに設定します。Booleanの引数devは、Productionビルド中であるかどうかを示します。export default { getRoutes: async ({ dev }) => [...routes], }開発サーバー起動中に
static.config.jsを更新した場合、getRoutesは自動で再度実行され、以下で説明するroutesやrouteDataへの変更はhot reloadされます。
以下ではこのプロパティで扱うrouteオブジェクトについて解説します。
route
routeはサイトの特定のパスを示すオブジェクト。React staticサイトの屋台骨となる設定。
プロパティは以下
path...文字列このルートにマッチするパス。
siteRoot + basePathからの相対パスで、アンカーリンクやクエリパラメータは除いたもの。
子ルートの場合は、親ルートからの相対パス。
component...文字列このルートをレンダリングするためのコンポーネントへのパス。
プロジェクトルートからの相対パス。
getData...非同期関数
(resolvedRoute, { dev }) => Object
このルートに必要なデータの配列をreturnまたはresolveする非同期関数。
引数は
- resolvedRoute...オブジェクト この関数が扱うルート
- flags...オブジェクト ビルドについてのメタ情報やフラグが入っている
- dev...真偽値 開発環境か本番ビルドかを示す
children...routeオブジェクトの配列配下の子ルート。パスは親から引き継がれるので、この中でprefix部分を繰り返す必要はない。
redirect...URLセットすると、
http-equivを利用した301リダイレクトと同じ挙動をし、ページはリダイレクト以外何もしない最低限のHTMLになる。
noindex...真偽値デフォルトでfalse。このルートと子ルートを自動生成される
sitemap.xmlに含みたくない場合はtrue。例:
export default { getRoutes: async ({ dev }) => [ // A simple route { path: 'about', component: 'src/containers/About', }, // A route with data { path: 'portfolio', component: 'src/containers/Portfolio', getData: async () => ({ portfolio, }), }, // A route with data and dynamically generated child routes { path: 'blog', component: 'src/containers/Blog', getData: async () => ({ posts, }), children: posts.map(post => ({ path: `post/${post.slug}`, component: 'src/containers/BlogPost', getData: async () => ({ post, }), })), }, // A 404 component { path: '404', component: 'src/containers/NotFound', }, ], }getSiteData
getSiteDataはルートのgetData関数に似ていますが、結果はSiteDataコンポーネントとgetSiteDataHOCによってサイト全体で利用できます。ここから返されるデータはセッションに1回だけロードされますが、全てのページに埋め込まれます。慎重に使ってください。例:
export default { getSiteData: async ({ dev }) => ({ title: 'My Awesome Website', lastBuilt: Date.now(), }), }siteRoot
protocol://domain.comの形で設定します。SEO対策のため、設定を強くおすすめします。
この設定により、以下が起きます
- 自動で
sitemap.xmlを出力します- 静的なリンクを絶対パスに変換します。サイトがhttpsで配信される場合、こちらに必ず書いてください。リンクのパス末尾のスラッシュは自動的に削除されます。ベース・パスを設定する(例えばGithub Pagesを使っている場合)には、ここではなく、
bathPathオプションを使ってください。例:
export default { siteRoot: 'https://mysite.com', }stagingSiteRoot
siteRootと同じ機能ですが
--stagingフラグがついている場合のみ有効になります。basePath
some/routeの形で書きます。 ドメインのルート以外でサイトをホストする場合に使います。例えばGitHub Pagesでホストする場合、サイトはhttps://mysite.com/blogでホストされるので、basePathはblogとなります。例:
export default { basePath: 'blog', }stagingBasePath
basePathと同じ機能ですが、
--stagingフラグがついている場合のみ有効になります。devBasePath
basePathと同じ機能ですが、ローカル開発サーバーでのみ有効になります。
assetsPath
JSとCSSのロード先を指定します。アセットをCDNのような外部サービスでホストする場合に使えます。
Document
サイトのルートをレンダリングするコンポーネントです。ルートドキュメントのカスタマイズはかつてなく簡単になりました。
このコンポーネントには以下のようなことが書けるでしょう。
headやmetaタグの内容- サイト全体の計測スクリプト
- サイト全体に適用するスタイルシート
以下のpropsが与えられています。
Html...Reactコンポーネント
htmlタグ
Head...Reactコンポーネント
headタグ
Body...Reactコンポーネント
htmlタグ
body...Reactコンポーネント
bodyタグ
children...Reactコンポーネントサイトのメインコンテンツ。レイアウトやルートを含みます。
routeInfo...オブジェクト
routeDataを含む、現在のルートの情報。
siteData...オブジェクトこの設定ファイルの
getSiteDataから渡される全てのデータ。
renderMeta...オブジェクト
hookやtransformerでレンダリング中にセットされるデータです。例:
export default { Document: ({ Html, Head, Body, children, siteData, renderMeta }) => ( <Html lang="en-US"> <Head> <meta charSet="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> </Head> <Body>{children}</Body> </Html> ), }JSXを使うことになるので、この場合は
static.config.jsの先頭でimport React from 'react'してください。devServer
開発サーバーとして使われる
webpack-dev-serverインスタンスに渡されるオプションオブジェクトです。例:
export default { // An optional object for customizing the options for the devServer: { port: 3000, host: '127.0.0.1', }, }paths
React staticが使う内部的なファイルパスをカスタマイズするオブジェクトです。全てプロジェクトルートからの相対パスです。
デフォルトは以下:
export default { paths: { root: process.cwd(), // プロジェクトのルート。よく分からなければ変更しないでください。 src: 'src', // ソースフォルダ。index.jsをエントリーポイントとして含む必要あり。 temp: 'tmp', // ビルドで発生する公開しないファイルの置き場。 dist: 'dist', // 本番用出力フォルダ。 devDist: 'tmp/dev-server', // The development scratch directory. public: 'public', // The public directory (files copied to dist during build) assets: 'dist', // The output directory for bundled JS and CSS buildArtifacts: 'artifacts', // The output directory for generated (internal) resources }, }
続編 React Static 設定大全 https://qiita.com/IYA_UFO/items/b01ca2eb1ec0082c4b79
- 投稿日:2019-03-24T11:28:16+09:00
Expo+Reduxアプリをデバックする方法を4つ比較
Expo+Reduxアプリをデバックするのに一番いい方法は何だ?
4つの選択肢を比較しました。redux-logger
Remote Redux Devtools
React Native Debugger
Reactotron結論から先に言うと、
React Native Debuggerを使うべし。セットアップガイドはこちら1. redux-logger
Redux-loggerは、どのようなアクションがfireされ、どのようにstateが変わったかをconsole.logしてくれるミドルウェアです。
Expo特有のセットアップ難もなく、Web時と同様に使えます。
短所は、読み専で、Redux Devtools Extensionのようなフルフルの機能がないことです。
あと、プロジェクト毎にnpm iする必要があります。
2.Remote Redux DevTools
Remote Redux DevToolsは、Redux Devtools Extensionのデスクトップアプリ版でReact Nativeと使えます。
セットアップコードはシンプルで、Webの時と似ています。
ただ、完全に同じではないので、Docsをよく読んでください。
あと、プロジェクト毎にnpm iする必要があります。
一番痛いのは、このRedux機能に加えて、必要な全てを授けてくれる他の選択肢があることです。それが、React Native Debugger.
3.React Native Debugger
React Native Debuggerは、なんとあの三種の神器、Chrome, React, Reduxのdevtoolsを一つのデスクトップアプリにまとめたものです。
プロジェクト毎にnpm iする必要がなく、セットアップのコードはwebと全く同じ。
一つ欠点は、公式のdocsが結構分かりにくくて、特にexpoと合わせると不具合があること。その時は、このガイドを見てください。
4.Reactotron
では、最後です。ReactotronはReact Nativeを総合的にinspectできるデスクトップアプリで、reduxのデバッグに限らず、色々できるそう。
ただ、Reduxデバッグを簡単にしたい!と言う今回の趣旨からすると、いくつか痛い点があります。
第一に、いわゆるredux devtoolsやreact devtoolsとはUIが異なり、学習コストがあります。
第二に、プロジェクト毎に複数のnpm iが必要になります。
npm install — save-dev reactotron-react-native reactotron-redux reactotron-redux-saga第三に、こんな具合に新規のセットアップコードを書かないといけません。
と言う訳で、結論、
React Native Debuggerは、三種の神器(Chorme, React, Redux devtools)を、プロジェクト毎の新しいパッケージなく、ミニマムなセットアップコードで使えると言う点で、最も優れていると思いました。
- 投稿日:2019-03-24T10:52:35+09:00
react-dndがステージ環境でレンダリングされなかった。
TL;DR
結果としてバージョン下げたらなおった。
起こったこと
react-dndを用いて、ドラッグ&ドロップを使ったreactのspaを作っていた
開発環境ではちゃんとレンダリングされる。ドラッグ&ドロップもちゃんとできる。
でも、ステージ環境にあげると画面が真っ白になり、何も表示されない。
verは当時(2019 3/22)での最新のものを使っていた。
- react 16.8.4
- react-dnd 7.3.2
- react-dnd-html5-backend 7.2.0
環境としては開発環境はwebpack-dev-server、ステージ環境はwebpackでバンドルしたファイルをs3にあげてcloudFrontでキャッシュしているよくある構成。
react-dndの仕様
react-dndではドラッグする対象DragSourceとドロップする対象DropTargetのコンポーネントを定義し、そのコンポーネントよりも
上位のコンポーネントにDragDropContextか、DragDropContextProviderのどちらかを差し込まないといけない。(DragDropContext、DragDropContextProviderの両方使うとCannot have two HTML5 backends at the same timeというエラーがでる)この
上位のコンポーネントというのは上位であればどこでもいいが、アプリケーションのルートのコンポーネントに差し込んでいる例が多かった。一応、readt-dndで画面のレンダリングがされないというissueは存在するが、DragDropContext、DragDropContextProviderをそもそも利用していないことが原因でレンダリングされていなかったというオチ。
https://github.com/react-dnd/react-dnd/issues/1132今回の事象はDragDropContext、DragDropContextProviderは利用しているにも関わらず、画面がレンダリングされないという事象だったので、あまりissueは役に立たなかった。(そもそも開発環境だと表示されている)
遭遇したエラー
ローカルの開発環境だとDragDropContext、DragDropContextProviderのどちらを使ってもちゃんと画面表示される。
でもステージ環境だとDragDropContextProviderを使っているときはUncaught Invariant Violation: Hooks can only be called inside the body of a function component.というエラーが出て画面が真っ白。
↓最新のDragDropContextProviderはhooksを使っているみたいで、ここで死んでた
DragDropContextを使うと、そもそもエラーも何もでず、画面が真っ白。
対処
react-dnd、react-dnd-html5-backendのverをちょっとずつ下げていってつど確認していった。
v4まで下げたら、ちゃんとステージ環境でもちゃんと動くようになった。"react-dnd": "^4.0.0", "react-dnd-html5-backend": "^4.0.0",react-dndは直近でガンガンverが上がっているので、v4とかでも2018年のもの
https://github.com/react-dnd/react-dnd/releases最後に
なんで治ったのかよくわからずじまいですが、結構ハマったので誰かの役に立てば、、
- 投稿日:2019-03-24T08:57:42+09:00
graphql-codegen で型定義を生成する (React, Apollo, TypeScript)
本記事はこのリポジトリでやったことのまとめです。
https://github.com/mizchi-sandbox/graphql-react-apollo-playground
GraphQL + TypeScript への課題感
TypeScript(に限らず他の静的型付の言語) と GraphQL を使うと、型の二重定義が発生がちです。折角 GraphQL に通信規約としての型を書いているのに、それを多重定義することで、運用の面倒臭さやバグの温床になりかねない、という懸念がありました。
今回は、graphql のスキーマとクエリを書くと、サーバー向けに resolver の型定義、クライアント向けにクエリの型定義を生成し、それによってできるかぎり型安全なコードを扱うのをゴールとします。
やり方
graphql-code-generator を使います。(というか今回は主にこのライブラリの紹介です)
https://graphql-code-generator.com
期待してるディレクトリ構造はこんな感じ
├── graphql │ ├── mutations │ │ ├── addUser.graphql │ │ └── deleteUser.graphql │ ├── queries │ │ ├── user.graphql │ │ └── users.graphql │ └── schema.graphql ├── client │ ├── index.html │ └── main.tsx ├── server │ ├── index.ts │ ├── package.json │ └── resolvers.ts └── codegen.ymlごちゃごちゃしてますが、
graphqlにスキーマ定義を
clientが クライアント実装を
serverが GraphQL サーバー実装です。
型を生成する
server/genに graphql resolver の型定義を、client/genに graphql の query と mutation の hooks API を生成するとします。パッケージが細かく別れていて、依存がやたら多いですが、この辺をインストールします。
yarn add -D @graphql-codegen/cli @graphql-codegen/introspection @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-resolvers @graphql-codegen/typescript-react-apollo
yarn graphql-codegen initで生成してもいいですが、今回は次のようなcodegen.ymlを書きました。overwrite: true schema: - ./graphql/schema.graphql documents: - ./graphql/queries/*.graphql - ./graphql/mutations/*.graphql generates: ./server/gen/graphql-resolver-types.ts: plugins: - typescript - typescript-resolvers ./client/gen/graphql-client-api.tsx: plugins: - typescript - typescript-operations - typescript-react-apollo config: withComponent: false withHooks: true withHOC: false ./graphql/schema.json: plugins: - introspection
- typescript-resolver を使ったリゾルバ用型定義
- typescript-react-apollo を使った React 向け型定義
- schema.json
の吐き出すことを意図してます。
yarn 経由でコマンドを叩いてコード生成
$ yarn graphql-codegen --config codegen.yml ✔ Parse configuration ✔ Generate outputsどういうコードが生成されたかは、使いながら確認していきます。
サーバーサイドのリゾルバーの型定義
typeorm を使った簡単な CRUD のサンプルです。(というか正直言うと今回作ったリポジトリは typeorm の練習だったんですが…)
生成された型定義によって
MutationResolversとQueryResolversが手に入るので、これによってリゾルバの実装に型をつけることができます。import { ulid } from 'ulid'; import { MutationResolvers, QueryResolvers, Resolvers, } from './gen/graphql-resolver-types'; import { User } from './entity/User'; const Query: QueryResolvers = { async user(_parent, args, _context, _info) { const user = await User.findOne({ id: args.id }); return user || null; }, async users() { const users = await User.find(); return users; }, }; const Mutation: MutationResolvers = { async addUser(_parent, args, _context, _info) { const newUser = new User(); newUser.id = ulid(); newUser.name = args.name; await User.save(newUser); return newUser; }, async deleteUser(_parent, args, _context, _info) { const user = await User.findOne(args.id); await User.delete(args.id); return user; }, }; export const resolvers: Resolvers = { Query, Mutation, };VSCode などで触ればわかるのですが、第二引数の args に型がつき、返り値にも型を要求されます
生成コードを見ると、次のようなコードが生成されているのがわかります。
//... export type MutationResolvers<Context = any, ParentType = Mutation> = { addUser?: Resolver<User, ParentType, Context, MutationAddUserArgs>, deleteUser?: Resolver<User, ParentType, Context, MutationDeleteUserArgs>, }; export type QueryResolvers<Context = any, ParentType = Query> = { user?: Resolver<Maybe<User>, ParentType, Context, QueryUserArgs>, users?: Resolver<Maybe<Array<User>>, ParentType, Context>, };これで resolver を安心して書くことが出来るようになりました。
クライアントで生成されたコードを使う
↑ で作成された graphql server を
http://localhost:3333で動かすのを前提としています。
各種セットアップは略。リポジトリを見てください。手を抜くのに parcel を使っています。生成されたコードをどう使っているか、次のコードの
import ... from './gen/graphql-client-api'と UserList の hooks に注目してくださいimport React from 'react'; import ReactDOM from 'react-dom'; import { ApolloClient } from 'apollo-client'; import { createHttpLink } from 'apollo-link-http'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { ApolloProvider } from 'react-apollo'; import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks'; import { useUsersQuery, useAddUserMutation, useDeleteUserMutation, } from './gen/graphql-client-api'; const client = new ApolloClient({ link: createHttpLink({ uri: 'http://localhost:3333/graphql', }), cache: new InMemoryCache(), }); function UserList() { const usersQuery = useUsersQuery(); const addUserMutation = useAddUserMutation(); const deleteUserMutation = useDeleteUserMutation(); return ( <> <h1>Users</h1> <ul> {!usersQuery.loading && usersQuery.data.users.map(user => { return ( <li key={user.id}> {user.id}:{user.name} <button onClick={async () => { await deleteUserMutation({ variables: { id: user.id } }); usersQuery.refetch(); }} > delete </button> </li> ); })} </ul> <button onClick={async () => { await addUserMutation({ variables: { name: Math.random().toString() }, }); usersQuery.refetch(); }} > addUser </button> </> ); } function App() { return ( <ApolloProvider client={client}> <ApolloHooksProvider client={client}> <UserList /> </ApolloHooksProvider> </ApolloProvider> ); } ReactDOM.render(<App />, document.querySelector('.root'));副作用を起こしたら
usersQuery.refetch()を発行しています。感想
スキーマからコードを生成したことで、自分で定義した部分は最小限で、かつ TypeScript の型チェックを効かせることができました。
GraphQLと型、なんか全体的にもったいない感覚がしてたのが、これによってだいぶ楽になった気がしていて、やっとプロダクションに突っ込むやる気が出てきた気がします。
厳密には、typeorm と GraphQL 間で依然として二重定義のような部分は残ってるのですが、ここは GraphQL は ORM ではないという点を鑑みて、分離したままにしておいたほうがいいと思っています。
GraphQL を ORM として使いたいなら prisma を使うのが楽です。


