この記事は HTML 標準の解釈に関する一連の記事です。その他の記事については、こちらを参照してください。
HTML ページで js スクリプトを実行するには、次のようなさまざまな方法があります。
- スクリプトを実行するには script タグを使用します。
- 使用されるナビゲーション
javascript:URL
。 - DOM でイベント監視メカニズムを使用します。
- SVG 関連テクノロジーのスクリプト機能を使用します。
これらの方法の中で、最もよく使用されるのは間違いなく最初の方法です。script タグを使用すると、開発者は js スクリプトをページに挿入できます。type
属性の値に応じて、script 要素は 4 つの異なるタイプに分類できます。
タイプ | 対応する type 属性値 | 説明 |
---|---|---|
jsの伝統的なスクリプト(古典的なスクリプト) | type 属性が宣言されていないか、type 属性値が空であるか、type 属性値が任意のJavaScript MIME タイプ( などtext/javascript ) と一致します。 |
ECMAScript トップレベル スクリプトの構文規則に従って解析されるスクリプト。 |
jsモジュールスクリプト(モジュールスクリプト) | 「モジュール」 | ECMAScript の最上位モジュールの構文ルールで解析されるスクリプト。 |
輸入マップ | 「インポートマップ」 | ページ内のモジュール指定子の解析を制御します。 |
データブロック | 上記以外の値 | ブラウザはそれを処理せず、内部テキストは他のスクリプトのデータとして使用できます。 |
最初の 3 つのタイプについては、HTMLScriptElement.supports(type)
メソッドを使用してブラウザがこれらのタイプをサポートしているかどうかを検出できます。対応するパラメータは、 、またはtype
です。classic
module
importmap
従来のjsスクリプト
従来の js スクリプトは、私たちが最もよく使用するスクリプトのタイプです。js コードをタグ内に直接記述することも、src 属性を介して外部の js ファイルをインポートすることもできます。
歴史的な理由から、スクリプト タグのコンテンツの解析にはいくつかの奇妙なルールがあります。たとえば、次のスクリプト タグはどれも期待どおりに機能しません。
<!-- 1: script标签把字符串内容</script>看成是闭合标签 -->
<script>
const example = "script的闭合标签是</script>";
console.log(example);
</script>
<!-- 2: script标签把<!--看成是注释的起始标签 -->
<script>
if (x <!--y) {
... }
</script>
これらの落とし穴を回避するために、標準では、コメント コンテンツ内のスクリプト タグ、正規表現、および 内のすべての文字列を 、 でエスケープし、<!--
js式でそのような記述を使用しないことを推奨しています<script
。したがって、上記の問題は次のように修正できます。</script
\x3C!--
\x3Cscript
\x3C/script
<script>
const example = "script的闭合标签是\x3C/script>";
console.log(example);
</script>
<script>
if (x < !--y) {
... }
</script>
jsモジュールスクリプト
コードをさまざまなモジュールに分割することは、プログラマーが複雑さに対処するための重要な方法です。js モジュール スクリプトがネイティブ ブラウザー サポートを取得する前に、webpack などのパッケージ化ツールを使用するなど、何らかの間接的な手段を通じてのみモジュール化の目標を達成できます。
es6 以降、ブラウザはモジュール方式をネイティブにサポートします。これで、宣言型 js モジュール スクリプトを使用できるようになりましたtype="module"
。次のスクリプト タグでは、app.js とその依存関係がブラウザーによってフェッチされます。
<script type="module" src="app.js"></script>
歴史的な理由により、従来の js スクリプトの取得と実行は HTML 解析をブロックします。この場合、async
属性を使用してブラウザにスクリプトを非同期でフェッチさせることも、defer
属性を使用して HTML が解析されるまでスクリプトの実行を遅らせることもできます。jsモジュールスクリプトの場合、デフォルトでは非同期取得となり、HTMLの解析が完了してから実行が開始されます。この属性を使用するasync
と、取得が完了した直後に js モジュール スクリプトを実行できます。この時点で HTML が解析されていない場合、解析はスクリプトの実行によってブロックされます。この属性は js モジュールには影響しませんdefer
。モジュールスクリプト。
async
属性、defer
属性、および実行時の HTML 解析プロセスの間の関係は、次の図で要約できます。
js モジュールのインポートに加えて、js モジュール スクリプトは css モジュールと json モジュールもインポートできますが、assert
ステートメントを使用してそれらの型を宣言する必要があります。
<script type="module">
import json from 'example.json' assert {
type: 'json'}
import css from 'example.css' assert {
type: 'css'}
// ...
</script>
インポートマップ
importmap は、つい最近 (2022 年 10 月 5 日) に標準規格に正式に書き込まれたスクリプト タイプです。
AMD、commonJs、または es6 モジュールのいずれのモジュール システムにも、「モジュール識別子」という概念があります。モジュール識別子はモジュールのインデックスを付けるために使用され、単純にモジュールの名前として理解できます。多くの場合、モジュール識別子はコードが配置されているパスです。たとえば、次のコードでは、"/node_modules/moment/src/moment.js"
このファイルに対応するモジュールのモジュール識別子です。
import moment form "/node_modules/moment/src/moment.js"
importmap以前は、es6モジュールのモジュール識別子は上記のような実際のパスしかサポートしていませんでしたが、importMapではモジュール識別子の再マッピングが実現できます。たとえば、次の例では、上記"/node_modules/moment/src/moment.js"
にマップします"moment"
。したがって、このページ内のすべての js モジュール スクリプトを均一に使用して、import XXX from "moment"
このモジュールをインポートできます。
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js"
}
}
</script>
<script type="module">
import moment from "moment"
// ...
</script>
各ページには最大 1 つの importmap を含めることができます。importmap には、2 つのトップレベル キーをサポートするインライン JSON 表現が必要です。
-
imports
: 上に示したように、グローバルに機能するマップ。 -
scopes
:ローカルマップ上で動作します。次の例のように、ページ内で同じモジュールの異なるバージョンを使用するためによく使用されます。<script type="importmap"> { "scopes": { "/a/" : { "moment": "/node_modules/moment/src/moment.js" }, "/b/" : { "moment": "https://cdn.example.com/moment/src/moment.js" } } } </script>
を使用すると
import "moment"
、異なる場所にあるスクリプトには異なる状況が発生します。- 以下にあるスクリプト
/a/
がインポートされます"/node_modules/moment/src/moment.js"
。 - 以下にあるスクリプト
/b/
がインポートされます"https://cdn.example.com/moment/src/moment.js"
。 - 以下にあるスクリプトは
/c/
エラーを報告します。
- 以下にあるスクリプト
importmap は、いくつかのタイプのモジュール識別子もサポートしています。
-
裸の指定子 (裸の指定子)、
/
上記のようなスラッシュのない識別子。 -
スラッシュで終わる識別子: クラスのマップに使用できるパス。
<script type="importmap"> { "imports": { "moment/": "/node_modules/moment/src/" } } </script> <script type="module"> import localeData from "moment/locale/zh-cn.js" // ... </script>
-
URL クラス識別子: 絶対パスと相対パスを含む。
{ "imports": { "https://cdn.example.com/vue/dist/vue.runtime.esm.js": "/node_modules/vue/dist/vue.runtime.esm.js", "/js/app.mjs": "/js/app-8e0d62a03.mjs", "../helpers/": "https://cdn.example/helpers/" } }
実際には、three.js は長い間importmap を使用してきましたが、それは shims と一緒に使用されます。
データブロック
script タグの属性がtype
、js 従来のスクリプト、js モジュール スクリプト、または importmap のいずれのタイプにも一致しない場合、ブラウザーはこのタグを直接無視します。実際の開発ではこのタグがデータブロックとしてよく使われます。
たとえば、データ ブロックを使用してゲーム マップを保存できます。このデータ ブロックは、ゲームの実行時にマップを生成するために使用したり、サイトでの検索に使用して特定の機能を提供したりできます。
<script src="game-engine.js"></script>
<script type="text/x-game-map">
........U.........e
o............A....e
.....A.....AAA....e
.A..AAA...AAAAA...e
</script>
実際の例もいくつか見てみましょう。
-
systemjs : systemjs を使用すると、開発者は古いブラウザーで es6 モジュールの構文を使用できます。
type="systemjs-module"
およびscript タグを使用してtype="systemjs-importmap"
、それぞれ js モジュール script と importmap をシミュレートします。この script タグは本質的にデータ ブロックです。ブラウザはこれらの script タグに対して処理を行いません。これらのタグは内部処理のために systemjs に残され、それによって、モジュールをロードするプロセス:<script src="system.js"></script> <script type="systemjs-importmap"> { "imports": { "lodash": "https://unpkg.com/[email protected]/lodash.js" } } </script> <script type="systemjs-module" src="/js/main.js"></script>
-
three.js : threejs は 3D ライブラリです。その例
type="x-shader/x-vertex"
では、のスクリプトtype="x-shader/x-fragment"
タグなど、3D レンダリング モデルのデータを保存するためにデータ ブロックがよく使用されます。<script id="procedural-vert" type="x-shader/x-vertex"> varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } </script>
標準では、データ ブロックを使用する場合、将来的に標準で新しいタイプが追加されたときに競合を避けるために、その形式に準拠するMIME タイプを使用するのが最善であると示唆されていることに言及する価値があります。
"text/html" // 符合格式
"text/html;" // 不符合格式
"text/html;charset=uft-8" // 符合格式