Webエンジニアの備忘録

およそ自己の作業メモとして残しております。

2017年を振り返る

自身の来年やるべきことを模索するためにメモを残しています。

所感

昨年にクライアントサイドに転向し、主にブラウザ対応に重きをおく開発をおこなっていました。サーバーサイドでもRailsなど新しい経験もしましたが、Javascriptの世界は作法も大きく異なり、エンジニアリングの世界が格段に広がりました。

やったこと

Reactjs

クライアントサイドの学習基盤としてReactjs+Reduxに触れられたのは非常に良かったです。APIを通してデータを取得するフローや、特に非同期の考え方はクライアントの動きを深く知ることができました。 コンポーネントのライフサイクルはネイティブにも通じる考え方で、今後はiOSAndroidの開発にも触れてみたいと思っています。

Ruby / Rails

API開発とRSpecコーディングに触れる機会は多かったです。bundleもトラブルが多かったので、そこそこ多く調べましたね…特にVagrantではnfsストレージ上でbundleがうまく動かないトラブルもありました。CコンパイルMacローカルのコアを認識しつつVagrantCentOSで動くみたいな(^_^;) kaminariがCで書かれているのも知って驚きました。

CSS

Animationやレイアウトについて、実作業ベースで苦しみました(笑) 特にレイアウトについては縦方向でのセンタリングの難しさや、ブラウザによって出る描画差異なども実践なくしてはわからないものが多かったです。 flexやjustify-contentなどの魔法の単語を知って作業がかなり楽になりました。translateとtop、bottomを組み合わせて縦位置をど真ん中に合わせるなども今は重宝しています。(これはいずれブログにまとめたい…)

WebRTC

ブラウザができることが大きく広がっていることを知りました。 以下のコード、htmlにて保存してChromeで開いてみてください。カメラが起動します(笑)

<html>
<body>
  <video id="video" width="640" />
</body>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script>
  navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 } },
    (stream) => {
      const video = document.querySelector('#video');
      video.src = URL.createObjectURL(stream);
      video.onloadedmetadata = (e => video.play());
    },
    err => console.log("The following error occurred: " + err.name),
  );
</script>
</html>

ローカルHTMLでもカメラがいとも簡単に起動する、それを保存したりもできる、ネイティブアプリとブラウザとの違いはどんどん少なくなっているようです。

逆に言えば、Webエンジニアに求められることもどんどん増えているわけで、今後も精進が必要そうです(^_^;)

Xcode

業務都合で自社アプリのビルド程度まではおこなうようになりました。開発はまだ触れていませんが、エラーコンソールとBuild Settingくらいは読むようにはなっていました。ビルドを行うようになって意識が変わったことのひとつに、MacOSXcodeのバージョンは比較的早く最新のものを追うようになりました。

モヒカンSlack

kotakanbeさんが運用されているSlackチームですが、こちらの利用で情報収集の効率が格段に良くなりました。 qiita.com

冬眠気味だったTwitterアカウントも改めて情報収集に活かせるようフォローを増やしたり手をかけましたが、情報収集方法も年々進化していますね。

TILをやってみたが…

syossan.hateblo.jp

Today I learndでTILですね。1週間も持ちませんでしたが(^_^;)

ブログよりも細かい粒度で身につけたことを残したいと思いはじめましたが、情報が少なすぎると一生懸命ネタを増やそうとしてしまうのがブログやってるときの癖で… 調べたことを一個単位で残していくのは非常に効率的だと思うので、今後はもう少しマメに書いていきたいです!

GitHub - ttaniguchi/til: Today I learned

まとめ

今年はわたしにとってはクライアント元年であり、非常に楽しい試みを多く経験できた年だったと思います。ただ、環境的には同じ技術軸で作業をする人間が身近におらず、ガラパゴス懸念を感じながらLintだ勉強会だと他者の技術や標準化にやや枯渇した時期でした。 来年は身近なメンバーに技術普及をすることで切磋琢磨できる環境をつくることにも尽力したいですね。

それと、やはりネイティブには触れていきたいです。Reactjsかvuejsか、みたいな時期もありました。それでもReactjsを選んだのは「アプリも作れるみたいだし…」というとろこもあったわけで。 そこに回帰して、来年はプライベートでReact Nativeをはじめてみようかと思っています。(1年くらい前にHwllo Worldくらいまではやった気がします)

