Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【Node.js】nodebrewでNode.jsを管理する

nodebrewとは

github.com

nodebrewは、Node.jsのバージョンを管理するためのツールです。
nodebrewを使うことで、一つの環境に複数のバージョンのNode.jsを導入することができます。
Web開発でNode.jsが頻繁に使われる昨今、特にnpmのバージョン互換性が問題となる場合が多いです。そんな中で、複数のプロジェクト用に別々のパッケージを保持したNodeを管理できたり、Nodeのバージョンを即座に切り替えたりできるのは大きなメリットだと思います。

自分も長らく単一のNode.jsを惰性で運用していましたが、今回はきちんとnodebrewを導入し、その中でNode.jsを使っていきたいと思います。

(ちなみに作成していらっしゃる方はCookpadのエンジニアの方のようです、すごい)

Usage

nodebrewをインストール

環境は Mac OS X High Sierra 10.13.5 です。

はじめに、公式ドキュメントの通りcurlでnodebrewをダウンロードします。

$ curl -L git.io/nodebrew | perl - setup

その後出力に従いパスを通します。
お使いのshellのprofile、またはrcファイル等に下記行を追加してください。
例えばbashを使っている場合、~/.bash_profileに下記行を追加して保存します。

export PATH=$HOME/.nodebrew/current/bin:$PATH

お使いのshellが不明な方は、下記行をターミナルで実行してみてください。

$ echo $SHELL

ターミナル(shell)を再起動したらOKです。

Node.jsをインストール

インストール可能なNode.jsのバージョン一覧をnodebrew ls-remoteで見ることができます。

$ nodebrew ls-remote

候補がたくさん出てくると思いますが、試しに2018/06/14時点での推奨版であるv8.11.3を導入してみましょう。
nodebrew install-binaryでインストールします。

$ nodebrew install-binary v8.11.3

インストールしたNode一覧はnodebrew lsまたはnodebrew listで見ることができます。

$ nodebrew ls
v8.11.3

current: none

current:none、すなわち現在利用しているNodeがなにもない状態となっています。
Nodeを使える状態にしてみましょう!
nodebrew useで指定したverのNodeを利用可能な状態にします。

$ nodebrew use v8.11.3
use v8.11.3

これでver8.11.3のNode.jsが使えるようになりました!

ちなみに特定のNode.jsをアンインストールしたいときのコマンドは下記になります。

$ nodebrew uninstall [Node.jsのバージョン]

参考

github.com

qiita.com

【Slack】Slack Helperを抹消してPCに平和をもたらす

Slackのデスクトップアプリは重い

f:id:rennnosukesann:20180613214821p:plain

皆さんは、Slackのデスクトップアプリを起動してPC全体が「重い!」と感じたことはないでしょうか?

ただ一言メッセージを投稿したいだけなのに、Slackの起動も遅いし、ブラウザもスクロールできなくなるし、散々です。
なぜこんなにも重くなるのでしょうか?


ご存知の方もいるかも知れませんが、実はSlackのデスクトップアプリを起動すると、ログインしているワークスペースに存在するチャネルの数だけSlack Helperと呼ばれるプロセスが起動されます。

このプロセスが厄介で、1つあたり酷いときで約300MB(非圧縮)ほどメモリを食ってます

f:id:rennnosukesann:20180613215500p:plain

Slackのデスクトップアプリを起動すると重くなる理由はここにあります。 チャネルが1,2個しかないのであれば問題ありませんが、社内用Slackワークスペースともなるとチャネルが有に10を超えることもあるので、最悪メモリが2~3Gほど食われていることもあります。

実際、Slackデスクトップアプリを開いているときと開いていないときではPCのパフォーマンスがだいぶ違いました(社用PCのメモリが8GBしかないのも辛いところです。。。)。

代替案

...身も蓋もありませんが、Slackデスクトップアプリを閉じて素直にブラウザ版を使ってしまいましょう。
アプリを終了することで、Slack Helperプロセスも終了します。

f:id:rennnosukesann:20180613215540p:plain

Chromeなどタブごとにプロセスを生成するブラウザを使うとメモリをそこそこ食ってしまうのですが、不要なチャンネルのウィンドウはこまめに消してしまえばSlack Helperほどでもないです(圧縮無視で500MBくらい?)。

f:id:rennnosukesann:20180613215753p:plain

もしくはSlack用と割り切って、機能度外視で軽量なブラウザを導入するのもありだと思います!

vivaldi.com

www.egrath.net

また、もしブラウザSlackをデスクトップアプリライクに使いたければ、Fluidでアプリ化してみても良いでしょう。

fluidapp.com

メモリに余裕のない方は一度ブラウザ版のSlackに乗り換えてみてはいかがでしょうか?

追記(2018/06/14)

vivaldyを試してみたのですが、そこまでメモリ使用量は変わりませんでした。
残念。。。

f:id:rennnosukesann:20180614074943p:plain

【React Native】Camera UIコンポーネントで撮影した写真を保存する

React Native + Expoを使ったカメラ機能実装を前回の記事で行いました。

rennnosukesann.hatenablog.com

今回は撮影した写真を端末に保存する処理を追加します。

