$30 off During Our Annual Pro Sale. View Details »

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

shimataro
February 22, 2018

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

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

shimataro

February 22, 2018
Tweet

More Decks by shimataro

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. 従来のIMPORT
    従来のIMPORT

    View Slide

  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

    View Slide

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

    View Slide

  9. 動的IMPORTの需要
    動的IMPORTの需要

    View Slide

  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),
    }));

    View Slide

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

    View Slide

  12. DYNAMIC IMPORT 爆誕
    DYNAMIC IMPORT 爆誕

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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);
    });

    View Slide

  17. 使用上の注意点
    使用上の注意点

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. 解決方法
    解決方法

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  36. View Slide