今年も残すところ僅かですが、良いお年を! Happy Hacking

Github二段階認証後はID + tokenを使う

f:id:tak_taniguchi:20171207165716p:plain

https://github.com/settings/security

困ったこと

会社のルールでGithubで二段階認証(SMS)をおこなっていたのですが、コンソールでID+パスワードを求められた際に認証不足で処理が通らなくなります。

こんな場合です。
$ git clone https://github.com/ttaniguchi/private-repository.git
Cloning into 'private-repository'...
Username for 'https://github.com': ttaniguchi
Password for 'https://ttaniguchi@github.com':

対処方法

パスワードの代わりにPersonal access tokensを作れば大丈夫でした!

https://github.com/settings/tokens

  • 先ほどのパスワードの代わりに発行したトークンを利用すればOKです。
  • リポジトリの取り込み操作だけであれば、このあたりのチェックだけでおおよそOKです。

f:id:tak_taniguchi:20171207170258p:plain

これを知らないと二段階認証への移行に結構足踏みしそう… メモがてら共有します。

React+Redux環境でreact-router@v4への移行をおこなう

React Routerの4系がリリースされて久しいですが、相変わらず対応ができていませんでした。 ようやく重い腰を上げてアップデートしたのですが、噂に違わず障壁が多い移行だったため、メモがてら記録を残しておきます。

v3 → v4で何が変わったか

簡潔に言うと、とても良くなりました。

  • ルーティングコンポーネントのネストについての制約(縛り)が解消された。
  • contextを利用せずにパラメータを送れるようになった。

現在の状況

  • React+Reduxで構築されたサービスです。
  • react-router3系とreact-router-reduxを併せて利用していました。
  • 画面遷移はhistroyに対するrouter.push()がメインです。
<button onClick={context.router.push()} />

移行作業

パッケージの更新

今回更新したパッケージは以下の通りです。

react-router@3.0.0 → react-router-dom@4.2.2
  • DOMのみパッケージ分割されていましたが、こちらで事足りました。
react-router-redux@4.0.8 → @next
  • 4系への対応は@nextバージョンを利用するように、とのことでした。
  • それ以前のバージョンは2/3系用となっています。
query-string
  • クエリパラメータ取得に使います。

ルーティング部分

新たなルーティングがこちらです。

import React from 'react';
import { render } from 'react-dom';
import { HashRouter, Route, Switch } from 'react-router-dom';
import { createHashHistory } from 'history';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';

  :

const history = createHashHistory();
const store = configureStore(history);

render(
  (
    <Provider store={store}>
      <ConnectedRouter history={history}>
        <HashRouter>
          <NaviComponent>
            <Switch>
              <Route exact path="/" component={Top} />
              <Route path="/contents/:id" component={Contents} />
              <Route path="/contents" component={Contents} />
            </Switch>
          </NaviComponent>
        </HashRouter>
      </ConnectedRouter>
    </Provider>
  ),
  document.getElementById('app'),
);
  • ConnectedRouterがreduxと連結したhistoryを注入してくれます。
  • Hashによるルーティングを利用しているのでHashRouterで括っています。
  • NaviComponentのように共通のコンポーネントはそのまま組み込めます。
  • Switchで括っておくと、マッチするRouteが複数あっても最初にマッチしたコンポーネントしか表示しません。
  • pathは"/contents(/:id)"と書けなくなってしまったため、2行に分けて記載しています。
    • もっと良い方法があればコメントなどください。
  • URLパラメータの:id引き渡しについても以下の通り変更がありました。
- props.params.id
+ props.match.params.id

アクションの置き換え

  • push / replace / go / goBack等のファンクションはcontext.router内から取得していましたが、withRouter()メソッドの登場によって、Route直下にない孫コンポーネントにもprops経由でこれを送ることができるようになりました。
import { withRouter } from 'react-router-dom';

  :

export default withRouter(Component);
  • また、相対パスが利用できるようになったことも注意が必要でした。
- this.context.router.push('contents');
+ this.props.history.push('/contents');

contextを廃止

Context - React

公式ドキュメントにもあるように、contextは非推奨仕様でした。そもそもReactRouterの実装のために用いていたので、これを機にすべて撤廃することにしました。

クエリパラメータの対処

  • クエリが元のsearchパラメータからしか取得できなくなっていました。
  • query-stringを利用することで、key/valueに分割して値を返してくれます。
const search = props.location.search;
// ='?key1=hoge&key2=fuga'

