404 lines
12 KiB
Markdown
404 lines
12 KiB
Markdown
|
+++
|
||
|
date = "2018-08-25"
|
||
|
tags = ["webpack","babel"]
|
||
|
title = "exchatのfrontendをやってみる"
|
||
|
slug = "webpack"
|
||
|
+++
|
||
|
|
||
|
## 導入
|
||
|
|
||
|
昨日、[tony612/exchat](https://github.com/tony612/exchat)を扱いましたが、今回はexchatのfrontendの内容になります。
|
||
|
|
||
|
heorkuでは、通常、/app, /webにわけられdeployされます。
|
||
|
|
||
|
なので、phxのようなbackendの成否とreactのようなfrontendの成否は基本的に別々になっています。
|
||
|
|
||
|
つまり、phxのdeployは正常に成功したけど、webpackが失敗して、/webがうまく表示できないなんてこともありえます。
|
||
|
|
||
|
exchatは、backendをelixir(phoenix)で構成し、frontendは、react, reduxなどで構成しているようです。
|
||
|
|
||
|
ここで、frontend(js)のpreview, build, convertなどには、webpack, babelなどを使用しているようです。
|
||
|
|
||
|
今回は、exchatのfrontendの方を少しだけ見ていこうかなと思います。
|
||
|
|
||
|
## webpack
|
||
|
|
||
|
buildされたものは、`./priv/static/app.js`に置かれるようですね。しかし、.gitignoreに書かれていますので、pushしません。
|
||
|
|
||
|
```sh
|
||
|
$ cat webpack.config.js
|
||
|
|
||
|
$ mix deps.get
|
||
|
|
||
|
$ npm i
|
||
|
|
||
|
$ webpack
|
||
|
|
||
|
$ cat ./priv/static/app.js
|
||
|
|
||
|
$ mix phx.server
|
||
|
```
|
||
|
|
||
|
layoutを変えるには、`./web/templates/layout`から`html:head`より上をいじることもできますが、基本的には、`./client/`以下を触ります。
|
||
|
|
||
|
`./config/dev.exs`を見ることで、どのようにpreviewされるのかわかります。
|
||
|
|
||
|
riotを触ってた頃、たまたまwebpackも使ってた気がするんですけど、もう忘れてる。
|
||
|
|
||
|
なお、cloudflareとかを利用してる場合、cacheなどが効いてるので、今回生成されるようなapp.jsみたいな一箇所に詰め込まれたファイルというのは、特に、なかなか反映されないことがあります。これは、purgeをするとか、もしくはherokuapp.comで確認するとかしたほうがいいわけですが、それでも反映するまでにしばらく時間がかかることが多いです。browserのprivate windowを利用しても同じ。frontendはこのあたり、大変ですね。あくまで初見の印象ですが。
|
||
|
|
||
|
previewは、`$ bash ./compile`したあとに、`$ mix phx.server`します。`localhost:4000`
|
||
|
|
||
|
> You are currently using minified code outside of NODE_ENV === 'production'. This means that you are running a slower development build of Redux. You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) to ensure you have the correct code for your production build.
|
||
|
|
||
|
|
||
|
## react
|
||
|
|
||
|
> Warning: It looks like you're using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster. See https://fb.me/react-minification for more details.
|
||
|
|
||
|
```sh
|
||
|
$ cat ./compile
|
||
|
info "Building Phoenix static assets"
|
||
|
NODE_ENV=production webpack -p
|
||
|
mix phoenix.digest
|
||
|
|
||
|
$ bash ./compile
|
||
|
```
|
||
|
|
||
|
どうやら、buildは、`NODE_ENV=production webpack -p`すればいいらしいですね。
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
## heroku
|
||
|
|
||
|
./clientを書き換えて、heroku pushしても、全く変更が反映されずにおかしいなと思っていましたが、heroku cacheが効いていたのかもしれません。
|
||
|
|
||
|
`./priv/static/js/app.js`を`./client/js/app.js`に置き換えることで何故か対応できましたが、errorが色々出るので。
|
||
|
|
||
|
```sh
|
||
|
$ heroku plugins:install heroku-repo
|
||
|
$ heroku repo:purge_cache
|
||
|
```
|
||
|
|
||
|
## update
|
||
|
|
||
|
色々なパッケージをupdateしていきます。
|
||
|
|
||
|
```sh
|
||
|
$ npm list --depth=0 | grep webpack
|
||
|
|
||
|
# package.json update
|
||
|
$ npm install -g npm-check-updates
|
||
|
$ ncu
|
||
|
$ ncu -u
|
||
|
|
||
|
$ npm update
|
||
|
```
|
||
|
|
||
|
webpackを`1 -> 4`にしたので、だいぶ手こずりました。
|
||
|
|
||
|
一応、成果物を上げておきます。
|
||
|
|
||
|
|
||
|
追記 : 動くようになりました。`react-router@2.x`を指定すると動くのですが、`3.x ~ 4.x`では正常に動きません。多分、versionの書き方が必要なのでしょう。特に、prop-typesあたりのコードです。ただし、react 16.xにupdateすると、react-router 2.xには`_react.PropType`で書かれた箇所があります。`node_modules/react-router/lib/PropType.js`なのですが、現在のsrcでreactをupdateしたい場合は、それを書き換えるしかないですね。
|
||
|
|
||
|
forkしてgithubに上げると、こんな感じで取ってこれます。ただし、webpackで反映されないんで、一旦、npm iしたあとに、再度、本家のversionを指定して、npm iすると、なぜかforkしたlibが読み込まれました。
|
||
|
|
||
|
> package.json
|
||
|
|
||
|
```sh
|
||
|
"react-router" : "git:https://github.com/syui/react-router#v2.8.2"
|
||
|
```
|
||
|
|
||
|
追記終わり。
|
||
|
|
||
|
では、updateした設定ファイルを見ていきます。
|
||
|
|
||
|
> .babelrc
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"presets": ["react", "es2015", "stage-2"],
|
||
|
"plugins": [
|
||
|
"transform-function-bind"
|
||
|
]
|
||
|
}
|
||
|
```
|
||
|
|
||
|
> package.json
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"name": "exchat",
|
||
|
"version": "2.0.0",
|
||
|
"description": "phx chat",
|
||
|
"main": "index.js",
|
||
|
"directories": {
|
||
|
"test": "test"
|
||
|
},
|
||
|
"scripts": {
|
||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||
|
},
|
||
|
"author": "syui",
|
||
|
"license": "MIT",
|
||
|
"repository": {},
|
||
|
"devDependencies": {
|
||
|
"babel-core": "^6.26.3",
|
||
|
"babel-loader": "^7.0.0",
|
||
|
"babel-plugin-transform-function-bind": "^6.22.0",
|
||
|
"babel-preset-es2015": "^6.5.0",
|
||
|
"babel-preset-react": "^6.5.0",
|
||
|
"babel-preset-stage-2": "^6.5.0",
|
||
|
"webpack": "^4.17.1",
|
||
|
"webpack-cli": "^3.1.0"
|
||
|
},
|
||
|
"dependencies": {
|
||
|
"css-loader": "^1.0.0",
|
||
|
"sass-loader": "^7.1.0",
|
||
|
"resolve-url-loader": "^2.3.0",
|
||
|
"style-loader": "^0.23.0",
|
||
|
"url-loader": "^1.1.1",
|
||
|
"file-loader": "^2.0.0",
|
||
|
"bootstrap-sass": "^3.3.6",
|
||
|
"gravatar": "^1.5.2",
|
||
|
"history": "^4.7.2",
|
||
|
"humps": "^2.0.1",
|
||
|
"isomorphic-fetch": "^2.2.1",
|
||
|
"jquery": "^3.3.1",
|
||
|
"jwt-decode": "^2.0.1",
|
||
|
"lodash": "^4.11.2",
|
||
|
"normalizr": "^2.3.0",
|
||
|
"phoenix": "file:./deps/phoenix",
|
||
|
"phoenix_html": "file:./deps/phoenix_html",
|
||
|
"prop-types": "^15.6.2",
|
||
|
"react-bootstrap": "^0.32.3",
|
||
|
"react-select": "^2.0.0",
|
||
|
"react-router-redux": "^4.0.8",
|
||
|
"redux": "^4.0.0",
|
||
|
"redux-thunk": "^2.0.1",
|
||
|
"redux-logger": "^3.0.6",
|
||
|
"react-redux": "^5.0.7",
|
||
|
"react-router": "^2.8.1",
|
||
|
"react": "^15.5",
|
||
|
"react-dom": "^15.5"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
> webpack.config.js
|
||
|
|
||
|
```js
|
||
|
var path = require('path');
|
||
|
var webpack = require('webpack');
|
||
|
var config = {
|
||
|
mode: 'production',
|
||
|
entry: [
|
||
|
path.resolve(__dirname, './client/js/app.js')
|
||
|
],
|
||
|
output: {
|
||
|
path: path.resolve(__dirname, './priv/static/js'),
|
||
|
filename: 'app.js',
|
||
|
publicPath: '/',
|
||
|
},
|
||
|
module: {
|
||
|
rules: [
|
||
|
{
|
||
|
test: /\.jsx?$/,
|
||
|
use: {
|
||
|
loader : 'babel-loader',
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
|
||
|
use: [{
|
||
|
loader: 'file-loader',
|
||
|
options: {
|
||
|
name: '[name].[ext]',
|
||
|
mimetype: 'application/font-woff'
|
||
|
}
|
||
|
}]
|
||
|
},
|
||
|
{
|
||
|
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
|
||
|
use: [{
|
||
|
loader: 'file-loader',
|
||
|
options: {
|
||
|
name: '[name].[ext]',
|
||
|
mimetype: 'application/font-woff'
|
||
|
}
|
||
|
}]
|
||
|
},
|
||
|
{
|
||
|
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
|
||
|
use: [{
|
||
|
loader: 'file-loader',
|
||
|
options: {
|
||
|
name: '[name].[ext]',
|
||
|
mimetype: 'application/octet-stream'
|
||
|
}
|
||
|
}]
|
||
|
},
|
||
|
{
|
||
|
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
|
||
|
use: 'file-loader'
|
||
|
},
|
||
|
{
|
||
|
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
|
||
|
use: [{
|
||
|
loader: 'file-loader',
|
||
|
options: {
|
||
|
name: '[name].[ext]',
|
||
|
limit: '10000',
|
||
|
mimetype: 'image/svg+xml'
|
||
|
}
|
||
|
}]
|
||
|
},
|
||
|
{
|
||
|
test: /\.scss$/,
|
||
|
loader: 'style-loader!css-loader!sass-loader'
|
||
|
},
|
||
|
{
|
||
|
test: /\.css$/,
|
||
|
loader: 'style-loader!css-loader'
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
|
||
|
devServer: {
|
||
|
contentBase: path.resolve(__dirname, './client'),
|
||
|
inline: true,
|
||
|
open: true
|
||
|
},
|
||
|
|
||
|
resolve: {
|
||
|
extensions: ['.js', '.jsx', '.scss', '.css']
|
||
|
},
|
||
|
plugins: [
|
||
|
new webpack.ProvidePlugin({
|
||
|
$: 'jquery',
|
||
|
jQuery: 'jquery',
|
||
|
'window.jQuery': 'jquery'
|
||
|
})
|
||
|
]
|
||
|
}
|
||
|
|
||
|
module.exports = config
|
||
|
```
|
||
|
|
||
|
|
||
|
```sh
|
||
|
$ mix deps.get
|
||
|
$ npm i
|
||
|
|
||
|
$ cat ./compile
|
||
|
./node_modules/webpack-cli/bin/cli.js -p
|
||
|
|
||
|
$ bash ./compile
|
||
|
|
||
|
$ mix phx.server
|
||
|
```
|
||
|
|
||
|
あとは、以下の箇所のコードを修正しなければいけません。
|
||
|
|
||
|
### react-select
|
||
|
|
||
|
> Module not found: Error: Can't resolve 'react-select/dist/react-select.css'
|
||
|
|
||
|
jsで読み込まれるので最新では必要なくなったらしいです。
|
||
|
|
||
|
> ./client/js/app.js
|
||
|
|
||
|
```js
|
||
|
- import 'react-select/dist/react-select.css'
|
||
|
```
|
||
|
|
||
|
```sh
|
||
|
$ mix deps.update postgrex
|
||
|
$ bash ./compile
|
||
|
$ mix phx.server
|
||
|
```
|
||
|
|
||
|
### prop-types
|
||
|
|
||
|
> React.PropTypes.func : Uncaught ReferenceError: Component is not defined
|
||
|
|
||
|
https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/187
|
||
|
|
||
|
```js
|
||
|
- import React, { PropTypes, Component } from 'react'
|
||
|
+ import React, { Component } from 'react'
|
||
|
+ import PropTypes from "prop-types";
|
||
|
|
||
|
Settings.propTypes = {
|
||
|
- dispatch: React.PropTypes.func
|
||
|
+ dispatch: PropTypes.func
|
||
|
}
|
||
|
```
|
||
|
|
||
|
こんな感じで直していけばいいです。ファイルは、`grep -R PropType . | sort | uniq`とかしましょう。
|
||
|
|
||
|
`react-codemod`を使って修正できたりもするようです。しかし、今回のsrcは、skipでした。
|
||
|
|
||
|
> Warning: Accessing PropTypes via the main React package is deprecated, and will be removed in React v16.0. Use the latest available v15.x prop-types package from npm instead. For info on usage, compatibility, migration and more, see https://fb.me/prop-types-docs
|
||
|
|
||
|
```sh
|
||
|
$ npm install -g jscodeshift
|
||
|
$ git clone https://github.com/reactjs/react-codemod.git
|
||
|
$ echo `pwd`/react-codemod/transforms/React-PropTypes-to-prop-types.js | pbcopy
|
||
|
$ find ./client/js -name "*.js" | xargs jscodeshift -t "`pbpaste`"
|
||
|
28 ok
|
||
|
Time elapsed: 0.000seconds
|
||
|
|
||
|
# skipされるなら以下のコマンド
|
||
|
$ find ./client/js -name "*.js" | xargs jscodeshift --extensions jsx -t "/Users/syui/git/exchat/react-codemod/transforms/React-PropTypes-to-prop-types.js"
|
||
|
|
||
|
$ npm i react react-dom prop-types
|
||
|
```
|
||
|
|
||
|
### normalizr
|
||
|
|
||
|
> Uncaught TypeError: normalizr.Schema is not a constructor
|
||
|
|
||
|
これはバージョン下げた。
|
||
|
|
||
|
### redux
|
||
|
|
||
|
> Uncaught TypeError: (0 , reduxLogger.createLogger) is not a function
|
||
|
|
||
|
> redux-logger : ^2.x -> ^3.x
|
||
|
|
||
|
```js
|
||
|
- import createLogger from 'redux-logger'
|
||
|
+ import { createLogger } from 'redux-logger'
|
||
|
```
|
||
|
|
||
|
|
||
|
> TypeError: Cannot read property 'listen' of undefined
|
||
|
|
||
|
> unsubscribeFromHistory = history.listen(handleLocationChange);
|
||
|
```sh
|
||
|
"react-router": "^3.0.5",
|
||
|
"react-router-redux": "^4.0.8",
|
||
|
```
|
||
|
|
||
|
ただし、`react-router@2.x`でないと`npm i -S prop-types`に対応したcodeはうまく動作しない。
|
||
|
|
||
|
### react-dom, redux-logger
|
||
|
|
||
|
> Uncaught RangeError: Maximum call stack size exceeded
|
||
|
|
||
|
https://github.com/erikras/redux-form/issues/2629
|
||
|
|
||
|
### webpack
|
||
|
|
||
|
webpackのversionをいろいろいじってると、config/dev.exsやcompileのwebpackを`./node_modules/webpack/bin/webpack.js`から`./node_modules/webpack-cli/bin/cli.js`などに変更しなければならないことがあります。もちろん、環境変数でやってもいいですが。
|
||
|
|
||
|
|
||
|
|
||
|
## 感想
|
||
|
|
||
|
node packageのupdateとかやりましたが、webpackのbuildするのは成功しましたが、やはり./client以下のreactで書かれたcodeが古いので、そちらを書き換えないと表示されない感じでした。残念です。特に、`prop-types`あたりがめんどくさそうでした。
|
||
|
|
||
|
追記 : いけました。ただし、`react-router@2.x`でなければならないのと、`react@15.x -> react@16.x`にしたい場合は、`react-router@2.x`のlib/PropTypesを書き換えなければならない感じでした。
|