Source

端末のフォルダに写真を保存するには、CameraRoll.saveToCameraRoll()を利用します。

import { CameraRoll } from 'react-native';
...
let uri = [写真のURI];
CameraRoll.saveToCameraRoll(uri);

以下、カメラを撮影してから写真を保存するまでのコードです。
カメラを撮影してプレビュー後、三秒後にプレビューを閉じて写真を保存します。

import React from 'react';
import { Text, View, Button, Modal, Image, CameraRoll } from 'react-native';
import { Camera, Permissions } from 'expo';

export default class CameraView extends React.Component {
  state = {
    hasCameraPermission: null,
    type: Camera.Constants.Type.back,
    photo: null
  };

  async componentWillMount() {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    this.setState({ hasCameraPermission: status === 'granted' });
  }

  setPhoto(photo) {
    this.setState({ photo: photo });
  }

  render() {
    const { hasCameraPermission } = this.state;
    if (hasCameraPermission === null) {
      return <View />;
    } else if (hasCameraPermission === false) {
      return <Text>No acess to camera</Text>;
    } else {
      return (
        <View style={{ flex: 1 }}>
          <Camera style={{ flex: 1 }} type={this.state.type} ref={cam => { this.camera = cam; }} />
          <Button style={{ flex: 1 }} title="snap!" onPress={photo => {
            if (this.camera) {
              this.camera.takePictureAsync().then(photo => {
                // カメラオブジェクト取得
                this.setPhoto(photo);
                // 三秒後にモーダルを閉じる
                setTimeout(() => {
                  this.setPhoto(null);
                  // 写真を保存
                  CameraRoll.saveToCameraRoll(photo.uri);
                }, 3000);
              });
            }
          }} />
          <Modal
            animationType="slide"
            visible={this.state.photo !== null}
            onRequestClose={() => {
              alert('Modal has been closed.');
            }}>
            <Image style={{ width: "100%", height: "100%" }}
              source={{ uri: this.state.photo === null ? "" : this.state.photo.uri }}/>
          </Modal>
        </View>
      );
    }
  }

}

Demo

f:id:rennnosukesann:20180611222402g:plain

参考

facebook.github.io

【React Native】アプリ内でカメラロールを開く

前回の記事ではネイティブのカメラ機能を使ったので、今度はカメラロール機能を実装してみました。

rennnosukesann.hatenablog.com

Usage

環境は Mac OS X High Sierra / iOS 11(iPhone X)です。

カメラロール機能はreact-nativeパッケージで利用できる他、Expoパッケージからも利用できるため、
create-react-native-appでプロジェクトを作成するだけで準備完了となります。

$ create-react-native-app react-native-image-picker-app
$ cd react-native-image-picker-app

create-react-native-appについてはこちら。

rennnosukesann.hatenablog.com

Source

React Nativeでカメラロールを呼び出すためには、Expo.ImagePicker.launchImageLibraryAsync()を利用します。

await () => {
  let result = await ImagePicker.launchImageLibraryAsync();
}

またImagePickerをネイティブ端末上で利用するために、カメラロールに対するPermission許可ダイアログを出現させる処理を追加します。

await () => {
  const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
}

以下、サンプルコードです。
画面中央のボタンを押すとカメラロールが開き、写真を選択すると画面上にプレビューが表示されます。

App.js
import React from 'react';
import { StyleSheet, View, Button, Image } from 'react-native';
import { ImagePicker, Permissions } from 'expo';

export default class App extends React.Component {

  state = {
    hasCameraRollPermission: null,
    photo: null
  };

  async componentWillMount() {
    // カメラロールに対するPermissionを許可
    const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
    this.setState({ hasCameraRollPermission: status === 'granted' });
  }


  render() {
    let { hasCameraRollPermission, photo } = this.state;
    console.log(this.state);
    return (
      <View style={styles.container}>
        {hasCameraRollPermission && photo && <Image style={styles.image} source={{ uri: photo.uri }} />}
        <Button style={styles.button} title={"open Image Picker."} onPress={async () => {
          // Image Pickerを起動する
          let result = await ImagePicker.launchImageLibraryAsync();
          console.log(result);
          this.setState({ photo: result });
        }} />
      </View>
    );
  }
}


const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  image: {
    width: '100%',
    height: '100%',
    flex: 9,
  },
  button: {
    flex:1
  }
});

Demo

f:id:rennnosukesann:20180610142919g:plain:w300

参考

ImagePicker - Expo Documentation

【React Native】React Nativeでカメラアプリを実装する

ここ最近、React Nativeを使ってネイティブアプリで使いそうな機能を一通り試すことが多くなってきました。

rennnosukesann.hatenablog.com

rennnosukesann.hatenablog.com

今回もネイティブ実装でよく使われる機能であるカメラ機能を試してみました。

実行環境は Mac OS X High Sierra / iOS 11(iPhone X)です。

Usage

create-react-native-appでプロジェクトを作成します。
ExpoのカメラUIコンポーネントを利用するので、追加でインストールするパッケージはありません。

$ npm install react-native-camera-app
$ cd react-native-camera-app

