Do you know the four usages of the four script tags?

This article is a series of articles on the interpretation of HTML standards. For other articles, see here .

There are many ways to execute js scripts in an HTML page, including but not limited to the following:

  • Use the script tag to execute the script;
  • the navigation used javascript:URL;
  • Use the event monitoring mechanism on the DOM;
  • Use scripting capabilities in svg-related technologies;

Among these methods, the most used one is undoubtedly the first one. The script tag allows developers to insert js scripts into the page, and according to typethe value of the attribute, the script element can be divided into 4 different types:

type The corresponding type attribute value describe
js traditional script (classic script) The type attribute is not declared, or the type attribute value is empty, or the type attribute value matches any JavaScript MIME type (such as text/javascript). A script that is parsed according to the syntax rules of ECMAScript top-level script.
js module script (module script) “module” A script that is parsed with the syntax rules of the top-level Module of ECMAScript.
Imports map “importmap” Controls the parsing of module specifiers within a page.
data block other values ​​than above The browser will not process it, and the internal text can be used as data in other scripts.

For the first three types, you can use HTMLScriptElement.supports(type)methods to detect whether the browser supports these types, and the corresponding parameters typeare classic, moduleor importmap.

js traditional script

js traditional script is the type of script we use the most. We can write js code directly in the tag, or import an external js file through the src attribute.

For historical reasons, the parsing of script tag content has some strange rules. For example, none of the following script tags work as expected:

<!-- 1: script标签把字符串内容</script>看成是闭合标签 -->
<script>
    const example = "script的闭合标签是</script>";
    console.log(example);
</script>

<!-- 2: script标签把<!--看成是注释的起始标签 -->
<script>
    if (x <!--y) {
      
       ... }
</script>

In order to avoid these pitfalls, the standard recommends that all strings in script tags, regular expressions, and , in comment content be escaped with <!--, <script, </scriptand \x3C!--avoid \x3Cscriptusing \x3C/scriptsuch writing in js expressions. Therefore, the above problem can be corrected as follows:

<script>
    const example = "script的闭合标签是\x3C/script>";
    console.log(example);
</script>

<script>
    if (x < !--y) {
      
       ... }
</script>

js module script

Breaking down code into different modules is an important way for programmers to deal with complexity. Before js module scripts get native browser support, we can only achieve the goal of modularization through some indirect means, such as using packaging tools such as webpack.

Starting with es6, browsers natively support modularity. Now you can use type="module"declarative js module scripts. In the following script tags, app.js and its dependencies will be fetched by the browser:

<script type="module" src="app.js"></script>

For historical reasons, the acquisition and execution of js traditional scripts will block HTML parsing. In this case, you can use asyncattributes to force the browser to fetch the script asynchronously, or use deferattributes to delay the execution of the script until after the HTML is parsed. For js module scripts, the default is asynchronous acquisition, and execution starts after the HTML parsing is completed. You can use asyncthe attribute to make the js module script execute immediately after the acquisition is completed. If the HTML has not been parsed at this time, the parsing will be blocked by the execution of the script; the deferattribute has no effect on the js module script.

asyncThe relationship between attributes, deferattributes and the HTML parsing process at runtime can be summarized with the following picture:

async_defer

In addition to importing js modules, js module scripts can also import css modules and json modules, but they need to use assertstatements to declare their types:

<script type="module">
    import json from 'example.json' assert {
      
      type: 'json'}
    import css from 'example.css' assert {
      
      type: 'css'}
    // ...
</script>

importmap

importmap is a script type that was only recently (October 5, 2022) officially written into the standard .

Any module system, whether it is AMD, commonJs or es6 modules, has the concept of "module identifier". The module identifier is used to index a module, you can simply understand it as the name of the module. In many cases, the module identifier is the path where the code is located. For example, in the following code, "/node_modules/moment/src/moment.js"it is the module identifier of the module corresponding to this file:

import moment form "/node_modules/moment/src/moment.js"

Before importmap, the module identifier of the es6 module only supported the actual path like the above, and importMap can realize the remapping of the module identifier. For example, in the following example, "/node_modules/moment/src/moment.js"map to "moment"above; therefore, all js module scripts in this page can be used uniformly to import XXX from "moment"import this module:

<script type="importmap">
    {
      
      
        "imports": {
      
      
          "moment": "/node_modules/moment/src/moment.js"
        }
      }
</script>
<script type="module">
    import moment from "moment"
    // ...
</script>

Each page can have at most one importmap. importmap requires an inline json representation that supports two top-level keys:

  • imports: A map that acts globally, as shown above.

  • scopes: Acts on a local map. Often used to use different versions of the same module within a page, such as the following example:

    <script type="importmap">
        {
            
            
            "scopes": {
            
            
              "/a/" : {
            
            
                "moment": "/node_modules/moment/src/moment.js"
              },
              "/b/" : {
            
            
                "moment": "https://cdn.example.com/moment/src/moment.js"
              }
            }
          }
    </script>
    

    When used import "moment", scripts in different locations have different situations:

    • The script located /a/below will be imported "/node_modules/moment/src/moment.js";
    • The script located /b/below will be imported "https://cdn.example.com/moment/src/moment.js";
    • The script located /c/below will report an error.

importmap also supports several types of module identifiers:

  • Bare specifiers (Bare specifiers), identifiers without slashes /, such as the moment above.

  • Identifiers ending in a slash: paths that can be used to map a class.

    <script type="importmap">
        {
            
            
            "imports": {
            
            
              "moment/": "/node_modules/moment/src/"
            }
        }
    </script>
    <script type="module">
        import localeData from "moment/locale/zh-cn.js"
      	// ...
    </script>
    
  • URL class identifier: including absolute path and relative path.

    {
          
          
      "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/"
      }
    }
    

In reality, three.js has been using importmap for a long time , but it is used with shims.

data block

When the attribute of the script tag typedoes not match any type of js traditional script, js module script, or importmap, the browser will directly ignore this tag. This tag is often used as a data block in actual development.

For example, you can use a data block to store a game map. This data block can be used to generate a map when the game is running, or it can be used for retrieval in the site to provide specific capabilities.

<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>

We can also look at some real-world examples:

  • systemjs : systemjs enables developers to use the syntax of es6 modules on older browsers. It uses type="systemjs-module"and type="systemjs-importmap"script tags to simulate the js module script and importmap respectively. This script tag is essentially a data block, and the browser will not process these script tags. These tags will be left to systemjs for internal processing, thereby simulating The process of loading modules:

    <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 is a 3D library. In its examples , data blocks are often used to store the data of the 3D rendering model, such as type="x-shader/x-vertex"script type="x-shader/x-fragment"tags of .

    <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>
    

It is worth mentioning that the standard suggests that when using data blocks, it is best to use a format-compliant MIME type to avoid conflicts when the standard adds new types in the future:

"text/html" // 符合格式
"text/html;" // 不符合格式
"text/html;charset=uft-8" // 符合格式

Guess you like

Origin blog.csdn.net/weixin_44384265/article/details/127927765