dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

React 16で導入されたError Boundaryについて

React 16 Beta がリリースされました。
Error Boundary という概念が導入されたそうなので公式ブログをざっくり読んでみます。

はじめに:React 15 までの問題

React 15 まではコンポーネントで発生したエラーをうまくハンドリングしたり、そこからリカバリするためのしくみが提供されてませんでした。
結果として

  • コンポーネントのどこかで JavaScript エラーが発生する
  • catch していない場合、知らないうちに React の内部状態(internal state)にも悪影響を及ぼす
  • 結果として開発者コンソール上はぱっと見原因がよくわからないエラーが発生する

みたいなことが起きてました。

(参考)
TypeError: Cannot read property '_currentElement' of null · Issue #4026 · facebook/react
Error: performUpdateIfNecessary: Unexpected batch number ... · Issue #6895 · facebook/react
Cannot read property 'getHostNode' of null · Issue #8579 · facebook/react


Error Boundary の導入

デモ:https://codepen.io/gaearon/pen/wqvxGa

Error Boundary は コンポーネントツリーで発生したエラーをハンドリングする ための React コンポーネントです。
ブログに記載されているサンプルコードを見てみます。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

注目すべきは componentDidCatch(error, info) という新しいライフサイクルメソッドで、これが実装されたコンポーネントのことを Error Boundary と呼ぶようです。

使うときは

<ErrorBoundary>
  <AddTodo />
  <VisibleTodoList />
  <FilterLinkList />
</ErrorBoundary>

のように、エラーをハンドリングしたいコンポーネントツリーをラップするようにして通常のコンポーネントと同じように記述します。

このようにすると、<ErrorBoundary> 以下のコンポーネントツリーで JavaScript エラーが発生したときに componentDidCatch() がエラーを捕捉してくれます。
レンダリング時だけでなく、コンストラクタやライフサイクルメソッド内で発生したエラーも捕捉できます)
try/catchcatch 句と同じですね。

そのため、あるコンポーネントツリーでエラーが発生したときもクラッシュせず
ログを送信したり、エラーメッセージ用のコンポーネントに差し替えるといった対応が可能になります。

注意

Error Boundary が捕捉するのは コンポーネントツリー内で発生したエラーのみで、自コンポーネント内で発生したエラーは捕捉できません。
つまり Error Boundary コンポーネント内のライフサイクルメソッドや render() の書き方をミスってエラーが発生した場合、それは componentDidCatch() で捕捉できません。


componentDidCatch(error, info) の引数の中身

デモ を確認した限り、

  • 第一引数の error は Error オブジェクト( messagestack をプロパティとして持つ)
  • 第二引数の infocomponentStack というプロパティを持つオブジェクト

のようです。
また componentStack には

in BuggyCounter (created by App)
in ErrorBoundary (created by App)
in div (created by App)
in App

のような情報が入っています。Component Stack Trace と呼んでいるようです。(後述)


Error Boundary はコンポーネントツリーのどこに配置すべきか

Error Boundary は複数配置することも可能なので、ツリーのどこに Boundary を設けるかは要件次第です。
アプリケーションのルートに「不明なエラー(Something went wrong)」というメッセージを表示する Boundary を1個だけ置いてもいいですし、
画面の一部分が壊れても他の部分は利用可能な状態を維持したいのであれば、その境界ごとに複数の Boundary を用意する可能性も考えられます。

例として、Facebook Messenger はサイドバーや info panel、チャット部分などはそれぞれ Error Boundary でラップしてるそうです。


Uncaught Error 時の挙動の変更

個人的にこれが大きな仕様変更だなと思いました。
React 16 から、どの Error Boundary でも捕捉されないエラー(Uncaught Error)が発生した場合 コンポーネントツリー全体が unmount され、非表示になります。

先ほどのデモを fork して試してみます。
https://codepen.io/zaki-yama/pen/dzYWNM

f:id:dackdive:20170729013405g:plain

一番下に追加したカウンター(インクリメントして 5 になるとエラー)は Error Boundary でラップしていないのですが、ここで発生したエラーのせいで画面全体がクリアされているのがわかります。


React 15 のときの挙動と比較

https://codepen.io/zaki-yama/pen/RZWVpK

f:id:dackdive:20170728125201g:plain

React 15 のときはコンソールでエラーが発生しているものの、画面自体はそのまま残っていることがわかります。


