- 投稿日:2019-02-18T13:27:24+09:00
TypeScriptでimport React from 'react'って書きたい!
書いてみる
import React from 'react';すると以下のエラー出る。。。
Module '"~~~~/node_modules/@types/react/index"' has no default export.(
~~~~
は各々のファイルの場所です。)以下にすればエラーが出なくなる。
import * as React from 'react';そういうことじゃない!!
tsconfigを直す
allowSyntheticDefaultImports: trueこれを足せばいいだけ!
https://www.typescriptlang.org/docs/handbook/compiler-options.html
上記からallowSyntheticDefaultImports
の説明を引用すると、、、Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
英語がわからないのでGoogle翻訳にぶん投げます。
デフォルトのエクスポートなしでモジュールからのデフォルトのインポートを許可します。これはコードの発行には影響せず、単に型チェックを行います。
has no default export
に対して解決してそうですね!めでたし!
- 投稿日:2019-02-18T07:43:14+09:00
create-react-appとTypeScriptでサラッと作ったSPAをgh-pagesにスルッとデプロイすっぞ!
SPAを手軽にリリースしたい!
昨今、ReactとかでサラッとSPA作ってる人多いじゃないですか!?
フロントエンドエンジニアでガンガンJS書いてて〜〜とかじゃない人でも日本語のドキュメントを少し漁ればなんとか作れちゃう時代になりつつありますよね。
だから私もReactで作ったものをリリースしたい!世に広めたい!そんなことを思うわけです。マークアップエンジニアに立ち塞がる「サーバサイド」の壁
デザインやマークアップをやっている人がFE領域に入り込もうとすると、どうしてもサーバのことがわかりません。
create-react-app
で確かにReactを書く環境は作れる。
ローカルサーバも立ち上がって表示確認はできる。
データをセットすればDOMを勝手にレンダリングしてくれる感動を得られる!
react-router-dom
を使ってルーティングまでも自分で実装できるようになってしまう!
テンションは上がる一方です!!でも、、、
これをどう世の中にリリースしたらいいか何もわからない!!!
詰みまくりですね。
救世主「GitHub Pages」
そんな私みたいなエンジニアに朗報です。
GitHubにサイトのリポジトリを作成し
gh-pages
というブランチを作ってそこにプッシュすると、なんとそれだけで静的なサイトが公開されてしまいます!詳しくは以下を参考にしてみてください。
https://www.tam-tam.co.jp/tipsnote/html_css/post11245.htmlまた、
create-react-app
で作り出せるものは、結局index.html
に<script>
でjsを読み込んだだけのものなので、静的なページというわけです。なので、ちゃんとルーティングを実装したとしても、GitHub Pageならサーバも借りずに自分のSPAをリリースすることができるわけです!!
(さすがにサーバサイドでのroutingだったり、DBを使うものは難しいです><)というわけで、実際に作ってみましょう!
作る上で使うもの
以下のものを使って作っていきます。
- GitHub
- node(v8.15.0
を使っています)
- npm(6.8.0
を使っています)
- create-react-app
- TypeScript
- Sass
- react-router-dom
- gh-pages私はTypeScriptとSassに慣れ親しんでいるので、これらが使える環境を用意したいと思います!笑
リポジトリ作成と環境構築
まず、GitHubの
GitHub Pages
を使うからにはここにリポジトリがないと始まらないので、会員登録とリポジトリ作成を行ってください。
GitHubはこちら。
会員登録をしたら、Repositories横の「New」というボタンから作成してください。
私は適当にreact-app-sandbox
というリポジトリ名にしました。そうしたら、
https://github.com/ユーザ名/react-app-sandbox.git
というのが出てくるのでそれをコピーします。
(リポジトリ名は、私が勝手につけた名前にしています。みなさんが別の名前をつけていればその名前になります。)次に、ターミナル等でローカルの好きなフォルダまで移動します。
私は~/Documents/git
に移動しています。(私はMacユーザです。)そこで以下のコマンドを叩きます。
git clone https://github.com/ユーザ名/リポジトリ名.gitGitHubのユーザ名とパスワードを聞かれた場合はそれを打ち込んでenterをターンッ!ください。
そうすると今いるフォルダの中にリポジトリ名のフォルダが出来上がります。
そのフォルダ内では、GitHubのリモートリポジトリにpushしたりpullしたりできるようになっています。ただ、まだそのフォルダには移動せずに、次に以下のコマンドを叩きます。
npx create-react-app react-app-sandbox --typescript
npx
を使って、一時的にcreate-react-app
をインストールして実行し、リポジトリ名と同じ名前でReact appを作成します。しかもTypeScript版で。
npx
を使えば、常に使うことのないcreate-react-app
を無駄にインストールしなくて良いので大変便利です。
(もしTypeScirptを使いたくない場合は、--typescript
をつけないで実行してください。)作り出されたリポジトリ名のフォルダに移動して、確認のために以下のコマンドを叩いてみましょう。
cd react-app-sandbox npm start
そうすると自動でブラウザが立ち上がり、
http://localhost:3000
にアクセスされて以下のページが出ればひとまず完成です!
Reactのロゴが回っていますね。Sassを書けるようにする
まだまだ私はSassから抜け出せないので、Sassでかけるように設定します。
src
フォルダの中は以下のようになっています。src ├── App.css ├── App.test.tsx ├── App.tsx ├── index.css ├── index.tsx ├── logo.svg ├── react-app-env.d.ts └── serviceWorker.tsとりあえずSassにしたいので、
index.css
とApp.css
の拡張子を無邪気にscss
に変えてみちゃいましょう!^^src ├── App.scss ├── App.test.tsx ├── App.tsx ├── index.scss ├── index.tsx ├── logo.svg ├── react-app-env.d.ts └── serviceWorker.tsこのように変えられたら、一度ターミナル上で
control + c
を押してnpm start
のプロセスを終了し、もう一度npm start
を実行してみましょう。npm start Failed to compile. ./src/App.tsx Module not found: Can't resolve './App.css' in '/Users/senshu/Documents/git/react-app-sandbox/src'エラーです^^世の中そううまくはいきません^^
App.tsx
はApp.css
をimportするように書かれているので、このファイル内の拡張子を変えてあげないといけません。
index.tsx
の中身も同様なので、ついでにindex.css
をindex.scss
に変えましょう。そしてもう一度
control + c
を押してからnpm start
を実行すると、、、なぜかうまくいく!!!世の中ーーーー!!!
これ
node-sass
を入れていないのでうまくいかないと思ったのですが、なぜかうまくいっちゃいました。。。
package-lock.json
にもnode-sass
は入っていないので、なんでなんだろうーって思っていまして、誰か詳しい方いたら教えてください。。とりあえずうまくいくので次にいきましょう!w
Routerを入れる
今回のテーマは「SPA」なので、ブラウザ側でルーティングできるようにしたいです。
URLを直で叩かれたら、そのURLのページがちゃんと表示されるようにしたいですよね。ただ、今回注意したいのは、サーバ側でルーティングさせられないという点です。
サーバ側のルーティングはGitHub側でコントロールされてしまっているので、そのURLに相当するファイルを用意しないと404
のページになってしまいます。
URLに相当するページを作るのももちろん良いですが、それはもうSPAではなくなってしまいます…笑
なので、今回はハッシュによるルーティングを実現させようと思います。
なのでURLがhttps://~~~~/#/hoge
という形になりますが、そこはご容赦ください><
(こうすればハッシュじゃなくてもできるよというやり方があれば、ぜひ教えてください。)ルーティングを実現させるために、プロジェクトルート(リポジトリ名のフォルダ)で以下のコマンドを叩きます。
npm i -S react-route-dom @types/react-router-dom
@types
はTypeScriptで書いているので入れています。ここから、Reactをちょこちょこいじっていきます。
以下のサイトを参考に作っていきますので、ぜひみなさんも参考にしてみてください。
https://reacttraining.com/react-router/web/ここで、もし
npm start
を終了している場合は、これから開発していくのでnpm start
を叩いた状態にしておいてください。あちこち行くと分かりづらいので、基本的に
App.tsx
をいじっていきます。App.tsximport React, { Component } from 'react'; import logo from './logo.svg'; import './App.scss'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } } export default App;上記は、
App.css
をApp.scss
に変えたこと以外は何も変更していないソースコードです。
これを以下のように変えます。App.tsximport React, { Component } from 'react'; // react-router-domから必要なものを追加 import { HashRouter, Switch, Route } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; class App extends Component { render() { return ( // 全体をHashRouterでWrap <HashRouter> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> {/* ルーティングがわかりやすいようにdivを追加 */} <div className="_RoutingArea"> {/* Switchで囲んで、Routeで設定したcomponentが呼ばれるように設定 */} <Switch> {/* exactにしてrootにアクセスされたときはだけ「Top!」と表示されるように設定 */} <Route exact path="/" component={() => <>Top!</>} /> {/* exactではないので、「detail/hoge」でも以下のcomponentは呼ばれる */} <Route path="/detail/" component={() => <>Detail!</>} /> </Switch> </div> </div> </HashRouter> ); } } export default App;ついでに、Sassは簡単に以下を追加しています。
App.scss._RoutingArea { padding: 100px; background-color: #eaeaea; }上記で保存すると、以下のように表示されると思います。
下側にグレー背景で「Top!」と表示されたかと思います。
また、http://localhost:3000/#/
というURLで表示されるようにもなったはずです。ここで、URLを以下に変えてみましょう。
http://localhost:3000/#/detail/
上記画像のとおりになっていれば成功です!
ちゃんと、
/
でアクセスするとTop!
と表示され、/detail/
でアクセスするとDetail!
と表示されるようになりました。
今は簡単なFunctionComponentを差し込んで文字だけ表示させていますが、ここをちゃんとしたcomponentを差し込んであげれば、URLに応じてページごとのcomponentを切り替えるというようなことができるようになります。URLによって表示コンテンツを変える
ルーティングをするとなると、例えば、
/detail/hoge
とdetail/fuga
でアクセスされたときのテンプレートは一緒だけど、中身のコンテンツは変えたい!というようなことがありますよね。ちゃんと、
react-router-dom
はそれができるようになっています。まずは、
/detail/
というURLで待ち受けている設定を、以下の設定に変えます。<Route path="/detail/:id" component={() => <>Detail!</>} />今回は
/detail/
の後ろに:id
というのを追加しました。
こうすることによって、/detail/hoge
というURLでアクセスするとid
という名前でhoge
という文字列を受け取ることができるようになります!便利すぎる。。ただ、現在引き受ける先のcomponentが、ただただ
() => <>Detail!</>
と書かれていて何もできない状態なので、detail用のcomponentを作成していきます。App.tsximport React, { Component, FunctionComponent } from 'react'; import { HashRouter, Switch, Route, match } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; const Detail: FunctionComponent<{ match: match<{ id: string }> }> = ({ match }) => ( <>id: {match.params.id}</> ); class App extends Component { // 省略... }
App
というclassの上に、Detail
というcomponentを作成しました。
TypeScriptで書いているので少し複雑になっていますね。少しずつ説明します。まず、
Detail
はpropsとしてmatch
というものを受け取ります。
このmatch
というものはreact-router-dom
が自動でpropsに流してくれるものらしいです。
ES6等で書いている場合はただ受け取ればよいのですが、TypeScriptの場合はmatch
という受け取り物は何者なのかを知る必要があるので、それを宣言します。
match
が何者かは、react-router-dom
からimportできるため、今回新たにimportし、まずDetail
の型が以下のようになります。const Detail: FunctionComponent<{ match: match }>Reactが用意してくれている
FunctionComponent
という型自体を定義し、さらにジェネリックを使って、受け取るpropsの型を定義しています。
FunctionComponent
は、ちゃんとpropsの型を自由に定義できるようにFunctionComponent<T>
のT
のところに型を入れられるようにしてくれています。
それを活用して、今回はmatch
というkey名で、値の型がmatch
のものをpropsで受け取るよーと宣言しています。(match
がかぶっちゃっていて分かりづらくてすみません。。)ただ、上記だけだとまだエラーを起こしていまいます。
今回、
Route
のpathのところで/Detail/:id
という風に設定したので、id
というkeyで値を取得したいです。
それが入っているのがmatch.params
の中らしい(react-router-dom
のリファレンスにありました)ので、取得の仕方はmatch.params.id
ということになります。
ただ、match.params.id
と書いてしまうと「id
なんてkeyないんですけどー!」というTypeScriptのエラーを起こしてしまいます。
なので「id
というkeyがあって、しかもそのid
の値の方はstring
なんだよ〜」というのを教えてあげなければいけません。
react-dom-router
のmatch
という型は、ちゃんと上記のような怒られ方をするのを予期してくれているので、ジェネリックでparamsの型を渡すことができます。
それをちゃんと実装すると、以下のような型になるわけです。const Detail: FunctionComponent<{ match: match<{ id: string }> }>ちょっと長めの型になっちゃいましたね…笑
長くて嫌だなーという方は、以下のようにinterface
を使ってまとめてあげても良いです。interface DetailMatchParams { id: string; } interface DetailProps { match: match<DetailMatchParams> } const Detail: FunctionComponent<DetailProps>こっちのほうが見通しが良さそうですね。
最後に、作ったDetail
をRouteに登録してあげて、動作確認をしてみましょう。
全体のコードは以下になります。App.tsximport React, { Component, FunctionComponent } from 'react'; import { HashRouter, Switch, Route, match } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; interface DetailMatchParams { id: string; } interface DetailProps { match: match<DetailMatchParams> } const Detail: FunctionComponent<DetailProps> = ({ match }) => ( <>id: {match.params.id}</> ); class App extends Component { render() { return ( <HashRouter> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> <div className="_RoutingArea"> <Switch> <Route exact path="/" component={() => <>Top!</>} /> <Route path="/detail/:id" component={Detail} /> </Switch> </div> </div> </HashRouter> ); } } export default App;そして
http://localhost:3000/detail/hoge
でアクセスした結果が以下です。ページ上に
hoge
が表示されています!
別の文字列でも試してみます。いい感じですね!!!
これで、URLに応じてコンテンツを変えることもできそうです!!めでたし!SPAをdeployする
ついにここまでやってきました!
まず、一旦作ったものが完成したのでコミットしましょう。git add . git commit git push
簡単ですね。
そして、いよいよ、GitHub Pagesに公開するときがやってまいりました!まず、以下のコマンドを叩いてパッケージをインストールします。
npm i -D gh-pages
こちらは、
gh-pages
というブランチを勝手に生成して、勝手にプッシュまでしてくれる優れものです!
前述したとおり、gh-pages
というブランチに特定のファイルをプッシュすれば公開されるので、その作業を完全に自動化できちゃうわけです!gh-pagesというパッケージは、普通に使うのであれば以下のように使えばOKです。
gh-pages -d プッシュするディレクトリ名
ただ、今回はbuildして、そのbuildしたファイルだけを公開したいです。
なので、その一連をnpm run deploy
でできるようにしたいと思います。やることは簡単です。一行追加するだけです。
package.json
を開くと、scripts
という場所があるはずです。()package.json(scriptsのみ抜粋)"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" },ここに対して、以下のように編集を加えます。
package.json(scriptsのみ抜粋)"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "deploy": "npm run build && gh-pages -d build" },
eject
の下にdeploy
というのを加えました。
これで、npm run deploy
を叩けば、必ず最新版がdeployできるようになりました!!通常だと
npm run build
を叩いて、build
フォルダにリリース用のファイルを生成させて、その中身を他の手段でdeployしないといけないのですが、gh-pages
パッケージを使うことで、1回コマンド叩くだけでリリースができます!超便利!!というわけで、以下のURLにアクセスしてみましょう!!
https://ユーザID.github.io/リポジトリ名/
(私の場合は、https://hiraryo0213.github.io/react-app-sandbox/ です。)さぁ、ページは表示されたでしょうか…!!
されていないはずです!!
がーーーん。
chromeのデベロッパーツールとかで見てみると、もろもろファイルが
404
になってしまっています。
これは、読み込むべきJSやCSSのファイルパスがうまく設定できていなくて起きてしまっています。。。それを解消するために、
package.json
を最後にいじる必要があります!
これも一行入れればいいだけです。package.json(一部抜粋){ "name": "react-app-sandbox", "version": "0.1.0", "private": true, "homepage": "https://hiraryo0213.github.io/react-app-sandbox/",場所はどこでもいいのですが、
homepage
というkeyに、自分のページのURLを記載してあげます。
アクセスして真っ白になってしまったページのURLを入れてあげればOKです。上記を対応したら、もう一度
npm run deploy
をしてみましょう!!ちゃんと
http://localhost:3000
で見ていたまんまのものが表示されているはずです!
うれしーーーーー!言うことなしですね!!
さいごに
という感じで、TypeScriptとSassを使った環境で作ったReact Appを、静的なページを公開できるGitHub Pagesに無事公開するところまでできました!
これで好きなページを作ることができるようになりました!
あとは、おしゃなデザイン作ってSassできれいにして、redux-sagaとか使って外部APIからデータ取ってきて、Reduxとかでデータの管理をちゃんとして、、、とやっていけば、結構やりたいことはできるのではないかと思います!!
道のりは長い。。。笑
ただここまでできるなんて、ほんといい時代になりましたね!今回試しに作ったもののリポジトリはこちらですので、参考までにご覧になってください〜
https://github.com/hiraryo0213/react-app-sandboxまた、なにか不明点、疑問点、指摘点あれば、コメント欄によろしくお願いいたします〜
- 投稿日:2019-02-18T07:43:14+09:00
create-react-appとSassでサラッと作ったSPAをgh-pagesにスルッとデプロイすっぞ!
SPAを手軽にリリースしたい!
昨今、ReactとかでサラッとSPA作ってる人多いじゃないですか!?
フロントエンドエンジニアでガンガンJS書いてて〜〜とかじゃない人でも日本語のドキュメントを少し漁ればなんとか作れちゃう時代になりつつありますよね。
だから私もReactで作ったものをリリースしたい!世に広めたい!そんなことを思うわけです。マークアップエンジニアに立ち塞がる「サーバサイド」の壁
デザインやマークアップをやっている人がFE領域に入り込もうとすると、どうしてもサーバのことがわかりません。
create-react-app
で確かにReactを書く環境は作れる。
ローカルサーバも立ち上がって表示確認はできる。
データをセットすればDOMを勝手にレンダリングしてくれる感動を得られる!
react-router-dom
を使ってルーティングまでも自分で実装できるようになってしまう!
テンションは上がる一方です!!でも、、、
これをどう世の中にリリースしたらいいか何もわからない!!!
詰みまくりですね。
救世主「GitHub Pages」
そんな私みたいなエンジニアに朗報です。
GitHubにサイトのリポジトリを作成し
gh-pages
というブランチを作ってそこにプッシュすると、なんとそれだけで静的なサイトが公開されてしまいます!詳しくは以下を参考にしてみてください。
https://www.tam-tam.co.jp/tipsnote/html_css/post11245.htmlまた、
create-react-app
で作り出せるものは、結局index.html
に<script>
でjsを読み込んだだけのものなので、静的なページというわけです。なので、ちゃんとルーティングを実装したとしても、GitHub Pageならサーバも借りずに自分のSPAをリリースすることができるわけです!!
(さすがにサーバサイドでのroutingだったり、DBを使うものは難しいです><)というわけで、実際に作ってみましょう!
作る上で使うもの
以下のものを使って作っていきます。
- GitHub
- node(v8.15.0
を使っています)
- npm(6.8.0
を使っています)
- create-react-app
- TypeScript
- Sass
- react-router-dom
- gh-pages私はTypeScriptとSassに慣れ親しんでいるので、これらが使える環境を用意したいと思います!笑
リポジトリ作成と環境構築
まず、GitHubの
GitHub Pages
を使うからにはここにリポジトリがないと始まらないので、会員登録とリポジトリ作成を行ってください。
GitHubはこちら。
会員登録をしたら、Repositories横の「New」というボタンから作成してください。
私は適当にreact-app-sandbox
というリポジトリ名にしました。そうしたら、
https://github.com/ユーザ名/react-app-sandbox.git
というのが出てくるのでそれをコピーします。
(リポジトリ名は、私が勝手につけた名前にしています。みなさんが別の名前をつけていればその名前になります。)次に、ターミナル等でローカルの好きなフォルダまで移動します。
私は~/Documents/git
に移動しています。(私はMacユーザです。)そこで以下のコマンドを叩きます。
git clone https://github.com/ユーザ名/リポジトリ名.gitGitHubのユーザ名とパスワードを聞かれた場合はそれを打ち込んでenterをターンッ!ください。
そうすると今いるフォルダの中にリポジトリ名のフォルダが出来上がります。
そのフォルダ内では、GitHubのリモートリポジトリにpushしたりpullしたりできるようになっています。ただ、まだそのフォルダには移動せずに、次に以下のコマンドを叩きます。
npx create-react-app react-app-sandbox --typescript
npx
を使って、一時的にcreate-react-app
をインストールして実行し、リポジトリ名と同じ名前でReact appを作成します。しかもTypeScript版で。
npx
を使えば、常に使うことのないcreate-react-app
を無駄にインストールしなくて良いので大変便利です。
(もしTypeScirptを使いたくない場合は、--typescript
をつけないで実行してください。)作り出されたリポジトリ名のフォルダに移動して、確認のために以下のコマンドを叩いてみましょう。
cd react-app-sandbox npm start
そうすると自動でブラウザが立ち上がり、
http://localhost:3000
にアクセスされて以下のページが出ればひとまず完成です!
Reactのロゴが回っていますね。Sassを書けるようにする
まだまだ私はSassから抜け出せないので、Sassでかけるように設定します。
src
フォルダの中は以下のようになっています。src ├── App.css ├── App.test.tsx ├── App.tsx ├── index.css ├── index.tsx ├── logo.svg ├── react-app-env.d.ts └── serviceWorker.tsとりあえずSassにしたいので、
index.css
とApp.css
の拡張子を無邪気にscss
に変えてみちゃいましょう!^^src ├── App.scss ├── App.test.tsx ├── App.tsx ├── index.scss ├── index.tsx ├── logo.svg ├── react-app-env.d.ts └── serviceWorker.tsこのように変えられたら、一度ターミナル上で
control + c
を押してnpm start
のプロセスを終了し、もう一度npm start
を実行してみましょう。npm start Failed to compile. ./src/App.tsx Module not found: Can't resolve './App.css' in '/Users/senshu/Documents/git/react-app-sandbox/src'エラーです^^世の中そううまくはいきません^^
App.tsx
はApp.css
をimportするように書かれているので、このファイル内の拡張子を変えてあげないといけません。
index.tsx
の中身も同様なので、ついでにindex.css
をindex.scss
に変えましょう。そしてもう一度
control + c
を押してからnpm start
を実行すると、、、なぜかうまくいく!!!世の中ーーーー!!!
これ
node-sass
を入れていないのでうまくいかないと思ったのですが、なぜかうまくいっちゃいました。。。
package-lock.json
にもnode-sass
は入っていないので、なんでなんだろうーって思っていまして、誰か詳しい方いたら教えてください。。とりあえずうまくいくので次にいきましょう!w
Routerを入れる
今回のテーマは「SPA」なので、ブラウザ側でルーティングできるようにしたいです。
URLを直で叩かれたら、そのURLのページがちゃんと表示されるようにしたいですよね。ただ、今回注意したいのは、サーバ側でルーティングさせられないという点です。
サーバ側のルーティングはGitHub側でコントロールされてしまっているので、そのURLに相当するファイルを用意しないと404
のページになってしまいます。
URLに相当するページを作るのももちろん良いですが、それはもうSPAではなくなってしまいます…笑
なので、今回はハッシュによるルーティングを実現させようと思います。
なのでURLがhttps://~~~~/#/hoge
という形になりますが、そこはご容赦ください><
(こうすればハッシュじゃなくてもできるよというやり方があれば、ぜひ教えてください。)ルーティングを実現させるために、プロジェクトルート(リポジトリ名のフォルダ)で以下のコマンドを叩きます。
npm i -S react-route-dom @types/react-router-dom
@types
はTypeScriptで書いているので入れています。ここから、Reactをちょこちょこいじっていきます。
以下のサイトを参考に作っていきますので、ぜひみなさんも参考にしてみてください。
https://reacttraining.com/react-router/web/ここで、もし
npm start
を終了している場合は、これから開発していくのでnpm start
を叩いた状態にしておいてください。あちこち行くと分かりづらいので、基本的に
App.tsx
をいじっていきます。App.tsximport React, { Component } from 'react'; import logo from './logo.svg'; import './App.scss'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } } export default App;上記は、
App.css
をApp.scss
に変えたこと以外は何も変更していないソースコードです。
これを以下のように変えます。App.tsximport React, { Component } from 'react'; // react-router-domから必要なものを追加 import { HashRouter, Switch, Route } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; class App extends Component { render() { return ( // 全体をHashRouterでWrap <HashRouter> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> {/* ルーティングがわかりやすいようにdivを追加 */} <div className="_RoutingArea"> {/* Switchで囲んで、Routeで設定したcomponentが呼ばれるように設定 */} <Switch> {/* exactにしてrootにアクセスされたときはだけ「Top!」と表示されるように設定 */} <Route exact path="/" component={() => <>Top!</>} /> {/* exactではないので、「detail/hoge」でも以下のcomponentは呼ばれる */} <Route path="/detail/" component={() => <>Detail!</>} /> </Switch> </div> </div> </HashRouter> ); } } export default App;ついでに、Sassは簡単に以下を追加しています。
App.scss._RoutingArea { padding: 100px; background-color: #eaeaea; }上記で保存すると、以下のように表示されると思います。
下側にグレー背景で「Top!」と表示されたかと思います。
また、http://localhost:3000/#/
というURLで表示されるようにもなったはずです。ここで、URLを以下に変えてみましょう。
http://localhost:3000/#/detail/
上記画像のとおりになっていれば成功です!
ちゃんと、
/
でアクセスするとTop!
と表示され、/detail/
でアクセスするとDetail!
と表示されるようになりました。
今は簡単なFunctionComponentを差し込んで文字だけ表示させていますが、ここをちゃんとしたcomponentを差し込んであげれば、URLに応じてページごとのcomponentを切り替えるというようなことができるようになります。URLによって表示コンテンツを変える
ルーティングをするとなると、例えば、
/detail/hoge
とdetail/fuga
でアクセスされたときのテンプレートは一緒だけど、中身のコンテンツは変えたい!というようなことがありますよね。ちゃんと、
react-router-dom
はそれができるようになっています。まずは、
/detail/
というURLで待ち受けている設定を、以下の設定に変えます。<Route path="/detail/:id" component={() => <>Detail!</>} />今回は
/detail/
の後ろに:id
というのを追加しました。
こうすることによって、/detail/hoge
というURLでアクセスするとid
という名前でhoge
という文字列を受け取ることができるようになります!便利すぎる。。ただ、現在引き受ける先のcomponentが、ただただ
() => <>Detail!</>
と書かれていて何もできない状態なので、detail用のcomponentを作成していきます。App.tsximport React, { Component, FunctionComponent } from 'react'; import { HashRouter, Switch, Route, match } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; const Detail: FunctionComponent<{ match: match<{ id: string }> }> = ({ match }) => ( <>id: {match.params.id}</> ); class App extends Component { // 省略... }
App
というclassの上に、Detail
というcomponentを作成しました。
TypeScriptで書いているので少し複雑になっていますね。少しずつ説明します。まず、
Detail
はpropsとしてmatch
というものを受け取ります。
このmatch
というものはreact-router-dom
が自動でpropsに流してくれるものらしいです。
ES6等で書いている場合はただ受け取ればよいのですが、TypeScriptの場合はmatch
という受け取り物は何者なのかを知る必要があるので、それを宣言します。
match
が何者かは、react-router-dom
からimportできるため、今回新たにimportし、まずDetail
の型が以下のようになります。const Detail: FunctionComponent<{ match: match }>Reactが用意してくれている
FunctionComponent
という型自体を定義し、さらにジェネリックを使って、受け取るpropsの型を定義しています。
FunctionComponent
は、ちゃんとpropsの型を自由に定義できるようにFunctionComponent<T>
のT
のところに型を入れられるようにしてくれています。
それを活用して、今回はmatch
というkey名で、値の型がmatch
のものをpropsで受け取るよーと宣言しています。(match
がかぶっちゃっていて分かりづらくてすみません。。)ただ、上記だけだとまだエラーを起こしていまいます。
今回、
Route
のpathのところで/Detail/:id
という風に設定したので、id
というkeyで値を取得したいです。
それが入っているのがmatch.params
の中らしい(react-router-dom
のリファレンスにありました)ので、取得の仕方はmatch.params.id
ということになります。
ただ、match.params.id
と書いてしまうと「id
なんてkeyないんですけどー!」というTypeScriptのエラーを起こしてしまいます。
なので「id
というkeyがあって、しかもそのid
の値の方はstring
なんだよ〜」というのを教えてあげなければいけません。
react-dom-router
のmatch
という型は、ちゃんと上記のような怒られ方をするのを予期してくれているので、ジェネリックでparamsの型を渡すことができます。
それをちゃんと実装すると、以下のような型になるわけです。const Detail: FunctionComponent<{ match: match<{ id: string }> }>ちょっと長めの型になっちゃいましたね…笑
長くて嫌だなーという方は、以下のようにinterface
を使ってまとめてあげても良いです。interface DetailMatchParams { id: string; } interface DetailProps { match: match<DetailMatchParams> } const Detail: FunctionComponent<DetailProps>こっちのほうが見通しが良さそうですね。
最後に、作ったDetail
をRouteに登録してあげて、動作確認をしてみましょう。
全体のコードは以下になります。App.tsximport React, { Component, FunctionComponent } from 'react'; import { HashRouter, Switch, Route, match } from 'react-router-dom'; import logo from './logo.svg'; import './App.scss'; interface DetailMatchParams { id: string; } interface DetailProps { match: match<DetailMatchParams> } const Detail: FunctionComponent<DetailProps> = ({ match }) => ( <>id: {match.params.id}</> ); class App extends Component { render() { return ( <HashRouter> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> <div className="_RoutingArea"> <Switch> <Route exact path="/" component={() => <>Top!</>} /> <Route path="/detail/:id" component={Detail} /> </Switch> </div> </div> </HashRouter> ); } } export default App;そして
http://localhost:3000/detail/hoge
でアクセスした結果が以下です。ページ上に
hoge
が表示されています!
別の文字列でも試してみます。いい感じですね!!!
これで、URLに応じてコンテンツを変えることもできそうです!!めでたし!SPAをdeployする
ついにここまでやってきました!
まず、一旦作ったものが完成したのでコミットしましょう。git add . git commit git push
簡単ですね。
そして、いよいよ、GitHub Pagesに公開するときがやってまいりました!まず、以下のコマンドを叩いてパッケージをインストールします。
npm i -D gh-pages
こちらは、
gh-pages
というブランチを勝手に生成して、勝手にプッシュまでしてくれる優れものです!
前述したとおり、gh-pages
というブランチに特定のファイルをプッシュすれば公開されるので、その作業を完全に自動化できちゃうわけです!gh-pagesというパッケージは、普通に使うのであれば以下のように使えばOKです。
gh-pages -d プッシュするディレクトリ名
ただ、今回はbuildして、そのbuildしたファイルだけを公開したいです。
なので、その一連をnpm run deploy
でできるようにしたいと思います。やることは簡単です。一行追加するだけです。
package.json
を開くと、scripts
という場所があるはずです。()package.json(scriptsのみ抜粋)"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" },ここに対して、以下のように編集を加えます。
package.json(scriptsのみ抜粋)"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "deploy": "npm run build && gh-pages -d build" },
eject
の下にdeploy
というのを加えました。
これで、npm run deploy
を叩けば、必ず最新版がdeployできるようになりました!!通常だと
npm run build
を叩いて、build
フォルダにリリース用のファイルを生成させて、その中身を他の手段でdeployしないといけないのですが、gh-pages
パッケージを使うことで、1回コマンド叩くだけでリリースができます!超便利!!というわけで、以下のURLにアクセスしてみましょう!!
https://ユーザID.github.io/リポジトリ名/
(私の場合は、https://hiraryo0213.github.io/react-app-sandbox/ です。)さぁ、ページは表示されたでしょうか…!!
されていないはずです!!
がーーーん。
chromeのデベロッパーツールとかで見てみると、もろもろファイルが
404
になってしまっています。
これは、読み込むべきJSやCSSのファイルパスがうまく設定できていなくて起きてしまっています。。。それを解消するために、
package.json
を最後にいじる必要があります!
これも一行入れればいいだけです。package.json(一部抜粋){ "name": "react-app-sandbox", "version": "0.1.0", "private": true, "homepage": "https://hiraryo0213.github.io/react-app-sandbox/",場所はどこでもいいのですが、
homepage
というkeyに、自分のページのURLを記載してあげます。
アクセスして真っ白になってしまったページのURLを入れてあげればOKです。上記を対応したら、もう一度
npm run deploy
をしてみましょう!!ちゃんと
http://localhost:3000
で見ていたまんまのものが表示されているはずです!
うれしーーーーー!言うことなしですね!!
さいごに
という感じで、TypeScriptとSassを使った環境で作ったReact Appを、静的なページを公開できるGitHub Pagesに無事公開するところまでできました!
これで好きなページを作ることができるようになりました!
あとは、おしゃなデザイン作ってSassできれいにして、redux-sagaとか使って外部APIからデータ取ってきて、Reduxとかでデータの管理をちゃんとして、、、とやっていけば、結構やりたいことはできるのではないかと思います!!
道のりは長い。。。笑
ただここまでできるなんて、ほんといい時代になりましたね!今回試しに作ったもののリポジトリはこちらですので、参考までにご覧になってください〜
https://github.com/hiraryo0213/react-app-sandboxまた、なにか不明点、疑問点、指摘点あれば、コメント欄によろしくお願いいたします〜
- 投稿日:2019-02-18T05:19:36+09:00
Reactの静的サイトを自動デプロイ
はじめに
今回はCI/CDの手法を学ぶために、静的サイトの自動ビルドと自動デプロイをCircleCIとAWSを用いて構築したので、ぜひ参考にしてみてください。
参考資料
https://circleci.com/docs/2.0/ecs-ecr/#section=deploymen
https://qiita.com/Sekky0905/items/7f9aa94261e17e4fd040
https://circleci.com/docs/2.0/hello-world/
https://devblog.thebase.in/entry/2018/10/31/110000概要
前提条件
前提条件としてAWSのアカウントを保持していること、GitHubの基本が分かっていること、CircleCIのアカウントがGitHubのアカウントに紐づけられていることを前提に記事を書きます。
静的サイトのホスト
今回は静的サイトをAWSのS3にホストする形で勧めますので、S3にバケットを作って、静的サイトのホストを完了させた状態にしてください。
一応自分が書いた記事のリンクを貼っておきますので、分からなければ確認してみてください。
AWS S3で静的サイトを構築CircleCIの簡単な使い方
CircleCIに詳しい人や、CircleCIのHello Worldドキュメントを読んだことがあり、CircleCIの簡単な使い方について理解している人は飛ばしてください。また公式ドキュメントの方が自分の記事より遥かに詳しく正確に書いてますので、自分のは参考程度に見てください。
CircleCIは内部にdockerを持っていてgithubのリポジトリのルートに.circleci/config.ymlに設定を書くことで、githubにpushしたのをトリガーにしてCircleCI内でdockerイメージのビルドと各種コマンドが実行されるものになります。
まずはgithubのリポジトリを作成してもらって、リポジトリのルートに.circleci/config.ymlを作成してください。
$ mkdir .circleci $ ls -a . .. .circleci .git $ vim .circleci/config.ymlしたら以下のコードを入力してください。
.circleci/config.ymlversion: 2 jobs: build: docker: - image: circleci/node:4.8.2 steps: - checkout - run: echo "hello world"コードを書き込んだらgit pushでpushしてください。このときリポジトリにconfig.ymlが無いとCicleCIでトリガが掛けられません。
コードの説明を簡単にすると、docker imageをCircleCIで用意されていてダウンロードの時間を少なくできるNodejsのイメージを選択し、build後にechoコマンドでhello worldを出力するものです。
次にリポジトリにトリガーをかけるためにCircleCIのアカウントページから
を選択後、特に何もいじらず
成功すると、以下のような画面になりecho "hello world"が実行され、hello worldが出力されているのが確認できます。
以上が簡単なCircleCIの説明になります。
Reactアプリのビルド
CircleCIを用いて、Reactで作成したアプリをビルドします。Reactについての詳しい説明は公式チュートリアルなどをご覧ください。
Reactチュートリアル: Intro To React【日本語翻訳】
まずReactのテンプレートを作ります。nodeをインストールしてもらってnpxを使える状態で以下のコマンドを入力してください。
npx create-react-app test mv test/* ./ rm -r test/ npm run build npm start上記のコマンドで、reactアプリのテンプレートを作成し、localhost:3000でサービスが立ち上がるのを確認できるはずです。
(reactのテンプレートフォルダから中身を取り出している理由は、CircleCIでのビルド時にpackage.jsonの有るディレクトリにcheckoutしているのに、package.jsonが見つからないエラーが出たので、中身を取り出しています。原因が分からないので、もし分かる人は教えてください)
Reactアプリがたち上がったのを確認したら、以下のコードを.circleci/config.ymlに書いてください。
.circleci/config.ymlversion: 2 jobs: build: docker: - image: circleci/node:11.8.0 steps: - checkout - run: npm run buildリポジトリをpushして、CircleCIの画面のBuild画面から、成功すれば緑色になっているのが確認できます。(大量に赤くなっているのは、working_directoryとcheckout絡みで検証して挫折した結果です。)
成功すれば、build画面の詳細からnpm run buildが成功したことが確認できます。AWS S3に自動デプロイ
AWS S3にデプロイするために、同じようにCircleCIでpythonイメージから、更にawscliというコマンドラインからawsのリソースにアクセスできるソフトをダウンロードしてBuildした結果をデプロイします。
まず以下のコードに.circleci/config.ymlを書き直しでください。(バケット名は各自で作成したバケット名を入力してください。)
.circleci/config.ymlversion: 2 jobs: build: docker: - image: circleci/node:11.8.0 steps: - checkout - run: pwd - run: npm run build deploy: docker: - image: circleci/python:2.7-jessie steps: - run: name: Install awscli command: sudo pip install awscli - checkout - run: name: Deploy to S3 command: aws s3 sync build/ s3://バケット名 workflows: version: 2 build-deploy: jobs: - build - deploy: requires: - build filters: branches: only: master重要な点はworkflowsという所で、ここでReactのアプリをBuildした後にS3にデプロイするように実行の順番を制御しています。
実際にAWSにデプロイしているのは以下のコマンドになります。
aws s3 sync build/ s3://バケット名またこのままでは、S3への権限が無いのでCircleCIに権限を付与しなければアップロードできません。そのためにIAMロールユーザーを作成してください。
実行権限はS3へのFullAccessとかで良いと思います。
詳しいことは以下の記事とか読んでください。IAMロールの公式ドキュメント
S3のアクセスコントロールまとめIAMロールユーザーが作成できたら、CircleCIのアカウントページから、
- 左のBuildsボタンをクリック
PERMISSIONSのAWS Permissionsをクリック
先程作ったIAMロールユーザーのAccess Key IDとSeacret Access Keyを入力
IAMロールユーザーの登録がすんだら、pushして結果を確認してみてください。
- CircleCIのWorkflowsを開く
- 詳細を見る
- buildとdeployが成功していることを確認!
S3の静的ホスト先のURLを開いてデプロイ出来ているかの確認
成功!
まとめ
筆者はあまり、CI/CDについて詳しくは無いのでセキュリティなどや、もっと良いやり方が有るのかもしれませんが、取り敢えずgithubにpushするだけで自動ビルド&自動デプロイが出来たので知識の整理のために書きました。
間違っている点や、もっとこうするべきだという点が有りましたらコメント下さい!