- 投稿日:2019-03-10T23:04:28+09:00
旧DMM.R18で女優をフォローするChrome拡張をReact+Webpackで作った
モチベーション
自分はジャンルやシチュエーションより出演者や監督でAVを選ぶことが多い。しかし、DMM.R18(元FANZA)では作品のお気に入りは出来るけど出演者をフォローする機能がない。「検索条件を保存」 という機能があるがこれは10件のキューで上限を超えて追加されると古いものから削除されるため使い勝手が悪かった。
そこで制限なく出演者をフォロー出来るアプリが欲しかった。試しにTwitterで調べてみると同じこと考えてる人はそれなりにいるみたい。
作ったもの
og24715/DMM.pornstars https://github.com/og24715/DMM.pornstars
構成
DMM.pornstars ├── README.md ├── node_modules ├── chrome │ ├── dist │ └── src │ ├── background.js │ ├── detail.js │ ├── favorites │ │ ├── App.jsx │ │ ├── component │ │ │ ├── card │ │ │ │ ├── index.js │ │ │ └── navbar │ │ │ └── index.jsx │ │ ├── index.css │ │ ├── index.html │ │ └── index.js │ └── manifest.json ├── package-lock.json ├── package.json ├── webpack.config.js └── yarn.lock
パス 説明 chrome/ 実質 Chrome 拡張用のプロジェクトディレクトリ。 chrome/dist/ webpack によってバンドルされた諸々が吐かれるディレクトリ。Chromeにはここを読ませる。静的ファイルも src/
に置いてcopy-webpack-plugin
を使ってdist/
配置されるようにしたので当たり前だけどここは触らない。chrome/src/background.js アイコンがクリックされたら一覧を表示するだけ。 chrome/src/detail.js DMM.R18 で作品の画面を表示しているときに、出演者の名前の横にフォローボタンを表示するだけ。 chrome/src/favorites/ 一覧画面。React使ってる。 ハマりどころ
CRA (create-react-app) を使いたい
やめたほうがよさそうです。
だいぶ前に試した記憶だと、CRAはSPAを作るときには非常に頼りになるスタートアップツールですが、react-scripts eject
をしない限り webpack の entry を複数設定することが出来ません。画面ごとにcreate-react-app {project_name}
しなくてはらず地獄です。
local.storage.get
の第一引数に文字列で与えたのに第2引数のコールバック関数への引数がキーバリューになるこれはドキュメントちゃんと読んでください。
local.storage.get
は第一引数に与えられた文字列もしくは文字列配列のキーをもとにキーバリューのオブジェクトを第二引数に与えられたコールバック関数へ引数として与えます。
第一引数に文字列を与える意図としては、キーを決定したので対応するバリューを返して欲しいってことなんですが、第一引数が文字列だろうと配列だろうとキーバリューをオブジェクトを返します。コードにすると
// こうじゃなくて chrome.storage.local.get('following', following = [] => { /* noop */ })); // こう chrome.storage.local.get('following', ({ following = [] }) => { /* noop */ }));こんな感じ。
今の所following
ってキーでしか値を保存していないんですが、キー一つ与えたところでその値は返って返ってこないってことです。
ブラウザ実装のWindow.localStorage.getItem
と挙動が違うのでドキュメント見ないで実装するとハマります。localStorage.getItem() - Web APIs | MDN https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem
chrome.storage - Google Chrome https://developer.chrome.com/apps/storage静的ファイルを
src/
に置きたい
dist/
は極力触りたくありませんでした。
今回のアプリで静的ファイルは拡張のマニフェストファイルであるmanifest.json
と Reactのエントリーポイントとなるindex.html
があります。
これらは素の webapck を通してもdist/
へ配置されないのでwebpack-copy-plugin
を使うしかなさそうです。plugins: [ new CopyWebpackPlugin([ { from: './chrome/src/favorites/index.html', to: 'favorites.html' }, { from: './chrome/src/manifest.json' }, ]),たかがコピーするだけなのにプラグインを追加し無くてはならない歯がゆさに gulp 時代の混沌をに似たものを感じます。
なにかいい方法があれば教えてください。性欲駆動開発
https://twitter.com/search?q=%E6%80%A7%E6%AC%B2%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA
- 投稿日:2019-03-10T20:22:23+09:00
React環境構築
はじめに
Reactの環境を1から構築していく。
アプリディレクトリの作成
まずは
mkdir
コマンドでアプリのディレクトリを作成する。
作成したらアプリディレクトリに移動する。whitecat:~/environment $ mkdir whitecat-app whitecat:~/environment $ ls README.md whitecat-app whitecat:~/environment $ cd whitecat-app/ whitecat:~/environment/whitecat-app $必要npmパッケージのインストール
package.jsonの作成
npmモジュールをインストールするために、最初に空のpackage.jsonを作成する。
whitecat:~/environment/whitecat-app $ echo "{}" > package.json whitecat:~/environment/whitecat-app $ ls package.jsonbabel
babelとは
reactのJSXで記述されたコードは、ブラウザでは解釈できない。そこでブラウザ解釈できるように変換する必要がある。この変換を行ってくれるのがbabelである。
babelのインストール
インストールは
npm install --save-dev babel-core babel-preset-react
コマンドで実施する。babel-coreはbabelの本体である一方、babel-preset-reactはreactアプリケーションに必要なbabelのプラグインをまとめたものだ。babel-preset-hogehogeは、特定の目的のためにbabelで必要なプラグインをまとめてくれているもの。自分でひとつひとつインストールしてもよいが、特に理由なければpresetを使うべき。
webpack
webpackはモジュールバンドラーであり、モジュール(機能ごとにファイルを分割したソースコードやnpmパッケージ)をバンドルして(依存関係を解決したうえで1ファイルにまとめて)くれる。
webpackのインストール
インストールは
npm install --save-dev webpack babel-loader@7 webpack-cli
コマンドで実施する。
モジュールをバンドルするだけならwebpakだけでよいが、reactではモジュールをバンドルする前にbabelでソースコードのJSX記述を変換する必要があるため、babel-loaderを併せてインストールする。なお、babel-coreのバージョン関係から@7のバージョンをインストールしないといけない。webpackでバンドルする際にはcliでコマンド実行させる必要があるので、webpack-cliもインストールする。
react
reactのインストール
最低限必要な2つのreactパッケージを
npm install --save react react-dom
でインストールする。package.json
インストール後のpackage.jsonは以下のようになる。
package.json{ "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-react": "^6.24.1", "webpack": "^4.29.6", "webpack-cli": "^3.2.3" }, "dependencies": { "react": "^16.8.4", "react-dom": "^16.8.4" } }アプリケーションソースコードの作成
srcディレクトリを作成し、サンプルとして以下ファイルを作成する。
index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import Hello from './Hello'; ReactDOM.render( <Hello />, document.getElementById('root') );Hello.jsimport React from 'react'; export default function Hello(){ return <h1> Hello world!! </h1>; }webpack設定ファイルの作成
webpack.config.jsを作成し、バンドルの設定を記述する。
webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['react'] } } } ] } };バンドル実施
./node_modules/.bin/webpack --config webpack.config.js --mode development
を実行し、バンドルファイルを作成する。モードについては、とりあえずdevとしておく。実行するとwebpackは自動的に/src/index.jsを起点として、バンドルする。実行後は、/dist/main.jsがアウトプットとして出力される。
index.htmlの作成
publicフォルダを作成し、index.htmlを作成する。index.htmlでは、バンドルされたmain.jsを読み込むようにする。
index.html<body> <div id="root"></div> <script src=".././dist/main.js"></script> </body>index.htmlを読み込む
htmlをブラウザで開くと、reactが実行されhello world!!が表示される。
最終的なディレクトリの構成
. ├── dist │ └── main.js ├── node_modules ├── package.json ├── package-lock.json ├── public │ └── index.html ├── src │ ├── Hello.js │ └── index.js └── webpack.config.js
おわりに
とりあえず、最低限のreact環境は作成できた。
- 投稿日:2019-03-10T20:22:23+09:00
React初期構築
はじめに
Reactの環境を1から構築していく。
アプリディレクトリの作成
まずは
mkdir
コマンドでアプリのディレクトリを作成する。
作成したらアプリディレクトリに移動する。whitecat:~/environment $ mkdir whitecat-app whitecat:~/environment $ ls README.md whitecat-app whitecat:~/environment $ cd whitecat-app/ whitecat:~/environment/whitecat-app $必要npmパッケージのインストール
package.jsonの作成
npmモジュールをインストールするために、最初に空のpackage.jsonを作成する。
whitecat:~/environment/whitecat-app $ echo "{}" > package.json whitecat:~/environment/whitecat-app $ ls package.jsonbabel
babelとは
reactのJSXで記述されたコードは、ブラウザでは解釈できない。そこでブラウザ解釈できるように変換する必要がある。この変換を行ってくれるのがbabelである。
babelのインストール
インストールは
npm install --save-dev babel-core babel-preset-react
コマンドで実施する。babel-coreはbabelの本体である一方、babel-preset-reactはreactアプリケーションに必要なbabelのプラグインをまとめたものだ。babel-preset-hogehogeは、特定の目的のためにbabelで必要なプラグインをまとめてくれているもの。自分でひとつひとつインストールしてもよいが、特に理由なければpresetを使うべき。
webpack
webpackはモジュールバンドラーであり、モジュール(機能ごとにファイルを分割したソースコードやnpmパッケージ)をバンドルして(依存関係を解決したうえで1ファイルにまとめて)くれる。
webpackのインストール
インストールは
npm install --save-dev webpack babel-loader@7 webpack-cli
コマンドで実施する。
モジュールをバンドルするだけならwebpakだけでよいが、reactではモジュールをバンドルする前にbabelでソースコードのJSX記述を変換する必要があるため、babel-loaderを併せてインストールする。なお、babel-coreのバージョン関係から@7のバージョンをインストールしないといけない。webpackでバンドルする際にはcliでコマンド実行させる必要があるので、webpack-cliもインストールする。
react
reactのインストール
最低限必要な2つのreactパッケージを
npm install --save react react-dom
でインストールする。package.json
インストール後のpackage.jsonは以下のようになる。
package.json{ "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-react": "^6.24.1", "webpack": "^4.29.6", "webpack-cli": "^3.2.3" }, "dependencies": { "react": "^16.8.4", "react-dom": "^16.8.4" } }アプリケーションソースコードの作成
srcディレクトリを作成し、サンプルとして以下ファイルを作成する。
index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import Hello from './Hello'; ReactDOM.render( <Hello />, document.getElementById('root') );Hello.jsimport React from 'react'; export default function Hello(){ return <h1> Hello world!! </h1>; }webpack設定ファイルの作成
webpack.config.jsを作成し、バンドルの設定を記述する。
webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['react'] } } } ] } };バンドル実施
./node_modules/.bin/webpack --config webpack.config.js --mode development
を実行し、バンドルファイルを作成する。モードについては、とりあえずdevとしておく。実行するとwebpackは自動的に/src/index.jsを起点として、バンドルする。実行後は、/dist/main.jsがアウトプットとして出力される。
index.htmlの作成
publicフォルダを作成し、index.htmlを作成する。index.htmlでは、バンドルされたmain.jsを読み込むようにする。
index.html<body> <div id="root"></div> <script src=".././dist/main.js"></script> </body>index.htmlを読み込む
htmlをブラウザで開くと、reactが実行されhello world!!が表示される。
最終的なディレクトリの構成
. ├── dist │ └── main.js ├── node_modules ├── package.json ├── package-lock.json ├── public │ └── index.html ├── src │ ├── Hello.js │ └── index.js └── webpack.config.js
webサーバーの作成と自動ビルド
このままだと、ソースを書き換えるたびにwebpackコマンドを実行し、htmlファイルを再読み込みしないといけない。webpack-dev-serverをインストールすると、ソースコードの書き換えにより勝手に再ビルドしてくれるようになる。
インストール
npm install webpack-dev-server --save-dev
コマンドを実行する。設定
webpack-dev-serverもwebpack.config.jsを設定ファイルとして利用する。なお、まずは設定として[disableHostCheck: true]とする。
※今回は開発環境にcloud9を利用しているためlocal環境ではない。よって、どこからでもアクセスできるようにしないといけない。webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['react'] } } } ] }, devServer: { disableHostCheck: true } };アクセス
./node_modules/.bin/webpacdev-server
コマンドでwebサーバーを起動する。起動後、https://hogehoge.hoge.cloud9.us-east-1.amazonaws.com/public/index.htmlにアクセスすると、hello world!!が表示される。distフォルダ削除
webpack-dev-serverではバンドルしたファイルはインメモリ上に保存される。よって、distフォルダは必要ないため削除する。
おわりに
とりあえず、最低限のreact環境は作成できた。
- 投稿日:2019-03-10T16:22:52+09:00
【React】Hooksの出現で変わったいまどきのコンポーネント設計
これまでのコンポーネント設計
これまでReactである程度開発できる人がアニメーションなどローカルで閉じて制御させたいコンポーネント実現しようとするとき、問題になってくるのが
State
とLifecycle
です。React入門者は各コンポーネントをクラスで作成していると思うのでローカルで
State
を使い、componentDidMount
などのLifecycle method
を使えば良いので問題はありません。しかし、慣れてくるとコンポーネントをクラスでは作成せず、SFC(Stateless Function Component)化していく流れがありました(過去形)。そしてそのような人たちは好んで
State
はRedux
かMobx
でグローバルに一元管理しているはずです。SFC化したコンポーネントはローカルに
State
を保持できないし、Lifecycle method
も使えません。そのため、HoC(Higher-Order Components)を活用し、コンポーネントをラップすることで擬似的にローカルState
を保持させたり、Lifecycle method
使えるようにすることで解決してきました。これまでコンポーネントはSFC化し、必要があればHoCを用いてラップするのがベストプラクティス的な流れがありましたが、React version16.8で正式にリリースしたHooksという機能の出現で流れが変わりました。
※
HoC
の代表的ライブラリーrecompose
の開発の停止の発表、recompose
の開発者がReactチームに参画したみたいなのでHoC
の流行りは終わると思っています。これからのコンポーネント設計
これまでのコンポーネント
まずクラスで作ったコンポーネント、SFC化したコンポーネント、HoCでラップしたコンポーネントの違いについてみてみます。
Component local state lifecycle method performance Class ○ ○ × SFC × × ○ HoC(wrap SFC) ○ ○ ○ つまりパフォーマンスを考えたとき、SFCを使い、必要に応じてHoC(wrap SFC)がベストプラクティスでした。
HooksがもたらすFC(Function Component)とは
Hooksはクラスを使わずとも、ローカルで
state
を管理できたり、様々な機能を使えるようにできるフックAPIです。
useState
、useEffect
、useCallback
、useReducer
などたくさんあります。ここら辺の使い方はたくさん記事が世に出回っているので説明しません。Hooksを利用することで、これまでHoC(wrap SFC)で実現していたローカルの
state
、lifecycle method
の利用を上手く吸収したFC(Function Component)を作ることができます。HoC(wrap SFC)とあまり変わらないが、一々ラップする必要がなくなっているところと、可読性がかなり上がります。
※パフォーマンスの面では、まだHoC(wrap SFC)よりは遅いらしいです。
FCの例をあげておきます。
カラーの部分はわざわざuseState
とuseEffect
を使わなくてもいいのですが、使い方の紹介として使っています。SampleButton.jsimport React, { useState } from 'react' import styled, { keyframes } from 'styled-components' const white = '#F5F5F5' const innerDefaultColor = '#3C3C3C' const outerDefaultColor = '#FF5A5F' const SampleButton = ({ onClick, inner, outer, text, children }) => { const [ hover, setHover ] = useState(false) const [ textColor, setTextColor ] = useState(white) useEffect(() => { if(text) { setTextColor(text) } else { setTextColor(white) } }, [text]) const [ innerColor, setInnerColor ] = useState(innerDefaultColor) useEffect(() => { if(inner) { setInnerColor(inner) } else { setOuterColor(innerDefaultColor) } }, [inner]) const [ outerColor, setOuterColor ] = useState(outerDefaultColor) useEffect(() => { if(outer) { setOuterColor(outer) } else { setOuterColor(outerDefaultColor) } }, [outer]) const scaleUp = keyframes` from { transform: scale(1) } to { transform: scale(${hover? 1.05 : 1}) } ` const Container = styled.div` position: relative display: flex justify-content: center align-items: center width: 210px height: 210px ` const Outer = styled.div` position: absolute top: 0 left: 0 width: 210px height: 210px background: ${outerColor} border-radius: 105px animation-name: ${scaleUp} animation-duration: 0.5s animation-timing-function: ease-in animation-iteration-count: infinite animation-direction: alternate z-index: -1 ` const Inner = styled.div` display: flex justify-content: center align-items: center text-align: center width: 200px height: 200px border-radius: 100px background: ${innerColor} color: ${textColor} cursor: pointer ` return ( <Container> <Outer /> <Inner onClick={onClick} onMouseOver={() => setHover(true)} onMouseOut={() => setHover(false)} > {children} </Inner> </Container> ) } export default SampleButton結論
これからのReactのコンポーネント設計は、SFCとHoC(wrap SFC)主体の設計からSFCとFC主体へとシフトしていきます。Hooksの出現は割と大きな変化だと思うので、一度Reactの知識をアップデートしておいた方がいいと思います。
- 投稿日:2019-03-10T12:57:52+09:00
設定不要で簡単にNode.jsアプリケーション構築できるZero Serverを試してみた
Zero Serverについて公式ページを読んで試したみたという内容です。
詳しく知りたい方は公式を参照するようお願いします。
間違いなどありましたらご指摘お願いいたします。Zero Server とは
※githubのreadmeの説明の部分をgoogle翻訳↓
Zeroは現代のWeb開発を単純化するためのWebフレームワークです。 パッケージ管理やルーティングを気にせずにアプリケーションを構築できます。 Node.js、React、HTML、MDX、および静的ファイルを組み合わせてコードを記述し、それらをすべてフォルダーに入れるのと同じくらい簡単です。 ゼロはそれらすべてに役立つでしょう。 作業を簡単にするために、ルーティング、バンドル、およびトランスパイルのための通常のプロジェクト構成がゼロで抽象化されています。
自分でいろいろ設定しなくても、
require('moment')
と書けば、Zero Serverが自動でmomentをインストールして、解決してくれます。Installation
bashnpm i zeroHello World
時刻を教えてくれるアプリケーションを作成する
time.jsconst moment = require("moment") module.exports = (req, res) => { var time = moment().format('LT'); // 11:51 AM res.send({time: time }) }bashnpx zerohttp://localhost:3000/time にアクセスすると、
time.jsで作成しましたので、localhost:3000/time にアクセスすると内容を表示してくれます。
moment
を自動でインストールして使えているのが確認できました。
サーバーを稼働し続けた状態で、
index.jsx
を作成する。index.jsximport React,{ Component } from 'react' export default class extends Component { static async getInitialProps(){ const { time } = await fetch("/time").then((resp) => resp.json()) return { time } } render() { return <p>Current time is: {this.props.time}</p> } }先ほどの
/time
からjsonを受け取り、jsxも正しく動作しているのが確認できました。webpackなどの設定をいじる必要がなく、簡単にアプリケーション構築できました!
Static Files
https://github.com/remoteinterview/zero/tree/master/docs/static
画像やテキストファイルなどの扱いについて
.js, .ts, .jsx, .tsx, .htm, .html, .md, .mdx以外の場合は、プロジェクトディレクトリにファイルを置けばそのまま表示してくれるようでした。
※プロジェクト直下のディレクトリに
nihon-amagaeru.jpg
を置いた場合Node.js (.js, .ts)
https://github.com/remoteinterview/zero/tree/master/docs/nodejs
hello.jsmodule.exports = function(req, res) { res.send("Hello"); }Zero はExpress.jsを使用しているようなので、req, resのリクエスト・レスポンスのオブジェクトはExpressのものと同じ。
./api/login.js
とファイルを設置した場合はhttp://localhost:3000/api/login
でアクセスできる/api/logion.jsmodule.exports = function(req, res) { res.send("Login Api"); }パラメータの取得
param-test.jsmodule.exports = function(req, res) { console.log(req.body) res.send({body: req.body}) }POST, PUT, PATCH, DELETE でパラメータを問題なく受け取れるようでした。
bash$ curl http://localhost:3000/param-test -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "category=1&sort=asc" {"body":{"category":"1","sort":"asc"}} $ curl http://localhost:3000/param-test -X POST -H "Content-Type: application/json" -d '{"category":1, "sort":"asc"}' {"body":{"category":1,"sort":"asc"}} $ curl http://localhost:3000/param-test -X DELETE -H "Content-Type: application/json" -d '{"category":1, "sort":"asc"}' {"body":{"category":1,"sort":"asc"}}Route Rewrites
user.jsmodule.exports = function(req, res) { console.log(req.params) res.send({params: req.params}) }
/user/luke
にアクセスした時に、./user/luke.js
がなかった場合、req.params
に['luku']
と設定してくれます。TypeScript
.ts
ファイルにすると、TypeScriptとして動作させることができるhello-ts.tsimport * as express from "express"; function handler(req: express.Request, res: express.Response) { res.send("Hello TypeScript") } export default handlerReact
https://github.com/remoteinterview/zero/tree/master/docs/react
hello-react.jsxexport default () => <h1>Hello React</h1>Populating
<head>
react-helmet.jsximport {Helmet} from "react-helmet"; module.exports = () => ( <div> <Helmet> <meta charset="UTF-8" /> <title>Page Title</title> <link rel="canonical" href="http://mysite.com/example" /> </Helmet> <h1>Hello World</h1> </div> )
react-helmet
が使えるので、metaタグなどの扱いも簡単ですImporting CSS/Sass
cssとscssがサポートされているようです。
react-styleimport "./style.scss" module.exports = ({name}) => ( <h1>Hello {name}</h1> )style.scssbody { h1 { color: blue; } }TypeScript
.tsx
とするとことで使用することができました。react-ts.tsximport * as React from "react"; export default class HelloWorld extends React.Component<{}, {}> { render() { return (<b>Hello TypeScript</b>); } }HTML (.htm, .html)
バンドラーはparcelが使われているとのことです。なので
<script>
タグのsrc
から自動で解析してくれました。html-test.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="./style.js"></script> <title>Document</title> </head> <body> <h1>Hello</h1> </body> </html>style.jsimport "./style.scss";style.scssbody { h1 { color: blue; } }Markdown / MDX (.md, .mdx)
自動で
.md
ファイルをhtmlに変換してくれるmarkdown.md# Markdown in Zero Zero renders `.md` files too.mdx-test.mdximport Btn from "./component/btn" # Hello, world! <Btn />
最後まで読んでいただいてありがとうございましたm(_ _)m