なぜこのような仕様に変更されたのか

コンポーネントツリーが unmount されて画面全体が操作不能になることより、エラーによって UI や内部状態がおかしくなったことに気づかず中途半端に動作し続けることの方がまずい、との結論に達したようです。
たとえば、商品の支払い画面の UI で実際に表示されている金額と内部で保持している金額が乖離したまま決済を実行されるリスクがあるのは危険ですね。


Component Stack Traces

React 16 ではレンダリング中に発生したエラーは必ずコンソールに出力されます。(in development とあるので NODE_ENV === development の時のみ?)
ログにはエラーメッセージと JavaScript の stack trace に加え、コンポーネントの stack trace も出力されるようになります。

f:id:dackdive:20170729020849p:plain

これにより、コンポーネントツリーのどのコンポーネントでエラーが発生したのか追いやすくなります。


さらにファイル名と行番号も出力する

component stack trace にファイル名と行番号を出力することもできます。create-react-app の場合はデフォルトで有効になっています。
creacte-react-app を使わない場合、babel-plugin-transform-react-jsx-source という Babel プラグインを追加する必要があります。
また追加した際は production build 時には無効化する必要があります。


感想

これまで、アプリで発生したエラーを React ではどのように処理すべきなのかがいまいちわかっていなかったんですが、Error Boundary の導入によって基本的な実装方法がわかりました。
エラー時に外部の error reporting サービスに送信したり、UI のパーツごとに境界を設けてあるパーツが壊れても他のパーツは機能するように、といった制御はかなりやりやすくなりましたね。

Uncaught Error 時の仕様変更については、予期せぬエラーで画面が真っ白になるという状況は避けたいので、アップデート前に最低限アプリのルートには Boundary を設けておいた方が良さそうです。


React 16 Beta を今すぐ試すには

React 16 beta · Issue #10294 · facebook/react

によると

# Yarn
yarn add react@next react-dom@next

# Npm
npm install --save react@next react-dom@next

でインストールできるそうです。未確認。

[Chrome拡張]chrome.storage.localやchrome.storage.syncでストレージに保存したデータを確認・削除する

メモ。
大昔に作った Chrome 拡張に手を加えようと思い立った。

今回は chrome.storage API を使って設定内容をローカルに保存できるようにしたかったんだけど
開発時に保存したデータを確認したり、一旦削除したりするにはどうすればいいのかわからず調べてみた。

GUI で確認する方法とコンソールで確認する方法を示す。

参考


方法1:Storage Area Explorer という Chrome 拡張を使う

ストレージを確認するための Chrome 拡張があるらしい。

使い方は、インストールした後ストレージの内容を確認したい Chrome 拡張のポップアップやバックグラウンド、オプションページを開く。

ポップアップはアイコン右クリック -> 「ポップアップを検証」から。

f:id:dackdive:20170726190108p:plain

バックグラウンドやオプションページは chrome://extensions から。

f:id:dackdive:20170726190348p:plain

その状態でデベロッパー・ツールを開くと、「Storage Explorer」というタブが追加されているはず。

f:id:dackdive:20170726190517p:plain

さらにその下の「chrome.storage.local」「chrome.storage.sync」というそれぞれのタブから、保存されているデータが確認できる。
「Clear」または行の右の削除ボタンを押すとデータを削除することができる。


方法2:デベロッパー・ツールでコマンドを実行する方法

方法1と同じようにデベロッパー・ツールを開いた後、コンソールで以下を実行する。

// sync または local
chrome.storage.sync.get(null, function (data) { console.info(data) });

こうするとコンソールに保存されているデータが表示される。

f:id:dackdive:20170726190920p:plain

削除したい場合はドキュメントに従い remove(key) または clear() を実行する。

chrome.storage.sync.remove('customFormat', function (data) { console.log('Successfully deleted', data); });

// または全削除の場合
chrome.storage.sync.clear()

IDDD本もくもく読書会メモ#2(第3章 コンテキストマップ)

第1回 に続いて第2回も無事に開催することができました。