const params = perse(search);
// = {
//   key1: 'hoge',
//   key2: 'fuga',
// }
  • また、Switchからも見て取れるように、パスでのみコンポーネントが切り替わるためクエリの変化はpropsに流れてこなくなりました。
    • 上の対応として、props内のhistory.listen()メソッドを利用します。こちらはクエリを含め、historyの更新に対してコールバックをおこなってくれます。

所感

主に詰まったところしか記載していないのですが、大概の情報はこちらのページで仕入れる事ができました。 reacttraining.com

以下のサイトにもお世話になりました。 qiita.com

今回のredux連携のようなケースは単体でのパッケージ導入と異なり、公式ドキュメントだけだとなかなか追いづらいところもありましたが、開発者側も周辺パッケージを意識しているようで、今回のアップデートはかなりreduxに寄せたものだったとも思いました。

今後もナレッジについてはメモがてら残していこうと思います。

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門

Reactで絶対パスによるimportを使いたい

コンポーネントが深くなるとパスが読みづらい

ecmascriptでは外部ファイルを取り込む際に相対パスを指定します。

ただ、コンポーネントの階層が深くなるに連れて相対パスがわかりづらくなるので、絶対パスが使えればと思うケースも出てきます。

 -import Error from './../containers/ErrorContainer';
 -import Loader from './../containers/LoaderContainer';
 -import locale from './../libs/Locale';
 -import DevTools from './../DevTools';

  ↓ ↓ ↓

 +import Error from 'jsx/containers/ErrorContainer';
 +import Loader from 'jsx/containers/LoaderContainer';
 +import locale from 'jsx/libs/Locale';
 +import DevTools from 'jsx/DevTools';

babel-plugin-module-resolverを使う

Babelifyの際にパスを解決できれば絶対パスも利用可能です。 それを実現するライブラリがこちら。

www.npmjs.com

利用方法

npmでインストール

npm install -S babel-plugin-module-resolver

/.babelrcファイルに設定

{
  "plugins": [
    "transform-inline-environment-variables",
    ["module-resolver", {
      "alias": {
        "jsx": "./jsx", // componentを置くディレクトリを絶対パス化
      }
    }]
  ]
}

aliasとしてディレクトリ名を登録できるので、相対パスもそのまま利用できます。 npmパッケージと被らない名称であれば複数aliasを増やせます。

eslintも通したい

Babelで解決させた後はeslintでもパスを許容するよう修正します。 eslint-import-resolver-babel-moduleを利用するのが便利です。

www.npmjs.com

利用方法

npmでインストール

npm install -S eslint-import-resolver-babel-module

/. eslintrcファイルに設定

{
    "settings": {
        "import/resolver": {
            "babel-module": {}
        }
    }
}

これで絶対パスもlintが通るようになります。

ただ、VSCodeなどでタグジャンプなどを利用している場合、うまく動かなくなる可能性があります。こちらについては別途プラグインなどを調査中です。(見つかったら加筆します)

react-router画面遷移時のスクロール位置を操作する

前のページのスクロール位置が残る

react-routerはURLのハッシュを用いて画面遷移を表現していますが、実際のルーティングは固定だったりします。

なので、こんな気遣いをしないと遷移後に最上部にスクロールしてくれなかったりします。

  <Router onUpdate={() => window.scrollTo(0, 0)}>
    :
  </Router>

新しいプロジェクトを作成したときに、どうやってたっけ?と思ってしまったのでメモがてら残しておきます(^_^;)


ちなみに、ルーティングにパラメータを付けて、デフォルトのスクロール位置を変更させたい時はこちらのような共用モジュールを作成して対処しています。

自分の位置を返すComponent

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class Scroller extends Component {
  componentDidMount() {
    const { active, callback } = this.props;

    if (active) {
      callback(this.scroller.offsetTop);
    }
  }
  componentWillUnmount() {
    this.scroller = null;
  }
  render() {
    return (
      <div ref={e => (this.scroller = e)} />
    );
  }
}
Scroller.propTypes = {
  active: PropTypes.bool,
  callback: PropTypes.func,
};
Scroller.defaultProps = {
  active: false,
  callback: null,
};

Scrollerはスクロールさせたい位置にマーカー的に配置します。

activeの場合のみコールバックで自分の位置を返します。

使い方はこんな感じです。

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import Scroller from './Scroller';

