ES Modules で HTML から直接インポートされるとデフォルトの処理をさせたい
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ Node.js の require.main === module での分岐みたいなイメージ
◆ 通常は JavaScript から使われるように export だけする
◆ HTML からだと自動でデフォルトの処理も実行する
◆ 通常は JavaScript から使われるように export だけする
◆ HTML からだと自動でデフォルトの処理も実行する
ロードするだけで使えるようにしたい
ES Modules の .js ファイルがロードされる時 JavaScript でインポートされたときは export するだけで HTML から script タグでインポートされたらデフォルトの処理もしたいですHTML で直接インポートする場合はこういう感じのものです
<script type="module" src="http://example.com/module.js"></script>
JavaScript からロードする場合は
import { init } from "https://example.com/module.js"
init(options)
みたいなものです
JavaScript の場合は export された関数に引数を渡して呼び出せます
HTML からインポートする場合は export された関数を使って操作したりできないので デフォルトとして設定された処理をしてほしいです
JavaScript からインポートする場合は自分で設定して関数を呼び出すのでデフォルトの処理は実行しないでほしいです
いわば Node.js の CJS の
if (require.main === module) {
// ...
}
とか Python の
if __name__ == "__main__":
...
みたいなものです
細かに設定したいなら JavaScript からインポートする
ロードするだけで自動でいい感じに動いてほしい場合は HTML から直接ロードするという風にしたいです
インポート元はわかる?
インポートされたときにどのモジュールからインポートされたかを知ることができればそういう分岐もできそうと思ったのですが import.meta のデータを見てもそれらしいのはありませんでした考えてみれば 1 つのモジュールは複数箇所からインポートされることがあり モジュールのスクリプトが実行されるのは最初の一回だけです
最初にインポートされたものに依存することになりますし インポート元というデータはモジュール内で取れない気がします
手動で検出
そうなると別の方法でデフォルトの処理をすべきか判断しないといけませんHTML から直接ロードであれば script タグが document 以下のどこかに存在するはずです
if ([...document.scripts].some(script => script.src === import.meta.url)) {
// ...
}
とすれば自身のスクリプトが HTML から直接ロードされたものかわかります
ですが document.scripts を見るのは少し嫌な感じがします
Node.js でも動かすなら globalThis.document があるかの確認もすれば動きはしますが 他の手段が取れるならそっちにしたいです
ファイルを分ける
エントリポイントの .js ファイルを別にするのが簡単な方法ですindex.js は export するだけで JavaScript から使われる用とします
main.js では index.js をインポートしてデフォルトの処理を行います
使う側は JavaScript からなら index.js をインポートし HTML からなら main.js の方をインポートします
この方法だとファイルも分かれてはっきりとしてます
JavaScript からインポートする場合でも 自分で設定が不要なら main.js の方をインポートできます
でも 使う側的には指定するファイルが一つだけのほうが楽だったりするんですよね
オプションをつけて分岐
そうなると URL に ? や # をつけてオプションを指定する方法でしょうかデフォルトの処理をしてほしい場合には #main をつけるようにします
<script type="module" src="http://example.com/module.js#main"></script>
分岐はこういう風に import.meta.url を見て判断します
if (new URL(import.meta.url).hash === "#main") {
// ...
}
1 つのファイルになったと言っても URL に指定するものが別になってます
この仕組みと # を使うことと main という名前を覚えてるなら別ファイルでもいい気がします
これはどっちかというと提供する側が 1 ファイルに全部をまとめたい場合向けかもしれません
module かどうかで判断
module としてインポートするなら export で 従来の JavaScript としてロードされるとデフォルト処理という分岐も考えてみました<script src="http://example.com/module.js"></script>
というふうに type="module" でないロードだとデフォルト処理を行います
しかしこれは無理そうでした
どちらとしても動く JavaScript ファイルである必要がありますが 従来の JavaScript として動かすと export/import キーワードは使えません
それに合わせると module としてインポートされたときに export できないです
一般的な式とは違うので if 文の中で動的に eval で実行して export させることもできません
まとめ
使う側が色々考えなくても自動で判断してやってくれるを実現するなら document.scripts を見て自身が HTML から直接ロードされたかを調べるしかなさそうですこの方法がイマイチならロードするファイルを分けるか # などを使ってオプション指定という 使う側が切り替える方法しかなさそうです