React.js + firebaseでリアルタイムチャットアプリ作った。

f:id:anoChick:20161211183939p:plain

https://nekojima.anochick.com

でーきた。 特徴としては↓

  • リアルタイムチャット(勝手に更新が反映される)
  • チャンネルの作成が自由にできる。
  • 無限にふぁぼれる
  • 完全匿名

です。

f:id:anoChick:20161211190537g:plain

ふぁぼり放題です。

作り方の解説

anoninoni.hateblo.jp

こっちでも雑に書いてはいたんですが、もうちょっと丁寧に記述します。

react-boilerplateを使う。

github.com 開発環境を整えるのが大変なので、ボイラープレートを使いました。 詳細はリンク先みていただくとわかるかと思うんですが、

  • React.js
  • Redux
  • ES6
  • webpack
  • ServiceWorker
  • Mocha
  • enzyme
  • eslint
  • CLI

などなど、モダンなフロントエンド開発に使うものがいっぱい入ってます。

ただ、Angularと比べると非公式なものなので、結構バギー。 私の場合webpackのcss生成の記述がおかしくなってて手動で対応したりしてました。 あとeslintがめちゃめちゃ厳しいです... サンプルプログラムの時点で結構eslint:disabledの記述があったので、付き合い方が難しそう。

Material UIを使う

www.material-ui.com

UIframeworkをなににしようか悩んだんですが、やっぱり王道のMaterial-UIにしました。理由としては

  • バイルフレンドリーに書きやすい
  • 今回作るものがシンプル
  • よく使われているから安心

という感じです。

グリッドシステムも特に使わず、できるだけ純粋なMaterial-UIで書きました。

firebaseのリアルタイムDBを使う(reactfire)

これを使ってみたくてこのチャットを作り始めたみたいなところあります。 "データベース"のくせにリアルタイム更新と、プッシュ機能も持ってるのですごいです。

チャンネル内の情報なんかは

const ref = firebase.database().ref('channels/default');
this.bindAsArray(ref, 'channel');

と記述するだけで、そのコンポーネントのstate.channelにバインドされます。 バインドされた状態のstateにsetState()で変更を加えるだけで、DBに更新が走り、 バインドしている全クライアントに対してプッシュされます。 すごい!

普通にjavascriptで使う場合は

  firebase.database().ref('channels/default').on('eventname', function(snapshot) {
    var mes = $('<span>',{class:'message'}).text(snapshot.val().message);
    $('#message-box').append(mes);
  });

のような記述で動きます。

ちなみに、このリアルタイムDBはユーザ認証を用いたパーミッション機能があるのですが、 今回は認証なしで動いているため、誰でも自由に書き換えができます

一応サニタイズは行われるぽいのですが、バリデーションのかけようがないのが気になってる。

雑感(やってみて)

  • やっぱりSPAは夢があって楽しい
  • サーバ側はどうしようかもうちょっと考えたい
    • GraphQLとかちょっと手を出してみる予定
  • firebaseDB面白いけど使う場面がかなり限られる
  • とはいえfirebaseのホスティングhttpsカスタムドメイン対応しているのですごく良い!

あとServiceWorkerについてなんですが、キャッシュとしての効力が強すぎるのと、扱い方が難しそうだったのでオフにしてます。また別途勉強したい。

1日でリアルタイムチャットを作る。

作ります。後から加筆していく。

10:30 Start!