export default class Clips extends Component {
  componentDidUpdate() {
    if (this.position) {
      window.scrollTo(0, this.position);
      this.position = null;
    }
  }
  render() {
    const { location } = this.props;

    return (
      <div>
        <Scroller
          active={location.query.content === 'content1'}
          callback={e => (this.position = e)}
        />
        <div>
          コンテンツ1
        </div>
        <Scroller
          active={location.query.content === 'content2'}
          callback={e => (this.position = e)}
        />
        <div>
          コンテンツ2
        </div>
      </div>
    );
  }
}
Clips.propTypes = {
  location: PropTypes.shape().isRequired,
};

ルーティングに併せてスクロールできるパッケージもありますね。 こちらは座標指定が必要なので、併せて利用できるかと思います。 www.npmjs.com

ES6におけるReactコンポーネントの書き換えについて

用途に併せてコンポーネントを書き分けよう

eslintに従いクラスコンポーネントを書き換えるケースがよくあるので、サンプルを貼っておきます。 たくさん書いてありますが、全て結果としては同じ内容を表現しています。

クラスコンポーネント(class)

  • 以下のようなクラスを作るのが最も万能ですが、内容に対してコードが長くなる場合があります。
import React, { Component } from 'react';

export default class App extends Component {
  render() {
    return (
      <div>Hello {this.props.name} !</div>
    );
  }
}
App.propTypes = {
  myName: React.PropTypes.string.isRequired,
};

メソッドコンポーネント(function)

  • メソッド単体でもコンポーネントとして利用できます。
  • { myName }はpropsオブジェクト下のキーを具体的に定義しています。
  • 省略していますが、第1引数propsの後に第2引数contextを受け取ることができます。
import React from 'react';

export default function App({ myName }) {
  return (
    <div>Hello {myName} !</div>
  );
}
App.propTypes = {
  myName: React.PropTypes.string.isRequired,
};

メソッドコンポーネント(const)

  • 上記をアロー関数で置き換えたものをconst値として渡しています。
  • constは直接defaultのexportに設定できませんが、以下のように書くとdefaultに設定できます。
import React from 'react';

const App = ({ myName }) => {
  return (
    <div>Hello {myName} !</div>
  );
};
App.propTypes = {
  myName: React.PropTypes.string.isRequired,
};
export default App;
  • コンポーネントの戻り値は必ずjsxなので、処理が複雑でない限りは以下のような書き方がベストです。
import React from 'react';

const App = ({ myName }) => (
  <div>Hello {myName} !</div>
);
App.propTypes = {
  myName: React.PropTypes.string.isRequired,
};
export default App;
  • 丸括弧がアロー関数の戻り値を表していたり、JSXを内包していたりとわかりづらいですが、どちらも省略可能なので、以下のようにも書けたりします。
    • ここまで省略すると若干読みづらい感じもします。
import React from 'react';

const App = ({ myName }) => <div>Hello {myName} !</div>;
App.propTypes = {
  myName: React.PropTypes.string.isRequired,
};
export default App;

Mac新OS「Sierra」で内蔵カメラが動かなくなった件

今回「Sierra」へのアップデートでは早くもいくつか辛酸を味わっていますが、FaceTimeカメラについてはようやく解決したのでメモを残しておきます。

以下、やってみたこと。

VDCAssistantのプロセス強制終了

カメラを握っているプロセスがあることで、他で利用できなくなっている可能性があるそうです。 以下のコマンドを実行後に再起動をおこないます。 わたしは上記では解決しませせんでした。

sudo killall VDCAssistant

Macで急にFaceTimeカメラが認識されなくなった時の対処法 - Qiita

SMCリセット

電源を切った状態からCtrl + Shift + Option と電源ボタンを押す。 こちらも改善には至りませんでした。

セーフモード再起動

ここで解決しました。 こちらは起動のアップルロゴ表示時にDキーを押して立ち上げます。 動きがカクカクしたセーフモードで起動しアプリ「PhotoBooth」でカメラをチェックすると、何事もなかったかのように動いていました。 その後、再起動をおこない通常モードで起動、この時点でカメラは正常動作していました。

www.16channel.com

カメラのアクセス権がどこかで握られていたか、設定のキャッシュが残っていたかなど定かではありませんが、類似の事象でお困りの方は上記のいずれかで改善されるかもしれないので試してみてください。 (他の情報があればコメント等いただければ幸いです)