- 投稿日:2019-02-03T22:21:35+09:00
Gatsby.jsで静的なWebサイトをサクッと作る(CSS・SCSS篇)
Gatsby.jsで静的なWebサイトをサクッと作る(インストールからHTML編集篇)の続きです。
今回は、CSS(SCSS)でスタイルをあてるまでを解説します。
SCSSを使えるようにする
そのままでは、SCSSファイルが使えないので、プラグインをインストールします。
$ npm install --save node-sass gatsby-plugin-sass上記コマンドを実行し、完了したら
gatsby-config.jsファイルを作成し、SCSSが使えるように以下の記述をします。gatsby-config.jsmodule.exports = { plugins: [`gatsby-plugin-sass`] }これでSCSSが使えるようになります。
グローバルなスタイルを設定
まずは、リセットCSS等の全ページに適用されるスタイルを設定してみましょう。
src/styles/global.scssを作成します。global.scsshtml { color: red; }続いて、
gatsby-browser.jsを作成しsrc/styles/gloabl.cssをimportします。gatsby-browser.jsimport "./src/styles/global.scss"これで、文字が赤くなっているはずです。
$ npm run developでローカルサーバーを起動し、確認してみましょう。スコープされたスタイルを設定する
Gatsby.js(React)では、
CSS Modulesを使用し、CSS(SCSS)をスコープさせることができます。現時点ではindex.jsしか存在しないので無意味ですが、コンポーネントが増えてくると、そのコンポーネントでのみ有効なスタイルをあてることができるので、すごく便利です。では、
src/pages/index.jsでのみ有効なsrc/pages/index.module.scssを作成しましょう。index.module.scss.txt { color: green; }続いて、
src/pages/index.jsにsrc/pages/index.module.scssをimportします。index.jsimport React from "react" import Styles from "./index.module.scss" export default () => ( <div> <p className={Styles.txt}>Hello</p> <p>world!</p> </div> )
index.module.scssに書かれた.txtを、index.jsではStylesというオブジェクトとして扱っていています。.txtはStylesのプロパティになっているので、className属性で{Styles.txt}とすると、.txtで設定したCSSプロパティが反映されます。属性名が
classではなくclassNameになっているのは、classがJavaScriptの予約語で使用できないためです。また、{}で囲うことによりJavaScripを実行できます。ブラウザを確認すると、
Helloのみ緑色になっているはずです。また、ChromeのDevToolsでElementを見てみると、class属性部分がユニークな文字列になっています。今回はここまで!次回も、乞うご期待!
- 投稿日:2019-02-03T20:59:52+09:00
ReactにFirebaseを使ったログイン機能を実装する
この記事の内容を一言で
自作の家計簿アプリにFirebaseを用いてログイン機能を作成する
問題
家計簿アプリにログイン機能を実装して、ユーザー別に各項目を保存できるようにしたい
解決策
Firebaseを使用する
解決法
Firebaseについて
FirebaseはGoogleが提供しているサービスの一つ。
Baas(Backend as a service)/mBaas(mobaile Backend as a service)を提供している。
Firebaseを利用することで、フロントエンドの開発者でも簡単にデータベースが必要な作業をシステムに実装することができるように。
データベースに関する知識も必要だけど、今求めていることはそこではないため、今回はFirebaseを用いて問題を解決する。Firebaseについて: Firebaseの始め方
Baasについて: BaaSからSaaSまで!いま一度、クラウドサービスの違いを整理しようFirebaseのドキュメント
Firebase を JavaScript プロジェクトに追加する
Firebase AuthenticationFirebaseの導入
まずはPCにFirebaseを導入する。
install.sh$ npm install firebaseメモ
npm.shを実行した際、以下のエラーが発生した。
$ npm audit fixを使うことで治せるとあるが、詳細を確認するため$ npm auditを打つ。
セキュリティの問題らしい。
今のバージョンだとセキュリティ上良くないんで、更新してほしいと。
More infoにあるhttps://www.npmjs.com/advisories/725 をみると、バージョンを更新することで解決できると書いてあるので、更新する。
実行したのは以下のコマンド。yarn.sh$ yarn add react-scripts@2.1.3このあとに、yarn版のFirebaseをインストールするコマンドを実行したら無事解決。
yarn_install.sh$ yarn add firebaseログイン機能の実装
次に、ログイン機能を実装していく。
Firebaseにログインして、アプリの名前などを決める。
それが終わったら、この画面からコードをコピーする。
</>のボタンを押すとコードが表示される。
コードをコピーしたら、firebase用のディレクトリを作成し、コピーしたコードをペーストするファイルを作成する。
copy.sh$ mkdir firebase $ vim firebase.jsfirebase.jsimport firebase from 'firebase/app' import 'firebase/auth' const config = { apiKey: "***", authDomain: "***", databaseURL: "***", projectId: "***", storageBucket: "***", messagingSenderId: "***" } // "***"の部分にコピーしたコードの内容をペーストする。 firebase.initializeApp(config); export default firebase続いて、ログイン、ログアウトのボタンを作成していく。
Auth.sh$ mkdir Auth $ vim Auth.jsAuth.jsimport React, { Component } from 'react' import firebase from './firebase' class Auth extends Component { state = { user: null } componentDidMount() { firebase.auth().onAuthStateChanged(user => { this.setState({ user }) }) } //componentDidMountはrenderが実行された後に行われる。データの受け渡しが可能な状態になったら下記のコードが実行されていく。 //onAuthstateChangeでuserにログインしたユーザーの情報を与える login() { const provider = new firebase.auth.GoogleAuthProvider() firebase.auth().signInWithRedirect(provider) } //signInWithRedirectでGoogleのログインページに接続して、Google プロバイダ オブジェクトのインスタンスを作成する。 logout() { firebase.auth().signOut() } render() { return ( <div className="Auth"> {this.state.user ? ( <h1 className="UserName">{this.state.user && this.state.user.displayName}の家計簿</h1> // displayNameでログインした人のGoogleアカウントに登録されている名前を表示する ) : ( <h1 className="Name">あなたの家計簿</h1> //ログインしていない人用の表示 )} {this.state.user ? ( <button onClick={this.logout}>Google Logout</button> //ユーザーがログインしている時はlogoutボタンを表示する ) : ( <button onClick={this.login}>Google Login</button> //ユーザーがいない時はloginボタンを表示する )} </div> ) } } export default Authこちらのコードのほとんどは10分でできるReact+FirebaseのGoogleAuthから。
自分なりに解釈したものをコメントアウトに記述。あとは、
Auth.jsで作成したコンポーネントをapp.jsに設置し、各自の環境でサーバーを起動して、Firebaseが導入、実行されているかを確認する。
componentDidMountについて: 5分で理解する React.js
GoogleAuthProviderについて: Google ログインと JavaScript を使用して認証する
displayNameについて: Firebase でユーザーを管理するコードを隠す
firebase.jsのコードをGithubなどのインターネットに挙げるときはAPIキーを隠す。
その方法は、package.jsonといった、プロジェクトのroot階層に.envファイルを作成することでできる。
また、create-react-appを使っている場合、.envに書くのはREACT_APP〜である必要があった。
今回はapiKeyとmessagingSenderIdを隠してみる。REACT_APP_FIREBASE_APIKEY="***" REACT_APP_FIREBASE_MESSAGING_SENDER_ID="***"firebase.jsimport firebase from 'firebase/app' import 'firebase/auth' const config = { apiKey: process.env.FIREBASE_APIKEY, authDomain: "***", databaseURL: "***", projectId: "***", storageBucket: "***", messagingSenderId: FIREBASE_MESSAGING_SENDER_ID } firebase.initializeApp(config); export default firebaseという風に。
エラーが発生しても、yarn startをすることで認識される場合があるため、確認する。
.envの読み込みについて: create-react-appのプロジェクトで環境変数を.envから読み込めないときに確認することまとめ
データベースの知識を用いずにユーザーのログイン機能を実装できるのはメチャクチャ便利。
他にも色々機能あるみたいなので、触りながら理解を進めていきたい。
もし、Auth.jsの解釈で間違っているところがあれば、指摘をお願いします。Github
Issue: Login function
PullRequest: feat: Create Login Function参考資料
全般: 10分でできるReact+FirebaseのGoogleAuth
Firebaseについて: Firebaseの始め方
Baasについて: BaaSからSaaSまで!いま一度、クラウドサービスの違いを整理しよう
componentDidMountについて: 5分で理解する React.js
GoogleAuthProviderについて: Google ログインと JavaScript を使用して認証する
displayNameについて: Firebase でユーザーを管理する
.envの読み込みについて: create-react-appのプロジェクトで環境変数を.envから読み込めないときに確認すること
- 投稿日:2019-02-03T14:57:49+09:00
Reactアプリでもwebpack-bundle-analyzerでバンドルサイズを可視化する
概要
Reactでいろいろパッケージ入れていくと、いつの間にかバンドルサイズが肥大化してしまいます。
バンドルサイズ削減のため、どのパッケージがどの程度占めているのか可視化したいところです。これまでCreateReactAppで作成した場合、そのままではWebPackの設定にアクセスできないため、イジェクトする必要がありました。
(イジェクトすると設定ファイルが外に出てくるので自由にできる反面、そこからのメンテは自分たちで責任を持つというトレードオフだったはず)
現在ではイジェクトなしで可視化できるようになったので、それの紹介になります。読者対象
- Reactアプリでバンドルサイズをお手軽に可視化したい方。
- キャッシュが効かない時にアプリの初期表示が遅くて困っている方。
いきなり結論
CreateReactAppのドキュメントに関するプルリクの最初の投稿が全てです(;^_^A
(CreateReactAppのドキュメントがあるのを初めて知ったorz)
https://github.com/facebook/create-react-app/pull/6127
CreateReactAppでサポートしたから、あとはパッケージ入れてちょっとスクリプト実行してねという話です。
執筆時点ではまだドキュメントが更新されていませんが、CreateReactApp自体にはV2時点で入ったのかな?方法だけ知りたかった方はここまででOKです。
あとはこれに到達したまでの経緯の話になります。Road to 結論
Reactアプリでwebpack-bundle-analyzerを利用できないかググる
webpack-bundle-analyzerの記事を見かけて、ちょっと入れたいなという欲求がわく。
だけど、CreateReactAppがサポートしていないときついんじゃないかという不安も・・・
ぐぐってみると、ちょっとトリッキーですが単体プログラムとして実行している方のIssuesを見つける。
https://github.com/facebook/create-react-app/issues/3518
後のコメントを見ずにやってみてたんですが、最新ではCreateReactAppのWebPackの設定ファイルの構造が変わって?動かない(;^_^A
ようやく後尾のコメントのリンクを見て、CreateReactAppがサポートしていることを理解キタ――(゚∀゚)――!!ドキュメント草案の案内通りにやってみる(設定編)
いずれ公式のここに載るはずです。
https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
だけど執筆時点ではまだ載ってないので、プルリクの草案をベースに実施します。まずは本体のwebpack-bundle-analyzerをインストールします。
$npm install --save-dev webpack-bundle-analyzer次に解析を実行するためのコマンドをpackage.jsonに追加します。
scriptsにstart,build,test,eject等あると思いますので、それと同列に追加です。package.json"analyze": "webpack-bundle-analyzer build/bundle-stats.json",これで事前準備は完了になります。簡単ですね。
ドキュメント草案の案内通りにやってみる(実行編)
あとはコマンドを実行して可視化してしまいましょう。
素人には途中のハイフンの意味が分からないですが、考えずに打ち込む(;^_^A
$npm run build -- --stats実行が完了すると、buildフォルダにbundle-stats.jsonというファイルができています。
これが可視化のもとになるんでしょう。$npm run analyze実行するとブラウザが立ち上がって、パッケージサイズの割合が一目瞭然になります。
画像は私の小さなプロジェクトの結果ですが、導入パッケージが少ないのでGzippedでも約100KBのfirebase-firestore.jsのサイズが目につきます(;^_^A
プロジェクトが大きいといろんなパッケージが入り乱れていると思いますので、役割のわりに大きい場合は代替パッケージ等の検討になるでしょうか?
利用頻度が少なければコード分割して、利用時のみ動的ロードに変更するとかも。まとめ
CreateReactAppを使ったReactアプリでも、今はwebpack-bundle-analyzerがすんなり使えちゃうという話でした。
公式ドキュメントが更新され、しばらく時間がたてばこの記事の役割は終えますが、それまでにどなたかの役に立てれば幸いです。
実行方法等が変更になる可能性がありますので、公式ドキュメントを最初に確認してください。
- 投稿日:2019-02-03T11:05:20+09:00
ReactとAudioの付き合い方
![]()
このプレイヤーの実装方法がお題です。
はじめに
![]()
この記事は、React コンポーネントで内部状態をもつ要素を扱うための How を書いたものです。
今回のネタでいうと、内部状態をもつ要素とは、audio 要素のことです。
ここでいう内部状態をもつ要素はどんなものかというと、
const audio = new Audio("sample.mp3") // コンストラクタでaudio要素を生成 audio.play() // 再生 audio.currentTime = 50 // 経過時間を50秒にする的なやつです。
内部状態を持っている要素は React の setState で更新する対象にできないし、しないべきです。
(↑ このテーマは 大きくて書ききれないので割愛します)そうなると出てくる問題として、audio 要素の状態を直接更新しても、React コンポーネントは再描画されない んですよね。
はい。今回の問題は上記の通りで、 目指すのは、
audio 要素の状態を更新すると React コンポーネントが再描画される です。
そのために今回、render prop という React コンポーネントの実装パターンを使います。
render prop について
![]()
render prop について今回は詳しく説明しませんが、以前書いた記事で使用例などを交えて説明しています。
簡単に説明すると 「コンポーネントの render メソッドを外部から定義するためのテクニック」 です。
本テーマ: 内部状態をもつ要素を React で扱うための How
![]()
まず render prop パターンを使って何がしたいかというと、audio 要素(内部状態をもつ厄介なやつ)の状態管理と画面描画の責務を分離したいんです。
audio 要素の状態を知ってるヤツには見た目の知識を与えず、その逆もまた然りとする感じです。なので、実装としては、大きく二つのコンポーネントを用意します。
AudioProvider: audio の内部状態を知ってるし、変更できるけど、audio 要素が画面でどう使われるかは知らないコンポーネント
AudioPlayer: Provider から audio の情報(経過時間とか)を受け取って、画面を表示するコンポーネント
AudioPlayer (audio 情報を受け取って見た目を作るコンポーネント)
まず、audio 要素の 情報を受け取って画面表示する側の実装をお見せします
今回は冒頭の GIF でお見せした通り、再生・停止と 30 秒ジャンプができるだけのシンプルなプレイヤーを作りました。
AudioPlayer.jsxconst AudioPlayer = () => ( <AudioProvider url="path/to/audioUrl" render={({ currentTime, paused, play, pause, jump }) => ( <div> <p>currenttime: {currentTime}</p> <button onClick={paused ? play : pause}> {paused ? "Play" : "Pause"} </button> <button onClick={() => jump(30)}>30sec ▶︎</button> </div> )} /> );
render={({ currentTime, paused, play, pause, jump }) => ...の部分ですが、render という名前の props に
引数を受け取ってコンポーネントを返す関数を渡しています。実際、このコンポーネントはそんな重要ではないです。 値や関数を受け取って画面に表示してるだけなので。
ただよくみると、受け取っている引数が
currentTime(音声の経過時間)とかplay(再生するための関数)とかですが、これは AudioProvider から渡されます。それでは本題の AudioProvider 側の実装を 通して、
引数どっからきてるの?を 紹介しますAudioProvider (audio の内部状態を扱うコンポーネント)
class AudioProvider extends React.Component { audio = new Audio(this.props.url); // audio要素の生成 componentDidMount = () => { /** * audioの内部状態に変化があったときに再描画するための処理 * ex.) this.playメソッドが実行されると、"play" を登録してるイベントリスナーが反応し、 * コンポーネントが強制的に再描画される */ this.audio.addEventListener("play", this.forceUpdate); this.audio.addEventListener("pause", this.forceUpdate); this.audio.addEventListener("ended", this.forceUpdate); this.audio.addEventListener("timeupdate", this.forceUpdate); }; componentWillUnmount = () => { this.audio.removeEventListener("play", this.forceUpdate); this.audio.removeEventListener("pause", this.forceUpdate); this.audio.removeEventListener("ended", this.forceUpdate); this.audio.removeEventListener("timeupdate", this.forceUpdate); }; // --------状態変更用のコールバック関数-------- play = () => this.audio.play(); pause = () => this.audio.pause(); jump = value => (this.audio.currentTime += value); render = () => this.props.render({ currentTime: this.audio.currentTime, paused: this.audio.paused, play: this.play, pause: this.pause, jump: this.jump }); }AudioProvider が
- audio 要素を保持し、
- 変更をゴリゴリ行い、
- 変更を検知して自分自身(とその配下の AudioPlayer コンポーネント)を再描画し、
- 新しい状態を render prop に渡す
などなど、汚れ仕事をたくさんしています。
ここで大事なのは、
audio要素自体をrender propに渡さないということです。audio 要素の変更がAudioProviderのおかげでブラックボックスにできているのに、
audio要素自体を渡してしまうと、渡された側(AudioPlayer 側)でaudio要素の状態を変えたりできちゃいます。それを防ぐために、
audio 要素を分解して currentTime や paused などを ただの値として render prop に渡しているワケです。render = () => this.props.render({ currentTime: this.audio.currentTime, paused: this.audio.paused, play: this.play, pause: this.pause, jump: this.jump });さいごに
![]()
audio 要素だけではなくて、 内部状態をもつ要素をReactコンポーネントで綺麗に扱いたい 場合はこのパターンでだいたい乗り切れる気がします。
ただ、解決策は今回の render prop パターンだけではないと思うので、みなさんのプラクティスも、ぜひ教えて下さい