まずは大まかな要件

  • チャンネルの概念がある(slackのような)
  • 1チャンネル1000ポストまで(2chのような)
  • 匿名で投稿
  • 無限ふぁぼ( 超重要!

11:00

スーパーでネギトロを買ってネギトロ丼を食べる。 アニメを見る。 "ユーリ!!! on ICE"と"ガーリッシュナンバー"。よい。

11:30

少し細かく

firebaseのリアルタイムDB使う

  • channel_indexes:Array
    • (name):String
  • channels:Array
    • (channel):Object
      • id
      • message:Text
      • posted_at:Datetime
      • fabo:Integer こんなもんで良さそう

ルーティングとか

/chennel_name#messageID

UIコンポーネント

www.material-ui.com

これ使う。

12:00

プロジェクトを作り始める プロジェクト名はもちろんtimes-nakajima Nekojimaで。

github.com

やるぞ。 firebaseのほうのプロジェクトも作る。

DBを軽くつくっておく。

{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

ルールは一旦これでいいや。 f:id:anoChick:20161203122359p:plain

DBもこんなもんで。

frameworkもいれる

github.com

これつかう。

12:30

とりあえず何かしらが公開されている状態にする。 FirebaseのHostingを使う。 react-boilerplateでビルドした後の静的ファイルを公開する形にする想定。 でもいったんてきとーに内容書いてデプロイ。 あとはカスタムドメインを通す。

React.js Boilerplate

f:id:anoChick:20161203124717p:plain もちっと時間がかかる。

boilerplateの初期設定

$ npm run setup
$ npm run clean

これで初期化される。

あとは試しにビルドしてみる

$ npm run build

出来たけど/buildに吐き出された。 firebaseのデプロイ対象ディレクトリは/publicなので、これを変える。

{
  ...
  "hosting": {
    "public": "build"
  }
}

サイドdeployする。

f:id:anoChick:20161203125620p:plain

よしよし。 そんなこんなでSSL通ったぞ。 DNSレコード書き換えて... レコード設定ミスっていたのでもう少し時間がかかりそう。30分ぐらい?

13:00

UIframeworkと、firabaseを差し込んでいく。 <MuiThemeProvider>を全体に適応させる。 f:id:anoChick:20161203132802p:plain

はいった。

$ npm install reactfire firebase react-mixin@2 --save  

firebaseとreactfireをいれる。 react-mixinはReactコンポーネントにfirebaseリアルタイムDBとのバインディング機能をアドオンするために使う。

つながってるか確認するためのデータをRealtime Databaseに入れる。 f:id:anoChick:20161203134833p:plain

フロント側も適当な場所で

    var ref = firebase.database().ref("home_message");
    this.bindAsObject(ref, "homeMessage");

とかする。 f:id:anoChick:20161203134921p:plain ほいきた!

とかやってたらドメイン設定完了したっぽい ビルド&デプロイしよう。

React.js Boilerplate

できt.. てない!反映されない!Nannde!

14:00

デプロイで躓く わからん!反映に時間かかるのかな。 ちょっと休憩しよう。

16:00

この時点でもう結構色んな問題が起こっていることに気づいた。 少しづつ直していく。

17:45

materialUIがうまく扱えなくてどうしよう。一旦寝るという選択肢。

19:00

起きた。 github.com

寝たらすぐに解決した。よかった。

20:30

再開 チャンネル切り替えと追加の機能をつける。

だめでしたー!!!!

思いの外バグ踏んだのと、firebaseの扱いがうまくいかなかったので未完。 とりあえずURLだけ貼っておきます。

https://nekoijma.anochick.com

サーバレスな汎用スクレイパーを作った。

anoninoni.hateblo.jp

以前SERVERLESS FRAMEWORKを紹介したんですが、 今回はSERVERLESS FRAMEWORKを使ってスクレイパーを作りました。

github.com

使い方

serverless deploy --stage production

AWSにdeployして使います。

例: githubリポジトリのページからStar数を取ってくる。

/scrape?
url=https://github.com/anoChick/basicscraper&
query=.social-count.js-social-count&
datatype=int

urlで対象ページを指定し、queryで対象のhtml要素を指定します。 datatypeをつかうとトリムされたり小数点を丸めたり出来ます。

f:id:anoChick:20161128025823p:plain response:

{
  "datetime": "2016-11-27 05:56:14",
  "url": "https://github.com/anoChick/basicscraper",
  "result": {
    "query": ".social-count.js-social-count",
    "value": 0
  }
}

同一サイトに対して複数の要素を取得したい場合は、パラメータを配列にして渡すことも出来ます。 また、labelをつけることも可能です。

f:id:anoChick:20161128025747p:plain response:

{
  "datetime": "2016-11-27 05:54:51",
  "url": "https://github.com/anoChick/basicscraper",
  "result": [
    {
      "index": "0",
      "query": ".social-count.js-social-count",
      "value": 0,
      "label": "Star数"
    },
    {
      "index": "1",
      "query": ".author",
      "value": "anoChick",
      "label": "Author"
    }
  ]
}



## 次にやること
```/scrape```でスクレイピングの即時実行する機能が提供できました。

```/schedule```などでジョブのスケジューリングなんかもやれたら良いなって思ってます。

ご利用は自己責任でお願いします。

パーリンノイズでそれっぽいダミーチャートを作る

「折れ線グラフとかのダミーデータをどうやって用意しよう」と思い、 パーリンノイズを使ったらそれっぽくなったので紹介します。

パーリンノイズってなんだ

主にコンピュータグラフィックスの世界でよく使われるんですが、すごく直感的な説明をすると自然に連続な乱数です。 ゲームだとテクスチャや、地形の凹凸をそれっぽくするときなんかによく使われます。 f:id:anoChick:20161112115538p:plain 3次元のパーリンノイズが↑のようなものです。 ボコボコ感がそれっぽくなってると思います。このうち高さ情報を白黒に変換すると↓ のようになります。 f:id:anoChick:20161112115239p:plain

f:id:anoChick:20161112115243p:plain

このような、火山地帯っぽいテクスチャが自動生成出来るというわけです。

ラインチャートのデータとして使う

↑で紹介したのは3次元、2次元のパーリンノイズですが、さらに1次元落としたものを実際に作ってみようと思います。

gem install perlin_noise

require 'perlin_noise'
n1d = Perlin::Noise.new 1
result = []
0.step(10, 0.3) do |x|
   result.push((n1d[x] * 100).floor)
end
p result
$ ruby pn.rb
=> [50, 31, 26, 44, 61, 75, 61, 44, 26, 31, 50, 56, 45, 45, 61, 75, 61, 45, 45, 56, 50, 43, 54, 54, 42, 50, 57, 45, 45, 56, 50, 43, 54, 54]

f:id:anoChick:20161112115131p:plain

単純に1次元で生成したものがこちら。 そこまで不規則的ではなくてイマイチ。 次は2次元で生成したものをスライシング(ある面で切り取って次数を下げる)するという方法でやってみます。

require 'perlin_noise'
n2d = Perlin::Noise.new 2
result = []
0.step(10, 0.3) do |x|
   result.push((n2d[0,x] * 100).floor)
end
p result
$ ruby pn.rb
=> [50, 37, 41, 49, 51, 61, 58, 45, 33, 40, 50, 51, 46, 47, 54, 56, 50, 50, 51, 51, 50, 47, 45, 48, 53, 56, 52, 48, 37, 36, 50, 67, 72, 55]

f:id:anoChick:20161112115124p:plain

それっぽくなりました。 パーリンノイズのアルゴリズムによっては、一次元でそれっぽく生成出来るらしいのですが、使用したgemの実装がシンプルだったことと、比較グラフも合わせて表示させることを考えるとこの方が良さそうだったので、二次元をスライシングするやりかたを採用しました。というのも、

require 'perlin_noise'
n2d = Perlin::Noise.new 2,seed:12345
result1 = []
result2 = []
0.step(10, 0.3) do |x|
  result1.push((n2d[0,x] * 100).floor)
  result2.push((n2d[0.2,x] * 100).floor)
end
p result1,result2
$ ruby pn.rb
=> [50, 56, 46, 45, 57, 52, 45, 53, 59, 53, 50, 47, 42, 47, 57, 67, 58, 46, 44, 51, 50, 45, 49, 51, 48, 53, 54, 47, 39, 43, 50, 48, 42, 46]
[51, 56, 45, 43, 53, 48, 40, 48, 58, 60, 59, 53, 39, 38, 48, 62, 60, 50, 50, 61, 60, 55, 57, 58, 55, 54, 49, 40, 31, 32, 39, 40, 36, 41]

f:id:anoChick:20161112115116p:plain

このように、近くにあるデータも併せてとることができます。 いい感じに比較グラフっぽくなってますよね。

パーリンノイズのしくみとか

私にはちょっと説明できそうにないので参考文献だけ貼っておきます。

引用・参考文献

パーリンノイズを理解する http://postd.cc/understanding-perlin-noise/

ランダム地形生成 Part1~パーリンノイズ http://qiita.com/y_li/items/e058bfc2ff8051008679

ランダム地形生成 Part2~フラクタルブラウン運動 http://qiita.com/y_li/items/290754b9c3ba18e9fb2b

junegunn/perlin_noise https://github.com/junegunn/perlin_noise

React.jsにthree.jsぶっこむぞ

「背景でWebGLがぬるぬる動くWebサイト作りたいぞ!」

って思ったときに、 ページ遷移する度に背景再描画されるのすっごくイケてなさみがある。 結局SPAっぽく使うことになるんだとおもった。 ということでReact.jsでthree.jsを使うパッケージつかう。

github.com

import React from 'react';
import React3 from 'react-three-renderer';
import THREE from 'three';

export default class HomePage extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.cameraPosition = new THREE.Vector3(0, 0, 100);

    this.state = {
      cubeRotation: new THREE.Euler(),
    };

    this._onAnimate = () => {
      this.setState({
        cubeRotation: new THREE.Euler(
          this.state.cubeRotation.x + 0.004,
          this.state.cubeRotation.y + 0.002,
          0
        ),
      });
    };
  }
  render() {
    const width = window.innerWidth;
    const height = window.innerHeight;

    return (<React3
      mainCamera="camera"
      width={width}
      height={height}

      onAnimate={this._onAnimate}
    >
      <scene>
        <perspectiveCamera
          name="camera"
          fov={75}
          aspect={width / height}
          near={0.1}
          far={1000}

          position={this.cameraPosition}
        />
        <mesh
          rotation={this.state.cubeRotation}
        >
          <torusGeometry
            radius = {50}
            tube = {10}
            radialSegments = {10}
            tubularSegments = {20}
            arc = {Math.PI * 2}
          />
          <meshNormalMaterial
            wireframe = {true}
          />
        </mesh>
      </scene>
    </React3>);
  }
}