※社外からの参加者もお待ちしています(Slack グループ

教材

書籍に加え、前回見つけた CodeZine の解説記事

今回は書籍の前に目を通したが、最初に概要を掴んでから書籍に入れるので効果的。今後もこの進め方でいきたい。

第2回で読んだ範囲

第3章を一通り。この章はボリュームが小さくて助かった。


学習メモ

第2章で「境界づけられたコンテキスト」を学んだが、「コンテキストマップ」は複数の「境界づけられたコンテキスト」の関係を俯瞰する図となる。
抽出したコンテキストを線で結び、互いの関係性を整理していくイメージ。

f:id:dackdive:20170717225955p:plain:w320

(図は書籍より引用)

2 つのコンテキスト間にはどちらかが上流(Upstream)でどちらかが下流(Downstream)という関係がある。図中には U または D で書く。

プロジェクトの現状を示す図。こうあって欲しいという将来の図ではない。

コンテキストマップを書く理由

http://codezine.jp/article/detail/9837 より引用。

コンテキストマップを描くことによって、システム間の関係を適切に把握できるメリットがあります。DDDチームは既存システムとの連携方法を把握でき、他チームとのコミュケーションの必要性を判断できるようになります。

 コンテキストマップはアーキテクチャ図というよりも、チーム間のコミュケーション関係を示す図の意味合いが強くなります。コンテキストマップは組織間の問題を見つけ出せる唯一のドキュメントとなるため、プロジェクトの成功に不可欠といわれています。

コンテキストマップの分類

境界づけられたコンテキスト間の関係には、いくつか名前の付いた種類がある。
これは CodeZine で解説されているように、まず大分類として

  1. チーム間の関係を示す「組織パターン」
  2. データとプログラムの連携方法を示す「統合パターン」

に分けて考えると理解しやすい。(書籍では全部いっしょくたにしているが)

  1. チーム間の関係を示す「組織パターン」
    • パートナーシップ
      • 2つのコンテキストを担当するチームが協力的な関係にある
    • 別々の道
      • コンテキスト間で統合を行わない
    • 順応者
      • 上流側が下流側の要求に応える必要がない
      • 例:TwitterGitHub などの API を使った場合
    • 顧客/供給者
      • 上流のチームが成功するかどうかが下流の結果に左右されうるという場合、上流(供給者)は下流(顧客)のニーズに対応する必要がある
      • 例:モバイルアプリ開発における、API 開発チーム(上流)とアプリ開発チーム(下流
  2. データとプログラムの連携方法を示す「統合パターン」
    • 共有カーネル
      • 複数ドメインにおいて共有が必要な部分に、共通で使用するドメインモデルを構築してソースコードレベルで共有する
      • 共有カーネルに変更が必要な場合は他のチームの承認が必要になるため、この部分は極力小さくする
    • 巨大な泥団子
      • (既存システムなど)大規模で複雑なものを、そのまま(適切なモデルに分割せず)大きな塊として捉えること
    • 公開ホストサービス(OHS:Open Host Service)
    • 公表された言語(PL:Published Language)
      • 2 つの境界づけられたコンテキスト内にあるモデル同士で変換するための、共通の言語
      • OHS と組み合わせて使うことが一般的
      • 例:JSON
    • 腐敗防止層(ACL:Anti Corruption Layer)
      • 下流側が上流側の機能を自コンテキストのドメインモデルに変換するレイヤ
      • 上流側と協力関係が築けなかった場合に、上流側に振り回されないように設ける変換層
コンテキストの分析と統合ポイント

境界づけられたコンテキストが適切に分割されているかを分析し、複数の概念が 1 つのコンテキストの中に混じっていた場合は Brandolini の記法では三角の警告アイコンを記載する。

f:id:dackdive:20170717235155p:plain:w320

(図は書籍より引用)


ディスカッションメモ

  • OHS と PL のどちらか一方だけしか使われないことってあるんだろうか

→ あんまりなさそう

  • 競合ポイントはどうやって見つけるのか

書籍に出てきた図が(ひょうたん型になっていて)恣意的な感じが...

  • リモートモデル、ローカルモデルとかのくだりがよくわからない

自立性を確保するには、依存するオブジェクトの状態をローカルシステム側に保持しておけばいい。依存するオブジェクト全体をキャッシュしておけばいいと考える人もいるかもしれない。しかし、DDDでは、この考え方は一般的ではない。その代わりに、ローカルのドメインオブジェクトを作って外部のモデルをそれに変換し、ローカルのモデルに必要な最小限の状態だけを保持する。

→ たとえば、GitHub API を使って Issue 管理アプリを作るとき、

  • API で Issue を GET した結果のプロパティをアプリ内で全部保持するわけはなく、実際には必要なものだけ保持するはず
  • API に仕様変更があったときにも影響を最小限にするため、レスポンスをそのまま使うのではなく何らかのオブジェクトに変換して使うはず(ACL にもなる)

というわけで、API のレスポンスを加工してアプリ内で使うためのモデルに変換する、というのはここに記載されたことそのものズバリな気がする。


次回

7/21(金)19:00 頃からやります。


資料

見つけたものをどんどん追加していきます。

"Redux Architecture Guidelines"を読んだ

こちらの記事を読んで。React&Redux でアプリを作る上で基本的だが重要なことが書かれていたので、後で見返すためにメモ。

なお本文は原文記事の完全な翻訳ではなくポイントだけかいつまんで自分の考えも交えたものです。
また、まとめさせていただく上で著者の許諾を得ています。
(拙い英語でおそるおそる訪ねたら流暢な日本語で快諾いただいてびっくり)


State

state の形を計画(設計)する(Plan your state shape

state オブジェクトの構造をきちんと設計することは非常に重要。
こういった構造にすればいい!という唯一解はないが、設計の手助けとなるチェックリスト(質問リスト)がこちら。

  • users, accounts, items など、API 経由で取得した複数のリソースか(配列かどうか、の意?)
  • ローディングアイコンを出す/出さないなど、loading state を扱うか
  • 成功またはエラー時の通知 UI を扱うか
  • 一貫性がありかつ予測しやすいか。別のチームメンバーがすぐに理解できるか
  • 必要なデータにアクセスしやすいか。不必要にネストしていないか
  • serializable か。ローカルストレージやデータベースに簡単に保存できるようになっているか
  • state のかわりに URL でアクセスできるプロパティはないか
    • URL でアクセスできる = state に保存する必要はない?
  • 重複したデータはないか


state の過剰なネストは避ける(Avoid nesting state objects

こういった state 構造になっていることがたまにある。

{
  foo: {
    bar: {
      baz: {
        qux: ...
      }
    }
  }
}

リレーショナルデータをそのまま state に突っ込むとこういった深いネスト構造になりがちだが、深いネストはそれだけ複雑さを上げる要因となる。

  • コンポーネントレベルでは、欲しい情報を得るためにネストをたどる必要がある
    • これは適宜 container component にすることでいくらか解決しそうだけど
  • reducer レベルでは、state 更新の際のマージ作業が複雑になる
  • 加えてパフォーマンスにも影響する

Normalizing State Shape · Redux
記事中でもリンク貼られてた Redux の公式ドキュメント。知らなかった。

これは今読んでる The Complete Redux Book にも書いてあった。


raw data だけ state に保存する(Storing only raw data in the state

記事中では Redux アプリにおける data には2種類あるとしていて、

  1. raw data:アプリが必要とするデータ。API 叩いて fetch したデータなど
  2. derived data:raw data から計算することで得られるデータ
    • firstNamelastName からユーザーの Name を得る、のような

後者の derived data は raw data からいつでも導出できるので、わざわざ state に保存する必要はない。
state に何か新しい情報を追加するときは、「これって現在の state の情報から作れない?」というのを自問してみるといいとのこと。


React の state よりも Redux の state を使う(Prefer Redux state over React state

React にも state 管理のしくみはあるが、 state の大部分は Redux の state として持たせた方が一貫性があって良い。特にチーム作業においては。

とはいえ例外として React の state で管理した方がいいケースもある。たとえば、複雑な UI の状態など、アプリ全体には重要でないような state を管理するようなとき。


Actions

action の payload は標準化する(Standardize action payloads

特にチーム開発においては、action の型というものも統一しておいた方がいいよねという話。
私も使っているけど Flux Standard Action に従っておけば大体問題ないんじゃないかな。


Action は composable にする(Ensure action creators are composable

composable は「組み立て可能な」ぐらいの意味?
既存の Action を組み合わせたより複雑な Action を作るというケースが実際のプロダクトだとよくあり、そのために Action のインターフェースを統一しておくと良い。
著者は全部 Promise でラップするようにしていて、そうすると全ての Action は then でつなげられる。

redux-thunk 使ってるとそんなに意識することがない?


Component Architecture

Containers & presentational components

コンポーネントの見た目に関する責務を持つ presentational component(純粋な React コンポーネント)と、コンポーネントの振る舞い?に関する責務を持つ container component(connect() したコンポーネント)を明確に分ける。

container と presentational という用語、公式ドキュメントにちゃんと書いてあったの知らなかった。
Usage with React · Redux


コンポーネントツリーの中間でも適切に container を使う(Use intermediary containers

コンポーネントツリーのどこを container にするかはアプリ作ってると悩まされる問題だけど、container にしていいのはアプリの root コンポーネントだけという制約はなくて、むしろそうすると末端のコンポーネントに必要な props を中間のコンポーネントがひたすら "passing through" するだけの無駄が発生する。

こういった不吉な臭いを感じ取ったら適宜 container 化していくといいんじゃないか。

Container にすることによるメリット・デメリットは kuy さんのこの記事が大変わかりやすい。

簡単に言うと connect() した container コンポーネントは Redux に依存することになるので、純粋な UI パーツとして再利用可能なコンポーネントにはならなくなる。そのトレードオフで判断するといい。

IDDD本もくもく読書会をやってみたメモ#1

はじめに

経緯など

教材

最初に読む書籍はこれにした。IDDD 本などと呼ばれたりする。

おそらく「ドメイン駆動設計とは何か」を知るためには エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) を最初に読んだ方がいいんだろうけど、

  • IDDD本の方が発売日が新しく、取り上げているソフトウェアの例とかも馴染みやすいのではないか
  • DDD 本はコードがほぼ出てこない(と聞いている)のでどうしても話が抽象的になりがちで、挫折するんじゃないか

という理由でこちらにした。

なお、後から気づいたんだけど CodeZine で IDDD 本をかいつまんで説明した連載記事があり、
各章に入る前に一度こちらの記事を読んでから書籍に入った方が理解が早い気がしている。

IDDD本から理解するドメイン駆動設計連載一覧:CodeZine(コードジン)


もくもく読書会の進め方

第1回は本当に手探りで、19 時に集まってだらだら喋った後 19:30 から各自その場でもくもくと読書。
20:30 過ぎぐらいから(力尽きたのもあって)読めた範囲についてお互いの理解を確かめるために自由なディスカッションとなった。

第1回で読んだ範囲

第2章から読み始めた。

  • 2.8 全体像
  • 2.9 なぜそれほどまでに戦略的設計を重視するのか
  • 2.10 実世界におけるドメインサブドメイン
  • 2.11 境界づけられたコンテキストの意味を知る

2.11 あたりで頭に全然入ってこなくなったのでギブアップ。


学習メモ

以下、今回学んだことを個人的にメモ。

IDDD本の第2章「ドメイン」「サブドメイン」「境界づけられたコンテキスト」を読み解く (1/4):CodeZine(コードジン)
も一緒に読む。


キーワード


ユビキタス言語

ドメインエキスパート(そのドメインについて一番詳しい人)や開発者を含めたチーム全体で作り上げる共有言語のこと。
チーム全体で「A という用語は B という意味である」というぶれない共通認識を作り上げることが大切。


ドメイン

ドメインとは、広い意味で言うと、組織が行う事業やそれを取り巻く世界のことだ。

ソフトウェアを作るときには必ず対象となるビジネスがあるはずで、その領域のことだという理解。


コアドメインサブドメイン(支援サブドメイン、汎用サブドメイン

ソフトウェアが対象とする事業領域を1つのドメインと捉えて全部入りのドメインモデルを構築すればいいかというとそうではなくて(むしろ逆で)、事業のドメインを分野ごとに適切に小さなドメインに分割していって、分割したドメインを組み合わせて全体を構築することになる。
この分割したドメインコアドメインサブドメイン と呼ぶ。

コアドメインドメイン全体の中でも事業的に最も重要な部分。
ビジネス的に最も価値があり、他社との競争を行う上で差別化要因となる部分のこと?

コアドメインではない補助的な部分を サブドメイン と呼ぶ。
サブドメインはさらに、コアドメインほど需要ではないが業務に不可欠なものを 支援サブドメイン、業務上特別なことはないがシステム上必要なドメイン汎用サブドメイン と呼ぶ。


境界づけられたコンテキスト

境界づけられたコンテキストは明示的な境界であり、ドメインモデルがどこに属するのかを表すものである。ドメインモデルは、ユビキタス言語をソフトウェアモデルとして表したものだ。

これがまだうまく言葉で説明できない。
別の書籍や web 上の解説を読むと「ユビキタス言語が適用できる範囲」などと言われてたりする。

例として本書では「アカウント」という用語を取り上げている。
アカウントという用語は、銀行取引コンテキストでは「口座」という意味になるが、文学コンテキストでは「報告書」という意味になる。

それぞれのアカウントの特徴は、名前だけでは区別できない。区別するには、それぞれが属する概念的なコンテナ、つまり境界づけられたコンテキストに注目する必要がある。これを見てはじめて、両者の違いを理解できるというわけだ。

境界づけられたコンテキスト内では、それぞれの用語はただ1つの意味を持つようになる。すなわち、ユビキタス言語が複数の意味を持たないようになる。


「境界づけられたコンテキスト」と「コアドメインサブドメイン」の関係性

上述したように境界づけられたコンテキストの中では用語についての意味がブレず、ただ1つに決まることが望ましい。ので、コアドメインサブドメインとは1:1の関係になることが望ましい設計といえる、はず。


わからなかったこと

ここで出てきた言葉や概念についてはなんとなくわかった気になれたが、結局

についてはわかっていない。最後の項目については後半で明らかになりそうだけど。


今後の予定

次回は 7/5(水) 19:00〜 にまた集まることになった。

順番にいくと次は2章の残りと3章なんだけど、これはもくもく会と言いつつ一度は各自で目を通してこないとついていけなくなるねーという話になり
できる範囲で読んでくることになった。

とばしてしまった1章も読んだ方がいい気がしてきたので、次回までに頑張って読みたい。。。!

参加者募集してますので、同じように DDD 興味あったけど今まで手を出せてなかったーとかって人がいたらぜひ一緒に勉強しましょう。お気軽にご連絡ください。


Slack グループ

読書会中に話したことをまとめたり、それ以外に書籍を読んでいてわからなかったことなどを共有するために Slack グループを作った。

https://iddd-mokumoku.herokuapp.com/

誰でも参加できるのでよかったらどうぞ。

CircleCIにssh接続したら"Permission denied (publickey)"と表示されたときの対処法

メモ。
CircleCI に SSH でログインするため

f:id:dackdive:20170623182941p:plain

に従い接続しようとしたところ、Permission denied (publickey) が表示された。

$ ssh -p 64543 ubuntu@13.59.112.96
The authenticity of host '[13.59.112.96]:64543 ([13.59.112.96]:64543)' can't be established.
ECDSA key fingerprint is SHA256:Rz1/kWO9LDOBndW9AYUN2XORNU++R87RmzthsSEzMK8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[13.59.112.96]:64543' (ECDSA) to the list of known hosts.
Permission denied (publickey).

公式ドキュメントのここに書いてあった。
SSH access to builds - CircleCI

$ ssh -v git@github.com

# or

$ ssh -v git@bitbucket.com

を実行すると

$ ssh -v git@bitbucket.com
OpenSSH_6.9p1, LibreSSL 2.1.8
debug1: Reading configuration data /etc/ssh/ssh_config
...

debug1: Offering RSA public key: /Users/yamazaki/.ssh/id_rsa

...

というように Offering... という行がある。

同じように CircleCI に対して -v オプションつきでログインすると

$ ssh -v -p 64543 ubuntu@13.59.112.96
...
debug1: Offering RSA public key: ...
...

というように同じような行が見つかるので、そこに書かれているパスが一致しているか確認する。

一致していない場合、GitHub/Bitbucket に接続したときに表示されていたパスを -i オプションで指定してログインする。

$ ssh -i ~/.ssh/id_rsa -p 64543 ubuntu@13.59.112.96

なお、接続しようとしたときに

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
...
Offending ECDSA key in /Users/yamazaki/.ssh/known_hosts:30
...

のようなメッセージが表示された場合、~/.ssh/known_hosts から該当行(ここでは 30 行目)を一度削除して再実行する。

webpack 3 リリース内容まとめ

2系に上げるかどうかを悩んでいる間に 3.0.0 がリリースされてしまった。

を参考にアップデート内容をメモ。

概要、v2 からのマイグレーション

大きな feature は Scope Hoisting と Magic Comment ぐらい。
v2 からの移行も特別な作業は必要なく、たいていはバージョン上げるだけで機能するとのこと。

それでもメジャーバージョンを上げたのは内部的に breaking changes があって、いくつかのプラグインには影響しそうだったから、とのこと。
ということはプラグインが動くかどうか一応確認した方がいい...?


Scope Hoisting

目玉機能その1。
これまでの webpack ではモジュールを1つ1つクロージャでラップしており、それによりそれぞれのモジュールの独立性が保たれていた(関数名の衝突とか)ものの、ブラウザでの実行速度が重くなる原因になっていた。
これに対し Closure Compiler やRollupJS は各モジュールを連結して1つのスコープにまとめてしまうので(hoist)ブラウザでの実行速度が速い。
(これができるのは ES Modules の import/export によって静的にモジュールの依存関係がわかるから?みたいなことが書かれていた)

webpack3 では以下のプラグインを指定することにより、RollupJS などと同じ Scope Hoisting が可能になった。

module.exports = {  
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

余計な関数でのラップがなくなったのでバンドル後のファイルサイズも小さくなったが、注目すべきは上述したように実行速度の向上ぽい。


参考

webpack freelancing log book (week 5–7) | by Tobias Koppers | webpack | Medium

冒頭のブログからリンクされていた。
Scope Hoisting のメリットや webpack でこれを実現するためにどうしたかみたいなことが詳しく書かれている。
特に気になったのは Scope Hoisting によるデメリットのところで、

There are some disadvantages, but most of them are not really relevant, when Scope Hoisting is seen as production-only optimization.

  • HMR can only replace modules if they are isolated with a function wrapper. (DX)
  • Minimizing can be more expensive when the scope is bigger. (Build Performance)
  • Modules can no longer processed individually, but need to be processed combined. (Build Performance)

HMR が効かなくなるみたいだし、プラグインは Production Build 時のみ有効にした方が良さそう。

サンプル

たとえば、

// main.js
import cube from './module_1';
import cube2 from './module_2';

console.log(cube(5) + cube2(5));

// module_1.js
export default function cube(x) {
    return x * x * x;
}

// module2.js
export default function cube(x) {
    return x * x * x;
}

というモジュール(module1 と 2 で同一名の関数をエクスポート)があって、
webpack 2.6.1 だと

/******/ (function(modules) { // webpackBootstrap

...

/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
function cube(x) {
  return x * x * x;
}


/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
function cube(x) {
  return x * x * x;
}

...

というようにたしかに関数でラップされているが、webpack 3.0.0 だと

/******/ (function(modules) { // webpackBootstrap

...

/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

// CONCATENATED MODULE: ./module-1.js
function cube(x) {
  return x * x * x;
}

// CONCATENATED MODULE: ./module-2.js
function module_2_cube(x) {
  return x * x * x;
}

...

というように関数のラップがなくなり、関数名の衝突も自動的に解決してくれてる。
※それ以外の部分の理解ができていないので解釈が間違ってるかもしれないけど

Rollup.js だと REPL で確認した限りもっと簡潔になる


Magic Comments

目玉機能その2。
元々、v2 から import() による動的インポートが可能になっていた。
(参考:webpack2でTree Shaking以外に何ができるようになったのかメモ - dackdive's blog

動的にインポートする部分のモジュールは chunk として別のファイルに出力されていたが、その名前が付けられないせいで 0.bundle.js のようなわかりづらいファイルが生成されていた。

webpack3 では import() 時に以下のコメントを入れることで任意のチャンクファイル名をつけることができる。

import(/* webpackChunkName: "my-chunk-name" */ 'module');

未検証。
※v2.4 あたりから既にできていたらしい


参考

より詳しくは公式ドキュメントのここを読んでくれとのこと。
https://webpack.js.org/guides/code-splitting-async/


その他の機能

https://github.com/webpack/webpack/releases/tag/v3.0.0 を見るしかないのかな?

  • output.libraryExport をサポート。default しかエクスポートしないよう選択可能に(ライブラリ向け)
  • node: false とすると Node.js 向けの機能すべて無効化

あたりが気になったが、詳細は調べきれてません。


今後

どこかのタイミングでリリースに含める feature は投票(vote)制になったらしい。
https://webpack.js.org/vote/