- 投稿日:2020-02-16T23:01:13+09:00
まだMVCで消耗してるの?〜Django x Reactで始めるSPA開発〜
ここ最近JSフレームワークを使ったサイトが増えてきています。
とくにReactやVueなどのJSフレームワークはSPAというアプリケーション開発によく使われ、サイトを利用するユーザーだけでなく開発者にも多くのメリットをもたらします。想定読者
- Web開発経験者
- APIを使ったWebアプリケーションを開発したことがある人
- JavaScriptをそこそこ知っててPythonもそこそこ知ってる人
- Djangoをちょっと知っている
- MVCもしくはMTVを使った開発をしたことがある人
別記事にもっと詳細に書いた記事があるので、本記事で難しいと感じた方やもっと深いところまで学習したい方はこちらをご覧ください。
まだMVCで消耗してるの?〜React x Djangoで始める今時Web開発〜この記事ではフロントエンドにReact、バックエンドにDjangoを使用してチュートリアルを進めていきます。
チュートリアルはToDoアプリを題材にして進めていきます。SPAとは
SPAはSingle Page Applicationと呼ばれ、ユーザーエクスペリエンスを向上させるのに有効な手立てとなります。
また、データバインディング、仮想DOM、Componentの3つの特徴を兼ね備えています。データバインディング
素のJavaScriptを使って値を変更する場合、DOMを指定して値を変更する処理を毎回動かさなければなりません。
ですが、JSフレームワークを使うと定義しておいた変数が更新されるたびに画面上の値も変更されます。仮想DOM
JSフレームワークには、クライアントのブラウザで描画をするためのDOMとサーバーとDOMの間に存在する仮想DOMの2種類があります。
仮想DOMの役割は、新しくサーバーから吐き出された仮想DOMと現在存在する仮想DOMとの差分を取り、その差分をDOMに反映することです。
そのためDOMの更新は差分があった部分だけとなり、ページのレンダリングを高速にすることができます。Component
JSフレームワークでは、ページの要素をコンポーネントと呼ばれる部品単位に分割することができます。 そうすることで、コンポーネントを再利用することができ同じコードを書かずに済みます。
このチュートリアルではページを1枚作るだけなので、ユーザーエクスペリエンスにつながるメリットを肌で感じることはできないかもしれないのですが、開発面でのメリットは感じることができると思います。
Django環境構築
まずはバックエンドから進めていきます。
以下のコマンドを順に実行してください。
mkdir todo-backend cd todo-backend python3 -m venv env source env/bin/activate pip install django djangorestframework django-cors-header django-admin startproject project . django-admin startapp todo python manage.py migrate python manage.py createsuperuser python manage.py runserver環境が構築できたら127.0.0.1:8000にアクセスしてください。
初期画面が表示されるはずです。Django環境の設定
settings.pyにプラグイン追加の設定とクロスオリジンの設定を追記していきます。
クロスオリジンの設定は、WebブラウザからAPIを実行するときにアクセス拒否されるのを防ぐために追記します。settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', 'todo' ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ] # 許可するオリジン CORS_ORIGIN_WHITELIST = [ 'http://localhost:3000', ]ついでにprojectディレクトリ内のurl設定ファイルに、APIのルーティングを設定します。
urls.pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('todo.urls')), ]バックエンドの実装
todoアプリ内を実装していきます。
models.pyfrom django.db import models class Todo(models.Model): name = models.CharField(max_length=64, blank=False, null=False) checked = models.BooleanField(default=False) def __str__(self): return self.nameマイグレーションを実行します。
python manage.py makemigrations python manage.py migrateadmin.pyfrom django.contrib import admin from .models import Todo @admin.register(Todo) class Todo(admin.ModelAdmin): passserializer.pyfrom rest_framework import serializers from .models import Todo class TodoSerializer(serializers.ModelSerializer): class Meta: model = Todo fields = ('id', 'name', 'checked')views.pyfrom rest_framework import filters, generics, viewsets from .models import Todo from .serializer import TodoSerializer class ToDoViewSet(viewsets.ModelViewSet): queryset = Todo.objects.all() serializer_class = TodoSerializer filter_fields = ('name',)urls.pyfrom rest_framework import routers from .views import ToDoViewSet from django.urls import path, include router = routers.DefaultRouter() router.register(r'todo', ToDoViewSet) urlpatterns = [ path('', include(router.urls)), ]ここまで終えたら、http://localhost:8000/admin にアクセスしてToDoをいくつか追加しておいてください。
React環境構築
Reactの環境立ち上げにはCreate React Appを使います。
yarn create react-app todo-frontend cd todo-frontend yarn starthttp://localhost:3000にアクセスして画面が正常に表示されたら環境構築完了です。
ルーティング
Reactはルーティング機能を持たないので、別にプラグインをインストールします。
yarn add react-router-domsrcディレクトリ直下にRouter.jsxを作成してください。
Router.jsximport React from 'react'; import { BrowserRouter, Route } from 'react-router-dom'; import Top from '../components/Top'; const Router = () => { return ( <BrowserRouter> </BrowserRouter> ); }; export default Router;App.jsにルーティングを読み込ませます。
App.jsimport React from 'react'; import Router from './configs/Router'; function App() { return ( <Router /> ); } export default App;画面デザイン
画面のデザインにはMaterial UIというデザインフレームワークを使います。
Reactのプラグインとして提供されているので、yarn addでインストールしてください。yarn add @material-ui/core下の画像が出来上がり図です。
API実装
まずはAPIを実装していきます。
一つのコンポーネント内に含めると可読性が落ちるので、別ファイルに分けてAPI処理を実装します。
実装するAPI処理は、ToDoリスト取得、ToDo作成、ToDoのチェック、ToDo削除の4つです。src/common/apiディレクトリを作り、その中にtodo.jsを作成してください。
todo.jsconst originUrl = 'http://127.0.0.1:8000'; const getTodoList = (() => { const url = new URL('/api/todo/', originUrl); return new Promise( (resolve, reject) => { fetch(url.href) .then( res => res.json() ) .then( json => resolve(json) ) .catch( () => reject([]) ); }); }); export default getTodoList; export const postCreateTodo = (name) => { const url = new URL('/api/todo/', originUrl); return new Promise( resolve => { fetch(url.href, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name }) }) .then( res => res.json() ) .then( data => resolve(data) ); }); }; export const patchCheckTodo = ((id, check) => { const url = new URL(`/api/todo/${id}/`, originUrl); fetch(url.href, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ checked: check }) }); }); export const deleteTodo = ((id) => { const url = new URL(`/api/todo/${id}/`, originUrl); fetch(url.href, { method: 'DELETE' }); });コンポーネント実装
次にコンポーネントを実装します。
index.jsximport React, { useEffect, useState } from 'react'; import Button from '@material-ui/core/Button'; import Box from '@material-ui/core/Box'; import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Checkbox from '@material-ui/core/Checkbox'; import Container from '@material-ui/core/Container'; import { makeStyles } from '@material-ui/core/styles'; import TextField from '@material-ui/core/TextField'; import getToDoList, { postCreateTodo, patchCheckTodo, deleteTodo } from '../../common/api/todo'; const useStyles = makeStyles(theme => ({ todoTextField: { marginRight: theme.spacing(1) } })); const Top = () => { const classes = useStyles(); const [todoList, setTodoList] = useState([]); const [todo, setTodo] = useState(''); useEffect(() => { (async () => { const list = await getToDoList(); setTodoList(list); })(); }, []); const handleCreate = async () => { if ( todo === '' || todoList.some( value => todo === value.name ) ) return; const createTodoResponse = await postCreateTodo(todo); setTodoList(todoList.concat(createTodoResponse)); }; const handleSetTodo = (e) => { setTodo(e.target.value); }; const handleCheck = (e) => { const todoId = e.target.value; const checked = e.target.checked; const list = todoList.map( (value, index) => { if (value.id.toString() === todoId) { todoList[index].checked = checked; } return todoList[index]; }); setTodoList(list) patchCheckTodo(todoId, checked); } const handleDelete = (e) => { const todoId = e.currentTarget.dataset.id; const list = todoList.filter( value => value['id'].toString() !== todoId); setTodoList(list); deleteTodo(todoId); }; return ( <Container maxWidth="xs"> <Box display="flex" justifyContent="space-between" mt={4} mb={4}> <TextField className={classes.todoTextField} label="やること" variant="outlined" size="small" onChange={handleSetTodo} /> <Button variant="contained" color="primary" onClick={handleCreate}>作成</Button> </Box> <FormGroup> {todoList.map((todo, index) => { return ( <Box key={index} display="flex" justifyContent="space-between" mb={1}> <FormControlLabel control={ <Checkbox checked={todo.checked} onChange={handleCheck} value={todo.id} color="primary" /> } label={todo.name} /> <Button variant="contained" color="secondary" data-id={todo.id} onClick={handleDelete}>削除</Button> </Box> ) })} </FormGroup> </Container> ) }; export default Top;最後に
ToDoアプリを一つ作りましたが、この記事の内容だけだとまだ実用はできないので、いずれホスティングに載せるところまでを紹介しようと思います。
誤字脱字や、間違いがあればご連絡ください。
ソースコードをGitHubに上げているので、必要であれば使ってください。フロントエンド
https://github.com/uichi/todo-frontendバックエンド
https://github.com/uichi/todo-backend参考
- 投稿日:2020-02-16T22:59:51+09:00
Reactで.envがどうしても読めなかった時に試してみるといいかもしれないこと
Reactで環境変数を.envファイルから読み込みたかった時の話です。
どうにかした軌跡を置いておきます。TL;DR
- webpack.config.js内に設定を追加した
- dotenv-webpackはなんかのタイミングで入ってたけど、入ってなければ入れるとよいと思います
- 僕はたぶんbabelかstorybook入れたとき入った
webpack.config.jsconst Dotenv = require('dotenv-webpack'); plugins: [ new Dotenv() ], // なんか言われたら node: { fs: 'empty' }ざっくり環境
- create-react-appした
- webpack-dev-serverで動かしてる
- .envのREACT_APP_HOGE_KEYがundefinedになる
経緯
公式に従って設定してみたのですが、どう頑張ってもundefinedになりました。
プロジェクトのルートに.env
ファイルを作り、REACT_APP_
から始まるものを定義すればいいよと書いてあります。適当にググってもそんな感じの内容が出てきます。
https://create-react-app.dev/docs/adding-custom-environment-variables/#adding-development-environment-variables-in-envでもダメでした。
デフォで入ってるのでダメなら改めて自分で入れてみるかぁと思い調査。
結論
明示的に
dotenv-webpack
を利用することに。
公式: https://www.npmjs.com/package/dotenv-webpack
参考: Can't resolve 'fs' https://megu-tech.hatenablog.com/entry/2019/10/09/170631
webpack.config.js
に設定を追加。webpack.config.jsconst Dotenv = require('dotenv-webpack'); plugins: [ new Dotenv() ], // なんか言われたら node: { fs: 'empty' }で読めるようになりました。webpack絡みでなんか起きてるのかなと思いつつも学びきれてなさみです。
- 投稿日:2020-02-16T22:34:48+09:00
無理してReduxを使わずに、Custom Hookで楽に状態を管理する
はじめに
Reduxを使わなくても、ReactでSPAをつくることは可能です。
画面の初期化時に一度だけ叩くapiをreduxで管理したことはありませんか?
apiを一度叩いて画面に表示するだけなのに、reducer/action/actionCreatorを作ってconnectをしてませんか?
Reduxを無理して使っていませんか?自分が参画したWebアプリケーション開発では、Reactが採用される場合、常にReduxもセットになっていました。
Reduxは要件によってはオーバーエンジニアリングかもなーと感じることが多いので、改めてどんな時にReduxが有効かのか自分なりに考えてみました。
SPAの設計で大事なこと
自分がフロントエンドの設計で大事だと思うのは、「ViewとStateの分離」です。複雑なアプリケーションでこれが出来ていないと可動性が低く変更しづらいです。
Viewはコンポーネントと呼ばれたりしますが見た目の部分です。
Stateはコンポーネントの状態です。本記事では、状態を変化させるロジックも含めStateと呼びます。個人的には以下を実現することが重要です。
ViewとStateが密結合にならず、コードの可読性を保てる。
ユーザーが複雑な操作をしても、アプリケーションの状態が予測可能になっている。
どんな時にReduxが有効か
Reduxが実現しているFluxアーキテクチャは、状態をStoreに、ロジックをAction/ActionCreatorにすることで、アプリケーションの状態を予測可能にしています。
Reduxを使うことで「ViewとStateの分離」は達成出来ます。
ですが、Reduxを使わなくても「ViewとStateの分離」は達成出来ます。
Redux辛いみたいな話はわざわざReduxで解決しなくても良い問題をReduxの冗長な処理を書いて解決しているからだと思います。では、Reduxを使わないと辛い時はどんな時でしょうか?
自分は以下だと考えます。
- コンポーネントの階層が深い
- 画面で発火するイベントが多い
componentの階層が深い
propsのバケツリレーと呼ばれる問題です。
prop drilling
の方が有名でしょうか。
出典:Learn React Context in 5 Minutes - A Beginner's TutorialReactではコンポーネントにデータを渡す時にpropsを使います。
コンポーネントの階層が深いと、親コンポーネントから子コンポーネント、さらに孫コンポーネントと渡したいコンポーネントまで延々とpropsをバケツリレーのように渡していく必要があります。
このバケツリレーを省略するためにReduxを使うのは有用です。
自分は3回を超えたらRedux導入を考えます。画面で発火するイベントが多い
1つの画面でユーザーがやれることが多い画面はReduxが有効だと判断します。
例えば、SlackやGoogle Carenderは1つの画面で様々なイベントを発火でき、リアルタイムにUIを変更しつつapiリクエストを飛ばします。様々なイベントを高速でハンドリングする場合は、アプリケーションの状態が予測しづらくなるのでReduxを使います。
Reduxはdevtoolが強力でstoreに対する変更を全て記録してくれるのでデバッグもしやすいです。Custom Hookで「ViewとStateの分離」を実現する
一覧画面等で、画面初期化時に一度だけfetchしたデータを表示するのにReduxを使うのはオーバーエンジニアリングです。
自分はReactのCustom Hookを使います。
以下はユーザー情報を表示するだけの架空の画面の例です。
Custom HookにState(状態とロジック)を切り出します。
Custom HookはUtil的な使い方だけでなく、ドメイン固有の状態とロジックを管理するのにも有効です。
useUser.tsexport const useUser = (): [ User | undefined, () => void ] => { const [user, setUser] = React.useState<User | undefined>(undefined); const [errorMessage, setErrorMessage] = React.useState<string | undefined>(undefined); const fetchUser = async (): Promise<void> => { try { const res = await axios.get<User>('/api/user'); setUser(res.data); } catch (e) { setError(e.response.date.message) } }; return [ user, errorMessage, fetchUser, ]; };stateとstateを操作するロジックを
useUser
というCustom Hookに分離しています。
Reduxのreducer/action/actionCreatorをまとめたduckに近いですが、少ない記述量で「ViewとStateの分離」が出来ています。container.tsimport { useUser } from 'useUser'; export const UserContainer: React.FC = () => { const [user, errorMessage, fetchUser] = useUser(); React.useEffect(() => { fetchUser(); }, [fetchUser]); return ( <UserComponent user={user} errorMessage={errorMessage} /> ); };Custom Hookは任意のcontainer/component層でデータとロジックを注入できます。
自分はいつもcontainer層で注入します。Reduxを使うならアプリケーション全体で、Stateの管理方法をReduxで統一したいと思ってた時もありましたが、最近は要件によってCustom HookとReduxを使い分けるのが良いかなと考えています。
Custom Hookの中でapiリクエストをする場合、middlewareを気にしなくて良いです。
今のところ上手くいってますが、同じ画面やドメインでCustom HookとReduxの両方を使わないようにしています。
まとめ
Reactでは「ViewとStateの分離」をいくつかの方法で実現出来ます。
とりえあずReduxにすると大きく外すことがないですが学習コストやコード量が大きいので、Reduxでオーバーエンジニアリングになりそうな時はCustom Hookがおすすめです。
参考
JavaScript: Reduxが必要なとき/不要なとき(翻訳)
- 投稿日:2020-02-16T21:38:44+09:00
React のチュートリアルを Typescript でやってみた【環境構築編】
背景
React 触ってみたい。
Typescript もやってみたい。作業環境
今回は docker を使用し環境を作る。
- macOS Catalina 10.15.3
- Docker version 19.03.5, build 633a0ea
- docker-compose version 1.25.3, build d4d1b42b
- webpack@4.41.6
- typescript@3.7.5
- react@16.12.0
開発環境の準備
プロジェクトのディレクトリーを作成
作業するディレクトリを作成します。
ここではreact-tutorial
としておきます。mkdir react-tutorial cd react-tutorialディレクトリの中に以下のファイルを作成します。
- Dockerfile
- docker-compose.yml
以下それぞれのファイルの内容
Dockerfile
DockerfileFROM node:13.8.0-alpine3.11 ENV NODE_ENV=development WORKDIR /appdocker-compose.yml
docker-compose.ymlversion: "3" services: webserver: container_name: react-tutorial image: react build: . volumes: - ./:/app tty: true ports: - 8080:8080tree はこんな感じ
react-tutorial $tree . ├── Dockerfile └── docker-compose.yml開発環境の作成
とりあえずビルドします。
docker-compose up --build -d以下のコマンドでコンテナが起動していれば OK
react-tutorial $docker-compose ps起動している場合以下のように表示される。
react-tutorial $docker-compose ps Name Command State Ports --------------------------------------------------------------------------- react-tutorial docker-entrypoint.sh node Up 0.0.0.0:8080->8080/tcpコンテナ内に移動する場合は以下
docker exec -it react-tutorial /bin/shpackage.json の作成
まずはコンテナ内に移動
docker exec -it react-tutorial /bin/sh以下のコマンドで package.json を作成します。
npm init設定は以下のように設定しました。
package name: (app) react-tutorial version: (1.0.0) description: entry point: (index.js) webpack.config.js test command: git repository: keywords: author:humi3 license: (ISC) About to write to /app/package.json:ワークディレクトリに以下の package.json が以下の内容でされたと思います。
package.json{ "name": "react-tutorial", "version": "1.0.0", "description": "## コンテナビルド `docker-compose up --build`", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "humi", "license": "ISC" }※今回は 1 から環境を構築しますが、以下のコマンドで React + Typescript の環境できます。
my-app
の箇所にプロジェクト名npx create-react-app my-app --typescript必要なライブラリのインストール
Webpack
npm install --save-dev webpack webpack-cliTypescript で必要なライブラリ
npm install --save-dev typescript awesome-typescript-loader source-map-loaderreact & react-dom
npm install --save-dev react react-dom @types/react @types/react-domディレクトリとファイルの準備
以下のディレクトリの構成になるようにファイルを作成します。
react-tutorial ├── dist │ ├── index.html │ └── bundle.js ├── node_modules ├── src │ └──index.tsx ├── Dockerfile ├── docker-compose.yml ├── package-lock.json └── package.jsonbundle.js 及び index.tsx はファイル作成だけでいいです。
index.html だけ以下のような内容にしておきます。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <title>TypeScript HelloWorld</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html>tsconfig.json の作成
以下のコマンドで
tsconfig.json
を作成します。npx tsc --init設定は以下のようにしました。
tsconfig.json{ "compilerOptions": { "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "outDir": "./dist/" /* Redirect output structure to the directory. */, "strict": true /* Enable all strict type-checking options. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "sourceMap": true /* Emit a single file with source maps instead of having a separate file. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "jsx": "react", "lib": ["es2015", "dom"] } }現在のディレクトリ
react-tutorial ├── dist │ ├── index.html │ └── bundle.js ├── node_modules ├── src │ └──index.tsx ├── Dockerfile ├── docker-compose.yml ├── package-lock.json ├── package.json └── tsconfig.jsonwebpack.config.js の作成
webpack.config.js
を作成します。react-tutorial ├── dist │ ├── index.html │ └── bundle.js ├── node_modules ├── src │ └──index.tsx ├── Dockerfile ├── docker-compose.yml ├── package-lock.json ├── package.json ├── tsconfig.json └── webpack.config.json
webpack.config.js
の内容webpack.config.jsconst path = require("path"); module.exports = { entry: path.resolve(__dirname, "./src/index.tsx"), output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js" }, devtool: "source-map", target: "node", resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, module: { rules: [ { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, { enforce: "pre", test: /\.tsx?$/, loader: "source-map-loader" } ] } };以上でファイルの準備が完成しました。
動作確認
index.tsx の内容を以下へ
index.tsximport * as React from "react"; import * as ReactDOM from "react-dom"; export default class App extends React.Component<{}, {}> { render(): JSX.Element { return <div>HelloWorld!</div>; } } ReactDOM.render(<App />, document.getElementById("root"));package.json に build 用の script を追加
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"webpack" },以下のコマンドで build を行います。
npm run buildindex.html をブラウザで開き
HelloWorld!
が表示されたら OK です。
今回は、環境構築の部分をやってみました。
次回は、公式のチュートリアルを Typescript にしてやっていきたいと思います。参考
https://speakerdeck.com/takepo/reactxreduxniokerutypescriptru-men
https://qiita.com/soarflat/items/28bf799f7e0335b68186
https://qiita.com/one-kelvin/items/b810aafb6b5ef90789a3
- 投稿日:2020-02-16T20:29:46+09:00
2020年版 Node.js+Reactのインストール
1.概要
MacbookにNode.jsとReactをインストールし、アプリを開発するための環境を構築するための手順について。
2.前提条件
事前作業
- MacのOSを最新にする
- Xcodeのコマンドラインツールをインストールする
作業日時
- 2020年2月
環境
- MacBook Pro
- macOS Catalina
ソフトウェアのバージョン
ソフトウェア バージョン nodebrew 1.0.1 Homebrew 2.1.11 yarn 1.21.1 3.インストール手順
Node.jsをインストールする
以下の流れでインストールする。
- Homebrewのインストール
- nodebrewのインストール
- Node.jsのインストール
Homebrewのインストール
以下のコマンドでHomebrewをインストールする。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"nodebrewのインストール
Homebrewを使用してインストールする。
brew install nodebrew
インストール後は以下コマンドでnodebrewのバージョンが確認できます。
nodebrew -v nodebrew 1.0.1
node.jsのインストール
$ mkdir -p ~/.nodebrew/src最新版を取得する際は
$ nodebrew install-binary latest安定版を取得する際は
$ nodebrew install-binary stableインストールされたnodeを有効化
$ nodebrew ls上記、コマンドでインストールされたバージョンが一覧できる。
v7.1.0 current: none
インストール直後はcurrent: noneとなっているため、必要なバージョンを有効化する。
$ nodebrew use v7.1.0もう一度nodebrew lsを試すと
v7.1.0 current: v7.1.0
v7.1.0が設定されました。
参考記事
- MacにNode.jsをインストール
ちなみに、Windowsの場合は、以下のインストーラでインストール可能。
https://nodejs.org/en/download/
yarnのインストール
npmの代わりのパッケージマネージャ。
yarn add
でパッケージをインストールできる。$ npm install -g yarnReactのアプリの作成
以下コマンドでReactアプリのフォルダが作成される。
Typescriptで開発を行うため、--template typescript
のオプションを設定する。npx create-react-app <app name> --template typescript各種ライブラリのインストール
作成したフォルダに移動して、各種ライブラリをインストールする。
$ cd my-react-appReduxのインストール
プロジェクトルートフォルダで以下コマンドを実行する。
reduxはreduxそのもの、react-reduxはreactとreduxをつなぐライブラリです$ yarn add redux react-redux typescript-fsa typescript-fsa-reducerstypescript-fsaはAction側、typescript-fsa-reducersがReducer側で利用するライブラリです。
- typescript-fsa → ActionCreatorを簡単に生成するライブラリ
- typescript-fsa-reducers → Reducerを簡単に作成するためのライブラリ
開発ツール(redux-devtools)のインストール
redux-devtoolsは、Reduxで開発する際に利用できる便利な開発ツールで、アクション実行時のstoreの状態を確認するのに利用する。
これに加えてChromeの拡張機能でRedux DevToolsを追加するとツールでRedux上のStoreの状態が確認でき、非常にデバッグしやすくなります。yarn add --dev redux-devtoolsPrettierのインストール
整形ツールのPrettier(-Dで開発版のみにインストールされる)
設定はこちら
https://qiita.com/awakia/items/3a05edfa135762d7952c# yarn add -D prettierEslintのインストール
# yarn add -D eslint \ @typescript-eslint/eslint-plugin \ eslint-plugin-prettier \ eslint-config-prettierMaterial-ui
Materialデザインを利用するためのパッケージ。
タッチ、タップ、クリックなどのイベントを使うためにreact-tap-event-pluginのインストールも必要。# yarn add material-ui react-tap-event-plugin # yarn add @material-ui/core @material-ui/iconsreact-router-dom
react-routerの代わりにConnected React Routerを使う。
URLに応じて、表示するコンテンツを変更するルーティングを行うためにインストールする。# yarn add react-router-dom connected-react-router # yarn add -D @types/react-router-domdate-fns
dateオブジェクトのライブラリ。
momentより軽いらしい。$ yarn add date-fns4.コマンド
ローカルでサーバーを起動する
以下コマンドでローカルでサーバーを起動できる。
http://localhost:8080/でアクセスできる。$ yarn startビルドする
$ yarn build5.終わりに
- firebaseの設定は別途。
以上。
- 投稿日:2020-02-16T20:10:01+09:00
create-react-appで作ったTypeScriptプロジェクトで絶対パスでのimportをしたい
概要
絶対パスでimportしたい。
import Post from '../../components/Post'↓
import Post from 'components/Post'やり方
.envの設定
.env
の中を下記のようにすることで、Webpack側でaliasが効くようになります。NODE_PATH=srcref) https://github.com/facebook/create-react-app/issues/2188#issuecomment-302040874
.tsconfigの設定
これだとTypeScript側が認識してくれないため、
.tsconfig
でもpathを貼る必要があります。tsconfig.json"compilerOptions": { "baseUrl": "./src", "paths": { "*": [ "*"] } }しかし、ただ書き加えても
react-scripts start
すると消されてしまいます...。
どうやらreact-scripts
が勝手に消してくるらしい。そのため別ファイルに定義し、それを
extends
することでそれを回避します。paths.json"compilerOptions": { "baseUrl": "./src", "paths": { "*": [ "*"] } }tsconfig.json{ "extends": "./paths.json", ... }おわり
これで絶対パスで読み取れるようになりました!
import Post from 'components/Post'Webpackerもそうですが、こういうのは導入は便利でも改変しづらいのが面倒ですね。
生が一番です。
- 投稿日:2020-02-16T20:10:01+09:00
create-react-appで作ったTypeScriptプロジェクトで相対パスでのimportをしたい
概要
相対パスでimportしたい。
import Post from '../../components/Post'↓
import Post from 'components/Post'やり方
.envの設定
.env
の中を下記のようにすることで、Webpack側でaliasが効くようになります。NODE_PATH=srcref) https://github.com/facebook/create-react-app/issues/2188#issuecomment-302040874
.tsconfigの設定
これだとTypeScript側が認識してくれないため、
.tsconfig
でもpathを貼る必要があります。tsconfig.json"compilerOptions": { "baseUrl": "./src", "paths": { "*": [ "*"] } }しかし、ただ書き加えても
react-scripts start
すると消されてしまいます...。そのため別ファイルに定義し、それを
extends
する形で定義する必要があります。paths.json"compilerOptions": { "baseUrl": "./src", "paths": { "*": [ "*"] } }tsconfig.json{ "extends": "./paths.json", ... }おわり
これで相対パスで読み取れるようになりました!
import Post from 'components/Post'Webpackerもそうですが、こういうのは導入は便利でも改変しづらいのが面倒ですね。
生が一番です。
- 投稿日:2020-02-16T18:08:09+09:00
雑に知ってしまったDockerを知り直す ~アプリケーション持ち込み編~
おさらい
第2回です。
前回の投稿はこちら
前回はnginxのイメージをpullして来てコンテナ起動。
ポートフォワードしてウェルカムページを表示させるまでやりました。今回やりたいこと
- ローカルで適当にアプリケーションを用意する
- どうにかしてそのアプリケーションをコンテナの中にぶちこむ
- ホスト側から起動したアプリケーションへアクセスできるようにする
アプリケーションを用意
Nodeとgithubを勉強したときに作った、first-stepっていうオシャレ気取った名前のリポジトリが放置されていたので、これを再利用します。
せっかくなので触ったこと無いReactへ転生。この辺のステップは本題と外れてしまうので割愛。
$ npm start
これをどうにかしてコンテナへ持っていきたい。
コンテナを用意
前回はnginxのイメージを利用しましたが、今回はnodeのイメージを利用します。
現在のnodeの最新バージョンはv13.8.0
なんですが、ホストにインストールしてるのは安定版のv12.16.0
なのでこれをインストールしたい。バージョン指定してイメージをpullするのどうやるんだろう・・と思いつつとりあえずイメージを探します。
コマンドでも探せるみたいなんですが、初めてなのでGUIに頼ります。なるほどなるほど、なんかそれっぽいですね。
調べてみるとこれはTagと呼ばれるもので、古いのバージョンのイメージが欲しい場合はこのTagを指定してpullする必要があると。ただ、同じバージョン(v12.16.0)でもstretchとかslimとか更に違いがあるのが気になる。。
で更に調べると、例えばnodeの場合だとバージョンは同じでも無駄なファイルを省いて最適化されたslimバージョンがあったり、Debianのバージョンが異なるもの(stretchとかbusterとかはDebianのコードネーム)があったりと結構細かい違いがあるということが判明。
このあたりもちゃんと意識して適切なイメージを選択する必要があるってことか・・
で、更に気になったのがTag指定しない場合はどうなるんだ?とかlatest指定すると結局どれを引っ張ってくるのよとか、そもそもこれ誰が作ったイメージなん?安全なん?とか・・
TODO: これも色々調べてみたんですけど、今回の趣旨からは外れそうなのとボリューム膨らみそうなので別記事でまたやります。今回はとりあえず名前がかっこいいのでbusterでやります。
$ docker pull node:12.16.0-buster 12.16.0-buster: Pulling from library/node dc65f448a2e2: Pull complete 346ffb2b67d7: Pull complete dea4ecac934f: Pull complete 8ac92ddf84b3: Pull complete a3ca60abc08a: Pull complete ab849ba5abe0: Pull complete b9e0c215e971: Pull complete 766774022603: Pull complete f199c0426b2a: Pull complete Digest: sha256:66d5994de29952fe982729ef7c7f8d4c50d528279db386efbf373451f534fa16 Status: Downloaded newer image for node:12.16.0-buster $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE node 12.16.0-buster ce43ce61c1de 28 hours ago 882MB $ docker run [イメージ名:TAG] $ docker run -d --name node-react node:12.16.0-buster 505ba1fdefd2f705f90b083cee3653445256a0917e054147f8251d4e0eeef122 #前回はrunする前にイメージをpullしてなかったのでこのタイミングで色々ダウンロードしてましたが、 今回は事前にpull済なのでローカルのイメージを利用。カシコイ $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8a126cad2450 node:12.16.0-buster "docker-entrypoint.s…" 5 seconds ago Exited (0) 4 seconds ago node-reactで、ここで作成したコンテナのSTATUSを見るとUp(起動中)ではなく、Exitedになっちゃってる。
前回のnginxの場合サーバを立ち上げてUp状態になっていたけど、今回のイメージはあくまでnodeの実行環境を用意するだけなのでコンテナが立ち上がりっぱなしにはならないのかな、となんとなく推測。
環境を構築して後続の命令が無いと、プロセスが死ぬようになっているらしい。起動するアプリケーションも無いのに立ち上げておく意味ないもんね。
-itd
ってオプションつけるとコンテナが立ち上がりっぱなしになるらしいのでお試し。$ docker run -itd -i ホストの入力をコンテナの標準出力につなげる。コンテナ側へ正しくコマンドを渡せるようにする。 -t コンテナの標準出力をホストの標準出力につなげる。ttyをコンテナに割り当てて、対話できるようにする。 -d コンテナに入らずバックグランドで起動する $ docker run -itd --name node-react node:12.16.0-buster 10235f0996b7c1b1e5f47c5b1c36651ff4fe20cd72b14f47dc5a11f0e8607780 $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 10235f0996b7 node:12.16.0-buster "docker-entrypoint.s…" 3 seconds ago Up 2 seconds node-reactうんうん、STATUSがUpになってる。
コンテナに入って作業したい場合は、とりあえず-itdって覚えても良いかもしれない。
TODO: 1個づつオプション外して試してみたけど、ここの挙動が上手く理解できていないので別の機会にちゃんと調べる。# コンテナの中に入る $ docker exec -it node-react /bin/bash root@8bf7a6704bb9:/# node -v v12.16.0ちゃんと指定したバージョンで入ってますね
コンテナにアプリケーションをぶちこむ
最初は「ファイル転送でもすれば良いんかなぁ」とかかなり脳筋なこと考えていたんですけど、考えてみたらgithubからcloneしてきて起動すれば良いんじゃね?と思ったのでお試し。
root@8bf7a6704bb9:/# git --version git version 2.20.1git入ってる!勝った
# 適当なディレクトリへ移動して root@8bf7a6704bb9:/# cd /usr/src/ # cloneして root@8bf7a6704bb9:/usr/src# git clone https://github.com/wol-827/first-step.git Cloning into 'first-step'... remote: Enumerating objects: 22, done. remote: Total 22 (delta 0), reused 0 (delta 0), pack-reused 22 Unpacking objects: 100% (22/22), done. root@8bf7a6704bb9:/usr/src# cd first-step/ # アプリ起動に必要なモジュールインストールして root@8bf7a6704bb9:/usr/src/first-step# npm i found 0 vulnerabilities # アプリケーション起動 root@8bf7a6704bb9:/usr/src/first-step# npm start Compiled successfully! You can now view sample-app in the browser. Local: http://localhost:3000/ On Your Network: http://172.17.0.2:3000/ Note that the development build is not optimized. To create a production build, use npm run build.起動できてそう!良い感じ
コンテナ起動時にポートフォワード設定し忘れてしまったので、改めて設定。
アプリケーションは3000番で起動していて、8080へ飛ばしたいのでこんな感じ。$ docker run -itd -p 8080:3000 --name node-react node:12.16.0-busterできた。ウレシイ。
まとめ
Dockerについて調べていると、環境を汚すことなくアプリケーションの実行環境を用意できる、なんでことが書かれているのをよく見るんですが身を持ってそれを実感しました。
ホスト側にDockerイメージだけ用意してあれば、nodeやらReact用のモジュールやらはコンテナの中にだけ入れるのでホスト側はキレイな体を保てると。専用のコンテナを立てるのでnodeのバージョンの切り替えも不要。
イメージはこれで、githubのリポジトリはこれ、コマンド実行手順はこれ、っていう風に準備すれば、とりあえず環境は手に入る。便利便利。とはいえ今回の場合、コンテナの中に入ってcloneしたり色々コマンド実行しているのが冗長。
次はこの辺りをコマンド1発で出来るようにしたいと思います。たぶん出来る。出来ろ。
- 投稿日:2020-02-16T17:32:05+09:00
ReactとAmplifyでCognitoとgoogleサインイン/サインナップ試してみた
一通り設定内容を把握したかったので、マニュアルで設定してみた記録です。先に感想ですが、サードパーティーのログイン機構が簡単に実装でき、だいぶ強力なサービスでした。
参考にしたページは、以下になります。
https://aws-amplify.github.io/docs/js/authenticationCognitoとGoogle APIsの設定
Mange User Pools
ユーザープールにアクセスします。
Pool nameを指定します。Pool nameは再設定ができない様なので、名前を決める時はよく考えて決めたほうが良いです。
Attributesを設定します。一度設定すると再設定できないようなので、扱う情報をどうするか事前に考える必要があります。
今回は、Emailアドレスとユーザー名でサインインできるようにします。Standard attributesは、ログインに使用するものだけ指定します。必要に応じて変更するとよいと思いますが、最初に決める必があります。Custom attribe(後で新たに追加)もできるようなので、この辺りは慎重に考えて決めたほうが良いでしょう。
パスワードの強さの設定やPoliciesで、管理者のみ追加権限指定で、任意のユーザー登録を無効にできます。
後、MFAの設定などありますが、必要に応じて設定ができるのでスルーします。
App Clientsが作成されると、App Client Idが発行されます。このIdは、後ほど使います。
作成完了します。ここで生成されたPool Idは、後ほど使います。
Manage Federated Identities
Pool nameを指定して、Authentication providersを開き、赤く囲ったcognitoの設定項目に、先程作成した、User Pool IdとApp Client Idを指定します。そして、作成を押して進みます。
最後に、Identity Pool Idが表示されるます。これも後ほど使いますので、記録してください。
Identity Pool Idは、Edit Identity Poolから確認できます。
Google APIsの設定
User PoolのIdのプロバイダーにGoogleも使うので、その設定をします。
Google Cloud Platformにアクセスして、
メニュー > APIs & Services > Credentials
Web applicationを選択し、Restrictionsを以下の様に設定します。
Authorized JavaScript origins.
https://<your-user-pool-domain>Authorized Redirect URIs.
https://<your-user-pool-domain>/oauth2/idpresponse詳細はこちらにあります。
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-social-idp.htmlYour Client IDをプロバイダーGoogleの設定項目に設定します。
Federated IdentitiesUser Pools > Edit identity poolへ移動します。UnlockしてYour Client IDを入力して保存します。
後から確認もできます。
OAuth 2.0 Client IDs
Reactで使う情報まとめ
UserPoolId
User Pool作成時に生成されたIDで、フォーマットは見た目は、
ap-northeast-1_*******
な感じIdentityPoolId
Identity Pool作成時に生成されたIDで、フォーマットは見た目は、
ap-northeast-1:********-****-****-****-************
な感じ
- ClientId: App Client作成時に生成されたIDで、フォーマットは見た目は、
7nq7sou3kac74bkfuoee92nes33
な感じ
- Google Client Id: Google APIsで作成されたIDで、フォーマットの見た目は、
********-************************.apps.googleusercontent.com
な感じReactでサインイン/サインナップ
Reactの準備
今回はMacOSを使います。コンソールを開き、開発用のディレクトリを作成しそこに移動します。
npmの準備
$ mkdir workingdir $ cd workingdir/ $ brew install npm $ npm install -g n $ n stable # ローカルのnode_modulesを参照する $ npm install -g yarnバージョン確認
$ npm -v 6.13.6 $ node -v v12.15.0Reactをインストール
$ npm install reactReactアプリを作成
$ npx create-react-app my-app --template typescript $ tree -I node_modules -L 2 my-app/ my-app/ # 以下のようなディレクトリが作成されます(node_modulesは省いてます) ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── serviceWorker.ts │ └── setupTests.ts └── tsconfig.json $ cd my-app $ yarn add amazon-cognito-identity-js # cognitoを使うためのモジュールを入れておく $ npm start Compiled successfully! You can now view app in the browser. Local: http://localhost:3000/ On Your Network: http://192.168.***.***:3000/ Note that the development build is not optimized. To create a production build, use npm run build.となれば、OKです。ブラウザーが起動しreact-appが表示されます。
設定ファイルの準備
$ cd my-app/src $ touch appConfig.tsx $ vi appConfig.tsx以下の内容を記述します。
appConfig.tsxconst appConfig = { aws: { cognito: { region: 'ap-northeast-1', IdentityPoolId: 'ap-northeast-1:********-****-****-****-************', UserPoolId: 'ap-northeast-1_*******', ClientId: '***************************' GoogleClientID: '*****-**********.apps.googleusercontent.com' } } } export default appConfigAmplifyで認証機構を準備
$ yarn add aws-amplify aws-amplify-react
App.tsximport React, { Component } from 'react'; import logo from './logo.svg'; import Amplify from 'aws-amplify'; import { Authenticator } from 'aws-amplify-react'; import appConfig from './appConfig' Amplify.configure({ Auth: { identityPoolId: appConfig.aws.cognito.IdentityPoolId, region: appConfig.aws.cognito.region, userPoolId: appConfig.aws.cognito.UserPoolId, userPoolWebClientId: appConfig.aws.cognito.ClientId } }); const federatedConfig = { google_client_id: appConfig.aws.cognito.GoogleClientID }; const App = () => { 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> ); } class AppWithAuth extends Component { render(){ return ( <div> <Authenticator federated={federatedConfig} > <App /> </Authenticator> </div> ); } } export default AppWithAuth;再度、起動します。
$ npm start
以下の様なページが保存されます。cognitoでgmailアドレスでアカウントを作って、
感想
大体動作が把握できたので、次はHosted UI使いたいので
amplify init
使ってコンフィグつくって実装しようと思いました。Googleのみで利用できるようにして、lambda triggerのpre-signupやauthentcation機能を使ってSignupできるユーザーを事前にUserPool側に登録したユーザーの使えるようにして、細かい制御をする予定です。
自由を手に入れた感じです。
- 投稿日:2020-02-16T15:40:09+09:00
ReactNavigation v.5 とReduxでタブのバッジ数を管理する
この記事でやること
Reduxで管理する通知バッジ数をReactNativeのボトムタブに表示させる記事です。
通知バッジ数はスクリーンをまたぐ変数なので、Reduxで管理するのが良いかと思います。こんな感じのやつです ↓↓ (この記事ではこれの簡易版を作ります)
ReactNavigation v.4では、ボトムタブのレンダリングのタイミングに癖があった(?)ようです。
Reduxで状態を更新してもボトムタブに即時反映はされませんでした(僕の周りでも何人か言ってましたが、間違っていたら教えてください!)。
僕は無理やりテキトーな変数を入れたNavigationActionsをボトムタブにdispatchすることで、無理やり再レンダリングさせて、即時反映させていました(本当はこの記事はそれを書く予定だった)。しかしなんと、v.5ではそんな必要がなくなってました…!
ありがてぇ…!やりたいこと
画面から通知バッジ数を変更してボトムタブの数字に即時反映
主な環境
- Expo 36.0.0
- react 16.9.0
- @react-navigation/bottom-tabs 5.0.5
- @react-navigation/native 5.0.5
- @react-navigation/stack 5.0.5
- react-redux 7.1.3
- redux 4.0.5
ReactNavigationはモジュールの移動が激しいですね。
公式ドキュメントを読んで、必要なライブラリをインストールしていってください。画面構成
ホームスクリーンをStackにして、それをひとつのタブに対応させる単純な画面構成です。
コード
Redux
初期状態とreducerの定義をします
src/reducers/index.jsconst INITIAL_STATE = { badgeNumber:0, } const reducer = (state=INITIAL_STATE, action) => { switch (action.type){ case "SET_BADGE_NUMBER": return {...state, badgeNumber:action.badgeNumber} default: return state; } } export default reduceractionの定義をします
src/actions/index.jsexport const setBadgeNumber = badgeNumber => ({ type:"SET_BADGE_NUMBER", badgeNumber })storeを作ります
src/store.jsimport { createStore } from 'redux' import reducers from './reducers' export default createStore(reducers)スクリーン
HomeScreenを作り、Reduxと繋げます。badgeNumber+1というテキストをタッチするとバッジ数がインクリメントされる仕様に。
src/screens/Home.jsimport React from 'react'; import { Text, View, TouchableOpacity } from 'react-native'; import { setBadgeNumber } from '../../src/actions' import { connect } from 'react-redux' const HomeScreen =({ badgeNumber,setBadgeNumber})=>{ return( <View> <TouchableOpacity onPress={()=>setBadgeNumber(badgeNumber+1)}> <Text>badgeNumber + 1</Text> </TouchableOpacity> </View> ) } const mapStateToProps = state => ({ badgeNumber: state.badgeNumber }) const mapDispatchToProps = { setBadgeNumber } const Home = connect( mapStateToProps, mapDispatchToProps )(HomeScreen) export default Homeナビゲーション
ReactNavigationでナビゲーションを作ります。
StackとTabでHomeScreenをラップしていきます。App.jsimport React from 'react'; import { Text } from 'react-native'; import { createStackNavigator } from '@react-navigation/stack'; import { NavigationContainer } from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Provider, connect } from 'react-redux' import { setBadgeNumber } from './src/actions' import store from './src/store' import Home from './src/screens/Home' // ここでStackをつくります()今回は1画面だけだけど const Stack = createStackNavigator(); const HomeStack=()=>{ return ( <Stack.Navigator> <Stack.Screen name="Home" component={Home} /> </Stack.Navigator> ); } // ここでBottomTabを作ります(今回は上で作ったStack1つだけ) const BottomTab = createBottomTabNavigator(); const MyBottomTab=()=>{ return ( <BottomTab.Navigator> <BottomTab.Screen name="Home" component={HomeStack} options={{ tabBarLabel: 'Home', tabBarIcon: () => ( <Text>{store.getState().badgeNumber}</Text> ), }} /> </BottomTab.Navigator> ); } const Main=()=>{ return( <NavigationContainer> <MyBottomTab> </MyBottomTab> </NavigationContainer> ) } const mapStateToProps = state => ({ badgeNumber: state.badgeNumber }) const mapDispatchToProps = { setBadgeNumber } const ConnectedMain = connect( mapStateToProps, mapDispatchToProps )(Main) const App=()=>{ return ( <Provider store={store}> <ConnectedMain /> </Provider> ) } export default Appこれで、HomeScreenのbadgeNumber+1をタッチすれば、ボトムタブの数字も更新されていくと思います。
まとめ
基本に忠実なReduxの使い方、構成です。
上記のコードだけでタブに即時反映してくれるようになってとてもありがたいですね。
ReactNavigation v.5はv.4に比べて見通し良くなったと思います(前はcreateなんとかnavigatorみたいなのが何をやっているかわかりづらかった)。今回は、HomeScreenから通知バッジ数を変更する仕様でしたが、プッシュ通知が来たらバッジ数をインクリメントすることも考えられるかなと。
僕も、プロジェクトにおいてWebsocket対応の優先順位はまだ低いと感じたときには、プッシュ通知が来たのをトリガーにバッジ数をインクリメントしています。
その場合、Focusされているのがどの画面か問わず、storeの状態を書き換えなければいけませんが、上記で言うMainコンポーネントの中でNotifications.addListenerを使ってハンドリングすると上手くいきます。参考になれば幸いです!
- 投稿日:2020-02-16T13:58:30+09:00
ReactとVueとAngularの戦い。
日本ではReactとVueが競い合っているように思えますが、実際はどうなのかを調べてみました。
npm trends
npm trendsという場所がありまして、そこではnpmパッケージのダウンロード数をグラフに表示してくれます。
使い方は簡単、パッケージ名を一つづつ入力していくだけです。
調査結果
react vs @angular/core vs vue vs jquery vs typescript | npm trends
reactとvueと@angular/coreとjqueryとtypescriptの図
Reactの圧勝ですね。Vueは日本だと記事を色々見かけて人気なのはなぜなのでしょうか?
The State of JavaScript 2019
ついでにこちらでも調べてみます。
The State of JavaScript 2019でフレームワークを見てみます。Front End Frameworksカテゴリ
React
https://2019.stateofjs.com/front-end-frameworks/
Vue
https://2019.stateofjs.com/front-end-frameworks/
Angular
https://2019.stateofjs.com/front-end-frameworks/angular/濃いピンクが、以前使用したことがあり、また利用したい。
薄いピンクが、以前使用したことがあり、もう使わない。
濃い緑が、聞いたことがあり学びたいと思っている。
薄い緑が、聞いたことはあるが興味はない。満足度(=濃いピンク/(濃いピンク+薄いピンク))はReactとVueは同じくらいのようです。
SvelteがVueを抜き第二位に入っていますね。
The State of JavaScript 2019: Front End Frameworks