create-react-native-appについては下記記事参照。

rennnosukesann.hatenablog.com

Source

カメラビューを表示し、撮影も行えるアプリです(保存はつけてません)。 カメラ撮影後数秒間撮影した写真がモーダル上で表示されます。

CameraView.js
import React from 'react';
import { Text, View, Button, Modal, Image } from 'react-native';
import { Camera, Permissions } from 'expo';
import { timeout } from 'rxjs/operator/timeout';

export default class CameraView extends React.Component {
  state = {
    hasCameraPermission: null,
    type: Camera.Constants.Type.back,
    photo: null
  };

  async componentWillMount() {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    this.setState({ hasCameraPermission: status === 'granted' });
  }

  setPhoto(photo) {
    this.setState({ photo: photo });
  }

  render() {
    const { hasCameraPermission } = this.state;
    if (hasCameraPermission === null) {
      return <View />;
    } else if (hasCameraPermission === false) {
      return <Text>No acess to camera</Text>;
    } else {
      return (
        <View style={{ flex: 1 }}>
          <Camera style={{ flex: 1 }} type={this.state.type} ref={cam => { this.camera = cam; }} />
          <Button style={{ flex: 1 }} title="snap!" onPress={photo => {
            if (this.camera) {
              this.camera.takePictureAsync().then(photo => {
                // カメラオブジェクト取得
                this.setPhoto(photo);
                // 三秒後にモーダルを閉じる
                setTimeout(() => {
                  this.setPhoto(null);
                }, 3000);
              });
            }
          }} />
          <Modal
            animationType="slide"
            visible={this.state.photo !== null}
            onRequestClose={() => {
              alert('Modal has been closed.');
            }}>
            <Image style={{ width: "100%", height: "100%" }}
              source={{ uri: this.state.photo === null ? "" : this.state.photo.uri }} />
          </Modal>
        </View>
      );
    }
  }
}
App.js
import React from 'react';
import CameraView from './CameraView';

export default class App extends React.Component {
  render() {
    return (
      <CameraView />
    );
  }
}

Demo

f:id:rennnosukesann:20180609144721g:plain:w300

参考

Camera - Expo Documentation

【React Native】Googleマップを利用する(Android)

rennnosukesann.hatenablog.com

前回の記事では、iOS上でGoogle MapアプリをReact Native+Expoで作成しました。
今回はAndroid verを作成してみたいと思います。

Usage

環境構築環境は Mac OS X High Sierraです。

プロジェクトの作成

例によってcreate-react-native-appでプロジェクトを作成します。

$ create-react-native-app react-native-map-app-android 
$ cd react-native-map-app-android 

create-react-native-appがインストールされていない人は下記記事を参考にインストールしてください。

rennnosukesann.hatenablog.com

APIキーの取得

次に、Google Map APIAPIキーを取得します。

Googleアカウントを登録→ログイン後、Google Cloud Platformのページへ移動します。

Google Cloud Platform移動後、右上の「コンソール」ボタンを押します。

f:id:rennnosukesann:20180607223743p:plain

コンソール画面に移動した後、左上のハンバーガーメニューを押すと「APIとサービス」のメニューがあるのでクリック。

f:id:rennnosukesann:20180607223453p:plain

遷移後再度ハンバーガーメニューを開き「ライブラリ」をクリック。

f:id:rennnosukesann:20180607223558p:plain

APIサービス検索画面に遷移するので検索欄に「Google Map」と入力し、検索結果から「Maps SDK for Android」をクリックします。
クリックするとアプリのダッシュボード画面に移動するので「認証情報」タブをクリックしてください。

f:id:rennnosukesann:20180608222628p:plain

すると利用中のAPIキー一覧が表示されます。これでAPIキー取得完了です。

f:id:rennnosukesann:20180607223646p:plain

APIキーの設定

APIキーを取得後、プロジェクト内のapp.jsonを以下のように書き換えます。

{
  "expo": {
    "sdkVersion": "27.0.0"
  },
  "android": {
    "config": {
      "googleMaps.apiKey": "[API Key]"
    }
  }
}

前回の記事で取得したiOSGoogle Map APIキーを並列して設定することもできます。

{
  "expo": {
    "sdkVersion": "27.0.0"
  },
  "android": {
    "config": {
      "googleMaps.apiKey": "[Android API Key]"
    }
  }
 "ios": {
    "config": {
      "googleMapsApiKey": "[iOS API Key]"
    }
  }
}

APIキーの設定はこれで完了です。

Source

ソースはiOSと共通のものを使用できます。

Map.js
import React from 'react';
import { MapView } from 'expo';

export default class Map extends React.Component {
  render() {
    return (
      <MapView
        style={{ flex: 1 }}
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0.0922,
          longitudeDelta: 0.0421,
        }}
      />
    );
  }
}
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Map from './Map';

export default class App extends React.Component {
  render() {
    return (
      <Map />
    );
  }
}

Demo

f:id:rennnosukesann:20180608225213p:plain:w300

AndroidでもGoogle Mapを表示できました。
クロスプラットフォームの真髄ここに極まれり、ですね。

参考

MapView - Expo Documentation