f:id:anoChick:20161101054634g:plain

できたできた。 ちなみに私はReactアプリケーションを作るときはとりあえず

React.js Boilerplate

これ使ってます。サクッと環境が整うので良い。

SERVERLESS FRAMEWORKが最高すぎる

最近SERVERLESS FRAMEWORKってのがリリースされました(v1) serverless.com

これなに

プログラムを書いてデプロイすると AWS LambdaとAPIGateway環境にデプロイされる。 ルーティングもYAMLで記述ができる。 基本的にはこれだけ。

動作イメージ

module.exports.piyo = (event, context, callback) => {
    const response = {
      statusCode: 200,
      body: JSON.stringify({
        message: 'おっけー!'
      }),
    };
    callback(null, response);
}
service: servicename
provider:
  name: aws
  runtime: nodejs4.3
  region: ap-northeast-1
functions:
  piyo:
    handler: handler.piyo
    events:
     - http:
         path: piyo
         method: get

これでデプロイすると、

$ serverless deploy

Serverless: Packaging service…
Serverless: Merged stage variables into ApiGateway Deployment
Serverless: Removing old service versions…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
..............
Serverless: Stack update finished…

Service Information
service: servicename
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  POST - https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/piyo
functions:
  servicename-dev-piyo: arn:aws:lambda:ap-northeast-1:000000:function:ms-servicename-dev-piyo

