Slide 1

Slide 1 text

DYNAMIC IMPORT あれこれ DYNAMIC IMPORT あれこれ (2018/02/22) (2018/02/22) 小田島 太郎 / @shimataro NODE学園 29時限目 NODE学園 29時限目

Slide 2

Slide 2 text

自己紹介 自己紹介 ウェブリオ株式会社所属(京都) サーバサイドエンジニア 趣味は手品 小田島 太郎 shimataro@GitHub odashima.taro@Facebook shimataro999@Twitter

Slide 3

Slide 3 text

この発表について この発表について 対象: ES2017のdynamic importに興味のある人 技術レベル: 中級 Webサービス開発者には特に見てもらいたい ↓スライドはこちら↓ https://speakerdeck.com/shimataro https://shimataro.github.io/slides/

Slide 4

Slide 4 text

目次 目次 従来のimport 動的importの需要 dynamic import 爆誕 使用上の注意点 解決方法 まとめ

Slide 5

Slide 5 text

それでは始めます それでは始めます

Slide 6

Slide 6 text

従来のIMPORT 従来のIMPORT

Slide 7

Slide 7 text

従来のIMPORT 従来のIMPORT ES2015で標準化 複数の import 方法が定義されている 詳細は を参照 import defaultMember from "module-name"; import * as name from "module-name"; import { member } from "module-name"; import { member as alias } from "module-name"; import { member1 , member2 } from "module-name"; import { member1 , member2 as alias2 , [...] } from "module-name" import defaultMember, { member [ , [...] ] } from "module-name"; import defaultMember, * as name from "module-name"; import "module-name"; MDN

Slide 8

Slide 8 text

従来のIMPORT 従来のIMPORT 使えない例 グローバルスコープ で使用 実行開始時点で 必要なモジュールを確定 function bar() { import "foo"; } if (condition) { import "bar"; } import moduleName;

Slide 9

Slide 9 text

動的IMPORTの需要 動的IMPORTの需要

Slide 10

Slide 10 text

動的IMPORTの需要 動的IMPORTの需要 例1: セッションデータの保存先をcon gで指定したい # config.yaml session: store: "connect-memcached" options: hosts: - "localhost" secret: "a secret key" import session from "express-session"; import Store from config.session.store; app.use(session({ ..., store: new Store(session)(config.session.options), }));

Slide 11

Slide 11 text

動的IMPORTの需要 動的IMPORTの需要 例2: 開発時のみ、開発支援モジュールを使いたい # config.yaml develop: true if (config.develop) { // プロファイラやらメモリリーク検出やら import "some-development-support-modules"; }

Slide 12

Slide 12 text

DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕

Slide 13

Slide 13 text

DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 ES2017の新機能 V8 ver6.3 / Chrome ver63 で実装 Node.js ver10でサポート?

Slide 14

Slide 14 text

DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 関数呼び出しスタイル Promiseを返す named importのみサポート import("foo") .then((foo) => { // ロード成功 ... }) .catch((err) => { // ロード失敗 ... });

Slide 15

Slide 15 text

DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 関数呼び出しスタイル Promiseを返す named importのみサポート // async/awaitもOK try { const foo = await import("foo"); ... } catch(err) { ... }

Slide 16

Slide 16 text

DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 セッションデータ保存先の指定例 import() がPromiseを返すので、少し複雑になる import session from "express-session"; const StorePromise = import(config.session.store); app.use((req, res, next) => { StorePromise .then((Store) => { const store = new Store.default(session); session({ ..., store: store(config.session.options), })(req, res, next); }) .catch(next); });

Slide 17

Slide 17 text

使用上の注意点 使用上の注意点

Slide 18

Slide 18 text

使用上の注意点 使用上の注意点 …とまあ便利なdynamic importですが、 Webサービスで使う場合には注意が必要です。 ※ここから本題です

Slide 19

Slide 19 text

使用上の注意点 使用上の注意点 こんな状況を仮定 へのアクセスで、 moduleA が動 的にロードされる このページはまだアクセスされていない つまり moduleA もまだロードされていない http://example.com/a app.get("/a", (req, res, next) => { import("moduleA")...; });

Slide 20

Slide 20 text

使用上の注意点 使用上の注意点 こんな状況を仮定 その2 リファクタリングや仕様変更などで、 moduleA がなくなっ た(リネーム・移動を含む) app.get("/a", (req, res, next) => { // モジュール名を変更 import("moduleA2")...; });

Slide 21

Slide 21 text

使用上の注意点 使用上の注意点 リリース手順 1. 変更後のソースをサーバに転送 moduleA が削除される 2. Node.jsを再起動 1と2の間で にアクセス ⇒ import("moduleA") でエラー! http://example.com/a

Slide 22

Slide 22 text

使用上の注意点 使用上の注意点 時系列で説明しよう

Slide 23

Slide 23 text

解決方法 解決方法

Slide 24

Slide 24 text

解決方法(?) 解決方法(?) その1: 何もしない アクセス頻度の低いページでしか起きない 再起動はせいぜい数秒 そこに当たった人は運がなかったってことで。 ⇒あまり重要でないページならまあアリかも? 決済のようなクリティカルなページで起きたら問題 graceful restartは再起動までに時間がかかる

Slide 25

Slide 25 text

解決方法(?) 解決方法(?) その2: 転送時、不要になったモジュールも残す rsyncなら --delete オプションをつけずに実行 ⇒Not Foundエラーは起きない モジュールの仕様変更に対応できない Not Foundエラーより深刻になる可能性が…

Slide 26

Slide 26 text

解決方法(?) 解決方法(?) その3: オフラインでデプロイ 前段にバランサ・後段にNodeサーバ(複数) 1台ずつバランサから外してデプロイ ⇒中規模以上ならこんなサーバ構成になっているはず 自動化が難しい クライアントサイドでの発生は防げない

Slide 27

Slide 27 text

解決方法(?) 解決方法(?) クライアントサイドでの発生例 起点が Node.js起動後 ではなく ページロード後 ユーザ数が多いほど発生率が上がる SPAでは発生しやすい ページ滞在時間が長い(リロードしない) イベントが多い CDN上の古いモジュールをロードする可能性 $("#someButton").on("click", () => { // ページロード後、イベント発生前にモジュールが消えたらエラー! import("module")...; });

Slide 28

Slide 28 text

解決方法(!) 解決方法(!) その4: dynamic importを使わない ぶっちゃけ一番簡単で確実 全てグローバルスコープで import Node.js起動時に全モジュールをメモリにロード その後でモジュールが削除されてもOK サーバサイドなら最初に全部読んだ方が効率的

Slide 29

Slide 29 text

解決方法(!) 解決方法(!) dynamic importの存在意義って… ◎ Node.js起動時に動的ロード セッションデータ保存先の指定とか ○ バッチ処理 実行タイミングがある程度決まっている △ あまりクリティカルでないページ エラーが出ても「ごめんね」で済むところ ? Electron…で使える? アップデート時に同じ問題が起きそう

Slide 30

Slide 30 text

まとめ まとめ ES2017にdynamic importが導入されたよ 特にWebサービスで使うときは注意してね ていうか使わないほうがいいかもよ 手品に興味があったら声をかけてね!

Slide 31

Slide 31 text

ご清聴ありがとうございました ご清聴ありがとうございました

Slide 32

Slide 32 text

おまけ おまけ ここまで見てくれてありがとう( *´艸`)

Slide 33

Slide 33 text

参考資料1 参考資料1 私の記事です 今回の話はこれが元です Dynamic importをウェブサービスで使うときの注意点

Slide 34

Slide 34 text

参考資料2 参考資料2 同僚の記事です v6.3で追加された3つの機能にをサンプルコードつきで わかりやすく説明しています V8 6.3で追加されたECMAScriptの機能

Slide 35

Slide 35 text

参考資料3 参考資料3 V8チームのブログです ver6.3での変更点・改善点の詳説があります V8 JavaScript Engine: V8 Release 6.3

Slide 36

Slide 36 text

No content