React 入門以前 (Windows で環境構築)
犬さんにいろいろ教えてもらった。
@Linda_pp さんて今 TypeScript + React で NyaoVim とか書いてる感じですか?今 ES6 と TypeScript どっちがいいかなぁと思ってるのですが…
— tyru (@_tyru_) June 24, 2016
@_tyru_ はい,型チェックがほしい+豊富な型定義の資産が使いたいので TypeScript で書いてます (NyaoVim は React でなく Polymer ですが).どっちが良いかは一概には言えないと思います.最近だと Flow 使っている人もちらほらいますし.
— 3歳 (@Linda_pp) June 24, 2016
@_tyru_ これから React さわってみるかという感じですか?それなら書き慣れている JS のほうが絶対良いです.TypeScript と JavaScript は差分小さいので後から導入とかもできますし.
— 3歳 (@Linda_pp) June 24, 2016
というわけで
- ES6 (ES2015)
- React
で入門する。
進捗した結果 (リポジトリ)
環境構築メインで中身のコードほぼ書いてないので進捗ダメです。
調べたこと
React
- Reactを使うとなぜjQueryが要らなくなるのか - Qiita
- 今からはじめるReact.js〜React ver0.14〜 - Qiita
- React 触った - 大学生からの Web 開発
- 一人React.js Advent Calendar 2014 - Qiita
旧石器時代のJavaScriptを書いてる各位に告ぐ、現代的なJavaScript超入門
- Section1 ~すぐにでも現代っぽく出来るワンポイントまとめ~ - Qiita
- Section2 ~CommonJSモジュールと仲良くなろう~ - Qiita
- Section3 ~Browserifyをマスターしよう~ - Qiita
- Section4 ~Gulpで処理を自動化しよう~ - Qiita
- Section5 ~ES2015文法を覚えよう(前編)~ - Qiita
npm
npm run-scripts からは gulp のタスクを実行するだけ。
目に付いたメリットは
- Windows でも動く
- タスクが gulpfile.js に集約する
Nodist
複数バージョンの Node の運用には *nix 環境なら n とか nvm とか nave とか nodebrew とか色々あるみたいだけど、 不自由な Windows なので nodist というのを使う。
- Windowsでnode.jsをバージョン管理する - Qiita
- Windowsでのnpm環境の作り方 – Pokosho!
- GitHub - marcelklehr/nodist: Natural node.js and io.js version manager for windows.
ちなみになぜ複数バージョン動かす必要が出たかというと、gulp のタスクを動かした時に以下のエラーが出たため。
(node:61377) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
その解決方法が以下の通り。まだ Node v6 をサポートしていないパッケージが沢山あるので古い Node v5 を使えとの有り難いアドバイス。
node v6 is still not supported in many packages. Consider going back to v5.
ちなみに Nodist 調べたので一応ここに書いたけど、結局複数バージョン入れるのもだるかったので、 おとなしく公式サイトから現時点の LTS (v4.4.6) をダウンロードして入れました (既存の v6 はアンインストールした)。
gulpfile.js
Browserifyの運用 〜 bundleを分ける - Qiita
全部をbundleすると太る
なるほど~
最初これにしようかと思ったが、browserify().bundle() すると1つのJSファイルになってしまう。
Gulp+Browserifyでsrc/**/*.jsをdist/**/*.jsにする | 高橋文樹.com
この記事では1つのJSファイルになってしまう問題を解決するgulpの設定が書かれている。
…と思ったけど今実行したら下記の記事のエラーが出る。
TypeError: dest.write is not a function
Browserify + gulpではまったのでメモ - Qiita
結局上記記事に書かれてる through2 というパッケージを使う方法で解決…と思ったらまたエラー。
Error: File.contents can only be a Buffer, a Stream, or null.
該当箇所の変数を console.log()
で出力したりしてみると、そもそも react パッケージとか babelify も入ってなくて Unexpected token 吐かれてた。
(JSX が有効になってない。結論から言うと .babelrc
というものが必要)
$ npm run build > hello-react@1.0.0 build C:\msys64\home\tyru\git\hello-react > gulp build [02:51:46] Using gulpfile C:\msys64\home\tyru\git\hello-react\gulpfile.js [02:51:46] Starting 'build'... [02:51:46] Finished 'build' after 15 ms Error : C:/msys64/home/tyru/git/hello-react/src/app.jsx: Unexpected token (6:6) while parsing file: C:\msys64\home\tyru\git\hello-react\src\app.jsx SyntaxError: C:/msys64/home/tyru/git/hello-react/src/app.jsx: Unexpected token (6:6) 4 | render: function() { 5 | return ( > 6 | <div className="container">Hello {this.props.name}</div> | ^ 7 | ); 8 | } 9 | }) at Parser.pp.raise (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\location.js:22:13) at Parser.pp.unexpected (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\util.js:89:8) at Parser.pp.parseExprAtom (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:522:12) at Parser.pp.parseExprSubscripts (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:277:19) at Parser.pp.parseMaybeUnary (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:257:19) at Parser.pp.parseExprOps (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:188:19) at Parser.pp.parseMaybeConditional (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:165:19) at Parser.pp.parseMaybeAssign (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:128:19) at Parser.pp.parseParenAndDistinguishExpression (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:596:26) at Parser.pp.parseExprAtom (C:\msys64\home\tyru\git\hello-react\node_modules\babelify\node_modules\babel-core\node_modules\babylon\lib\parser\expression.js:481:19)
ちなみにこうしたら上のような詳細なエラーを吐いてくれるようになった。
.on("error", function (err) { console.log("Error : " + err.message); console.log(err.stack); })
Unexpected token while parsing file - kubotti’s memo
によると次のパッケージを入れるといいらしい (勘)。
- babel-preset-es2015
- babel-preset-react
- react
- react-dom
今からはじめるReact.js〜React ver0.14〜 - Qiita
最新だと React の API が変わってるらしい。
0.14でReactDOMがReactから分離されました。 そのため、render()やfindDOMNode()はReactDOMのメソッドを使用しなければなりません。
//React.renderをReactDOM.renderに変更。 ReactDOM.render( <Index />, document.getElementById('content') );
.babelrc
{ "presets": ["es2015", "react"] }
package.json
{ "name": "hello-react", "version": "1.0.0", "description": "", "main": "dist/app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "gulp build" }, "author": "tyru <tyru.exe@gmail.com>", "license": "ISC", "devDependencies": { "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.5.0", "babelify": "^7.3.0", "browserify": "^13.0.1", "gulp": "^3.9.1", "react": "^15.1.0", "react-dom": "^15.1.0", "vinyl-source-stream": "^1.1.0" } }
gulpfile.js
これだと結局
全部をbundleすると太る
問題が解決してないのですが力尽きました。 gulp 職人の方誰か助けてください。
var gulp = require('gulp'); var browserify = require('browserify'); var babelify = require('babelify'); var source = require('vinyl-source-stream'); gulp.task('build', function() { browserify('./src/app.jsx', { debug: true }) .transform(babelify) .bundle() .on("error", function (err) { console.log("Error : " + err.message); console.log(err.stack); }) .pipe(source('bundle.js')) .pipe(gulp.dest('./dist')) });
今度は bundle.js を読み込むタイミングが早すぎてエラーが出た。 Angular でもある例のやつ。jQuery だったら $.ready() で処理すべきなやつ。
うっかり <head>
タグに <script>
タグで bundle.js を読み込んでしまったんだけど、その際 React では以下のようなエラーが出る。
Uncaught Invariant Violation: _registerComponent(...): Target container is not a DOM element.
とうとう上記のエラーも解消して、肝心の HTML と JS は以下の通り。
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello React</title> </head> <body> <div id='app'></div> </body> <script type="text/javascript" src='dist/bundle.js'></script> </html>
src/app.jsx
const React = require('react'); const ReactDOM = require('react-dom'); const Hello = React.createClass({ render: function() { return ( <div className="container">Hello {this.props.name}</div> ); } }) ReactDOM.render(<Hello name="React" />, document.getElementById("app"));
フロントエンド周りって慣れないと環境構築にほんと時間かかるな…
gulpプラグインの作り方 | js | Horic Design
through2というパッケージは、gulpのstreamまわりをサポートしてくれるプラグインです。
vinyl-source-stream やら through2 やら本来やりたいことと関係ないプラグインがいっぱい出てくるので初心者にはつらい… と思ってたらそこら辺の事情が以下の記事にまとまっていた (ブクマしとるやんけ)。
gulp と browserify と vinyl の話 - <body>
gulp で利用するプラグインをひとつひとつ読み込むと大量の require が並ぶことになる。gulp-load-plugins を利用すると package.json から自動で読み込み利用できるようになり、require する必要がなくなる。
Flowtype
犬さんに教えてもらったのですが力尽きました。
追記:Windows じゃ動きませんでした。つらい。
JavaScript でスマホから気軽に遊べる糸通しゲーム作った
たまにものすごくやりたくなる。 ちょうど HTML5 Fullscreen API で遊びたいと思ってたので作った。
最初データはランダムじゃないけど一通り動くところまで作ったあと一旦興味を失ってしまった。 ので、そこからちゃんと遊べる状態にするまでずいぶん間が開いてしまった。
久しぶりにやってみたくなったので糸通し作って遊んでた。まだ固定データだし素材(画像)がショボくてつまんないけど… https://t.co/BAteeWPuzs
— tyru (@_tyru_) 2016年6月5日
https://t.co/BAteeWxTHU を作るまでのあらすじ pic.twitter.com/NSE8eoN1bd
— tyru (@_tyru_) 2016年6月30日
ただこれで最近中途半端に進めてたことは一旦片付けたのでスッキリした(仕事の方はスッキリしてない。忙しい時ほど寝る時間を削って趣味のコードを書いてしまう法則)。
ほしかった機能
- スマホから遊べること
- スマホの場合、「タッチしっぱなし・離す」で操作できること
- PC から遊べること
- PC の場合、「クリック押しっぱなし・離す」で操作できること
- ゲームのステージ部分のみをフルスクリーンで拡大できること
TODO & FIXME
@_tyru_ うーん、針穴との衝突判定がボール一個分ぐらい下にズレてる気がするぞ…
— tyru (@_tyru_) June 15, 2016
@ArcCosine 最初は速度も緩めた方がいい気がしますねw
— tyru (@_tyru_) June 15, 2016
- これは直さなくていいや
@_tyru_ いや、これくらいの鬼畜難易度の方が気軽に遊ぶゲームとしては良いと思います。
— ArcCosine (@ArcCosine) June 15, 2016
スマホ用の <meta name="viewport">
— tyru (@_tyru_) June 15, 2016
実装中に調べたこと
本文ここまで。以下は調べたことです (自分用メモ)。
HTML5 Fullscreen API
これ知ってゲーム作りたくなった。
SVG
- SVG でアニメーション
- xml - Are SVG parameters such as 'xmlns' and 'version' needed? - Stack Overflow
- svg要素の基本的な使い方まとめ
<path>
- SVG のノードの高さ・幅を取得 (Element.getBBox())
- SVG のテキストをクリックで選択させないようにする
D3.js
- 可視化ライブラリD3.jsの設計が素晴らしすぎる。 | 三度の飯とエレクトロン
- http://ja.d3js.info/alignedleft/tutorials/d3/binding-data/
- Real Time Update using setInterval · GitHub
- D3.jsを使ってSVGベースのドラムマニア風ゲームの基礎を作ってみた - syonx
- D3.jsはなぜ人気があるのか? そして、なぜ難しいのか? | #GUNMAGISGEEK
しかし、D3はデータを基に座標系に変換するなどの計算は行ってくれますが、実際に図を描画する機能はありません。 (中略) 実際にツリーマップを描画する処理は製作者が自身でコードに記述する必要があります。 これは一見不親切なように思えますが、Chartライブラリが提供するグラフのテンプレートデザインをカスタマイズするだけでは足りず、自ら凝ったデータビジュアライゼーションを作成したい製作者にとっては描画する方法を自由に選択できる利点となります。
React や Angular みたいにテンプレート&コンポーネント指向に作れないのはちょっと面倒だと感じた… んだけど次の記事を読んで気が変わった。D3.js 便利。
D3.js の Data-Driven な DOM 操作がおもしろい - てっく煮ブログ
- この記事でやっと .enter() と .exit() の意味をちゃんと理解できた…
- 作成、削除、更新に対してそれぞれアニメーションを付けるなどの処理を加えられるのすごい
javascript - Why are d3's select() and selectAll() behaving differently here? - Stack Overflow
- d3.js - D3js: When to use .datum() and .data()? - Stack Overflow
- d3.js API Advent Calendar 2012 - Adventar
- http://ja.d3js.info/alignedleft/tutorials/d3/
- about d3.js
- D3.jsにあてはまらないこと | プログラミング | POSTD
- API Reference · d3/d3 Wiki · GitHub
SVG, Canvas, WebGL の使い分け
- Canvas と SVG の使い分けに関する考察 – Internet Explorer ブログ (日本語版)
- SVG と Canvas: どちらを選ぶか (Windows)
- デザイン至上主義プログラマー: SVGとCanvas、どちらを選択すべきか。
- 適切な API の選択 (WebGL、Canvas、SVG) (Windows)
requestAnimationFrame
Protractor + WebDriver で楽天銀行と Gmail にログインして入出金明細を保存
上記のリポジトリに Pull Request を出させてもらった。それがこちら。
- (Gmail等に) IMAP でログインしてメールを監視
- 楽天銀行にログインしてワンタイムパスワードのメールを送信
- ワンタイムパスワードが書かれたメールが来るまで browser.wait() + protractor.promise.defer() *1 で待つ
- ワンタイムパスワードでログインして入出金明細を TSV ファイルとして保存 *2
みたいなことしてます。
正直 Protractor のデバッグが一番大変だった。 変な場所で expect() してて全然関係ないテストでコケたりしてた。 デバッグは正直勘で進めてしまったのでよくない。 ちゃんと詳細なログとか取れる方法があるかもっと調べればよかった。
Windows 対応
ついでに既存の npm run-scripts の Windows 対応もした。
本文終わり。以下は調べたことです。
実装中に調べたこと
参考にしたページ
- まだmechanizeで消耗してるの? WebDriverで銀行をスクレイピング(ProtractorとWebdriverIOを例に) - 詩と創作・思索のひろば
- Node.js の IMAP 用 ライブラリ inbox
iconv を Windows でビルドするための環境構築
- SE奮闘記: IMAPでGmailを監視して、新着メールがあったらPHPを実行
- node.js - NodeJS - Error installing with NPM - Stack Overflow
- Windows users are not happy. · Issue #629 · nodejs/node-gyp · GitHub
この2つのサイトがとっかかりとしてはとても助かったんだけど、一つまずい箇所がある。
それは {stream}.on('data', ...)
に来たバッファから文字列を検索してるけど、data
の時に渡されるデータは細切れなので、
iconv の変換時に不正なバイトとしてエラーになる可能性がある (なった)。
あと潜在的に検索文字列の途中で切れてたら検索に引っかからない。
なのでこのようにした。
stream.on("data", function(chunk) { body += chunk; }); stream.on("end", function() { body = converter.convert(body).toString(); // FIXME: body にはヘッダ部も含まれているため RFC822 に則ってちゃんとパースする? if (/ワンタイムキー[ ]*[::][ ]*([a-zA-Z0-9]+)/.test(body)) { var otKey = RegExp.$1; console.log('ワンタイムキーを本文から取得成功:' + otKey); deferred.fulfill(otKey); } else { console.log('ワンタイムキーを本文から取得失敗'); deferred.reject(); } });
- nodejs-guidelines/windows-environment.md at master · Microsoft/nodejs-guidelines · GitHub
- 上にリンクいっぱい書いたけど、結局公式のこのガイド通りにやったらいけた。
以下は init.cmd の Node 関連の内容。
rem # Node set PATH=C:\Program Files\nodejs;%USERPROFILE%\AppData\Roaming\npm;%PATH% rem # ================ Prerequisites for Node ================ rem # https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md rem # Visual Studio rem # set PATH=C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;%PATH% set PATH=C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;%USERPROFILE%\.dnx\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;%PATH% rem # Python 2.7 set PATH=C:\Python27;C:\Python27\Scripts;%PATH% rem # ================ Prerequisites for Node ================
browser.wait()
- How to use browser.wait() to wait for URL to change? · Issue #610 · angular/protractor · GitHub
- Protractor - end to end testing for AngularJS
- browser.wait() に Promise を返す関数を渡すと resolve() されるまで待ってくれる (Promise をそのまま渡してもいいのかな?)。
- Protractor blocking wait | Emmett Pickerel's
- protractor/toc.md at master · angular/protractor · GitHub
- Protractor - end to end testing for AngularJS, protractor/timeouts.md at master · angular/protractor · GitHub
- Jasmine, WebDriver, Protractor それぞれにタイムアウトがある
- Protractor - end to end testing for AngularJS
If your page does manual bootstrap Protractor will not be able to load your page using browser.get. Instead, use the base webdriver instance - browser.driver.get. This means that Protractor does not know when your page is fully loaded, and you may need to add a wait statement to make sure your tests avoid race conditions.
(2段階認証を使っている場合) アプリパスワードを生成
2段階認証を使っている場合はパスワードをそのまま入力したのではログインできない。 アプリパスワードというアプリ固有のパスワードを発行する必要がある。
node.js - Authentication issue with node imap and node mail lister - Stack Overflow
https://support.google.com/accounts/answer/6010255
この設定は、2 段階認証プロセスが有効になっているアカウントでは利用できません。こうしたアカウントで安全性の低いアプリにアクセスするには、アプリケーション固有のパスワードが必要です。 ヘルプ
発行されたパスワードをそのままアカウントのパスワードとして渡せば認証される。