Upgrade to Pro — share decks privately, control downloads, hide ads and more …

dynamic import あれこれ / dynamic import - overview and pitfalls

D79a0829d09a325462fc9a1462ec503b?s=47 shimataro
February 22, 2018

dynamic import あれこれ / dynamic import - overview and pitfalls

Node学園 29時限目の発表資料です
https://nodejs.connpass.com/event/78902/

D79a0829d09a325462fc9a1462ec503b?s=128

shimataro

February 22, 2018
Tweet

Transcript

  1. DYNAMIC IMPORT あれこれ DYNAMIC IMPORT あれこれ (2018/02/22) (2018/02/22) 小田島 太郎

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

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

    https://shimataro.github.io/slides/
  4. 目次 目次 従来のimport 動的importの需要 dynamic import 爆誕 使用上の注意点 解決方法 まとめ

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

  6. 従来のIMPORT 従来のIMPORT

  7. 従来の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
  8. 従来のIMPORT 従来のIMPORT 使えない例 グローバルスコープ で使用 実行開始時点で 必要なモジュールを確定 function bar() {

    import "foo"; } if (condition) { import "bar"; } import moduleName;
  9. 動的IMPORTの需要 動的IMPORTの需要

  10. 動的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), }));
  11. 動的IMPORTの需要 動的IMPORTの需要 例2: 開発時のみ、開発支援モジュールを使いたい # config.yaml develop: true if (config.develop)

    { // プロファイラやらメモリリーク検出やら import "some-development-support-modules"; }
  12. DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕

  13. DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 ES2017の新機能 V8 ver6.3 /

    Chrome ver63 で実装 Node.js ver10でサポート?
  14. DYNAMIC IMPORT 爆誕 DYNAMIC IMPORT 爆誕 関数呼び出しスタイル Promiseを返す named importのみサポート

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

    // async/awaitもOK try { const foo = await import("foo"); ... } catch(err) { ... }
  16. 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); });
  17. 使用上の注意点 使用上の注意点

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

  19. 使用上の注意点 使用上の注意点 こんな状況を仮定 へのアクセスで、 moduleA が動 的にロードされる このページはまだアクセスされていない つまり moduleA

    もまだロードされていない http://example.com/a app.get("/a", (req, res, next) => { import("moduleA")...; });
  20. 使用上の注意点 使用上の注意点 こんな状況を仮定 その2 リファクタリングや仕様変更などで、 moduleA がなくなっ た(リネーム・移動を含む) app.get("/a", (req,

    res, next) => { // モジュール名を変更 import("moduleA2")...; });
  21. 使用上の注意点 使用上の注意点 リリース手順 1. 変更後のソースをサーバに転送 moduleA が削除される 2. Node.jsを再起動 1と2の間で

    にアクセス ⇒ import("moduleA") でエラー! http://example.com/a
  22. 使用上の注意点 使用上の注意点 時系列で説明しよう

  23. 解決方法 解決方法

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

    restartは再起動までに時間がかかる
  25. 解決方法(?) 解決方法(?) その2: 転送時、不要になったモジュールも残す rsyncなら --delete オプションをつけずに実行 ⇒Not Foundエラーは起きない モジュールの仕様変更に対応できない

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

  27. 解決方法(?) 解決方法(?) クライアントサイドでの発生例 起点が Node.js起動後 ではなく ページロード後 ユーザ数が多いほど発生率が上がる SPAでは発生しやすい ページ滞在時間が長い(リロードしない)

    イベントが多い CDN上の古いモジュールをロードする可能性 $("#someButton").on("click", () => { // ページロード後、イベント発生前にモジュールが消えたらエラー! import("module")...; });
  28. 解決方法(!) 解決方法(!) その4: dynamic importを使わない ぶっちゃけ一番簡単で確実 全てグローバルスコープで import Node.js起動時に全モジュールをメモリにロード その後でモジュールが削除されてもOK

    サーバサイドなら最初に全部読んだ方が効率的
  29. 解決方法(!) 解決方法(!) dynamic importの存在意義って… ◎ Node.js起動時に動的ロード セッションデータ保存先の指定とか ◦ バッチ処理 実行タイミングがある程度決まっている

    △ あまりクリティカルでないページ エラーが出ても「ごめんね」で済むところ ? Electron…で使える? アップデート時に同じ問題が起きそう
  30. まとめ まとめ ES2017にdynamic importが導入されたよ 特にWebサービスで使うときは注意してね ていうか使わないほうがいいかもよ 手品に興味があったら声をかけてね!

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

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

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

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

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

  36. None