https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/piyo できた!(さんぷるなので動いてないです)

実用する

SERVERLESS FRAMEWORK本体の責務としては「デプロイするまで」というイメージです。 なので単体だと開発環境は全く用意されておらず、 開発環境を用意するには別途プラグインを入れていく必要があります。 中でも必須っぽいプラグインがいくつかあるので紹介すると、

serverless-run-function-plugin

書いたfunctionをローカルで実行する為のプラグイン

$ serverless run -f piyo

と叩けば実行される。

serverless-offline

Lambda環境をエミュレートしてくれて、ローカルでサーバを起動するプラグイン

$ serverless offline --port 3000

で立ち上がる。あとはhttpリクエスト送るだけ。

serverless-plugin-stage-variables

ステージ変数を定義出来る

(中略)
stageVariables:
  google_sheet_id: ===
  aws_access_key_id: ===
  aws_secret_access_key: ===

使ってみて

  • サーバレスのシステムがフレームワークに乗っかる形で書けるのでチームに共有しやすそう
  • ローカル環境で動作確認できるのがすごく良い
  • dev/prod環境わけられたり、とても実用的

結構本格的に使っていきそう。

webサイトを作った

Anomaly Animal

なにもないけど。

  1. Unityでそれっぽいグラフィカルな何かを作る
  2. WebGLで吐き出す
  3. 背景に組み込む
  4. S3でホスティングする
  5. 完成

です。f:id:anoChick:20161030005042p:plain

何に使うわけでもないwebサイトは好きにできるので実験的なことが出来て良い。 いろいろ追加していくかも。