script 标签容许你包含一些动态脚本或数据块到文档中,script 标签是非闭合的,你也能够将动态脚本或数据块当作 script 的文本节点。就是内联脚本。javascript
通常咱们最经常使用的就是写一些 JavaScript 脚本在 script 标签里,可是 script 也能够用来存储一些数据,好比当你设置 type="text/react" 的 script 时就能够在里面放 react 代码,可是游览器是不会执行它没法识别的 type 的,所以 script 还能够用来存放一些临时 APP 数据。html
<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>
也能够经过 documents.scripts[0].text 获取到第一个脚本的内容,能够修改它,可是不会有任何做用。前端
当你指定了 src 属性时,外部脚本的内容是不受脚本内容限制的;同时你的 script 标签内必须是空的。若是没有指定 src,就称这段脚本是内联的,内联脚本受到脚本内容限制。java
什么是脚本内容限制?react
<script> alert('hello <script') // 报错, DOM 解析器会认为 <script 是一个 script 标签开头 alert('hello <!--') // 报错,DOM 解析器会认为 <!-- 是一个注释开头 if (1<script) { } // 报错,DOM 解析器会认为 <script 是一个 script 标签开头 if (x<!--y ) { } // 报错,DOM 节气息会认为 <!-- 是一个注释开头 alert('hello <\script') // 正常,添加了转义 alert('hello <\!--') // 正常,添加了转义 </script>
因此看得出来,若是你使用打包工具,为了减小 CRP 而将脚本内联到文档里,代码要注意是否符合脚本内容限制;若是你还压缩了代码,更须要注意这一点。es6
<script defer=defer src="xxx"></script> <!-- 这段脚本不会阻塞 DOM 解析,会并发的下载脚本,并在 DOM 解析完成以后才会执行 --> <script async src="xxx"></script> <!-- 这段脚本不会阻塞 DOM 解析,会并发的下载脚本,并在脚本下载完成后暂停 DOM 解析,而后执行脚本 -->
在 script 中,默认的 type="text/javascript",还能够是 JavaScript MIME 中的任意一种。若是 script 里写的是 JavaScript,推荐省略 type 属性。不指定 defer 和 async 下的经典脚本的执行会阻塞 DOM 解析。web
若是 type=module,则说明标签引用的是一个 ES 模块。segmentfault
<script type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Modules are pretty cool.'); </script>
// utils.js export function addTextToBody(text) { const div = document.createElement('div'); div.textContent = text; document.body.appendChild(div); }
仅仅支持“裸导入”跨域
// Supported: import {foo} from 'https://jakearchibald.com/utils/bar.js'; import {foo} from '/utils/bar.js'; import {foo} from './bar.js'; import {foo} from '../bar.js'; // Not supported: import {foo} from 'bar.js'; import {foo} from 'utils/bar.js';
支持 type=module 的游览器会自动忽略带有 nomodule 的 script 标签。方便你回退到不支持 module 的老式的用户代理。服务器
<script type="module" src="module.js"></script> <script nomodule src="fallback.js"></script>
并且 type=module 默认带有 defer
<!-- This script will execute after… --> <script type="module" src="1.js"></script> <!-- …this script… --> <script src="2.js"></script> <!-- …but before this script. --> <script defer src="3.js"></script>
执行的顺序是 2.js,1.js,3.js
即使是内联的 module,依然具备 defer 属性。
<!-- This script will execute after… --> <script type="module"> addTextToBody("Inline module executed"); </script> <!-- …this script… --> <script src="1.js"></script> <!-- …and this script… --> <script defer> addTextToBody("Inline script executed"); </script> <!-- …but before this script. --> <script defer src="2.js"></script>
模块脚本只会执行一次
<!-- 1.js only executes once --> <script type="module" src="1.js"></script> <script type="module" src="1.js"></script> <script type="module"> import "./1.js"; </script> <!-- Whereas normal scripts execute multiple times --> <script src="2.js"></script> <script src="2.js"></script>
必须符合同源策略
<!-- This will not execute, as it fails a CORS check --> <script type="module" src="https://….now.sh/no-cors"></script> <!-- This will not execute, as one of its imports fails a CORS check --> <script type="module"> import 'https://….now.sh/no-cors'; addTextToBody("This will not execute."); </script> <!-- This will execute as it passes CORS checks --> <script type="module" src="https://….now.sh/cors"></script>
模块脚本在跨域的时候默认是不带 credentials 的。
<!-- Fetched with credentials (cookies etc) --> <script src="1.js"></script> <!-- Fetched without credentials --> <script type="module" src="1.js"></script> <!-- Fetched with credentials --> <script type="module" crossorigin src="1.js?"></script> <!-- Fetched without credentials --> <script type="module" crossorigin src="https://other-origin/1.js"></script> <!-- Fetched with credentials--> <script type="module" crossorigin="use-credentials" src="https://other-origin/1.js?"></script>
下图能够很好的诠释经典脚本和模块脚本加载的不一样
模块脚本的依赖层级的增长会不会致使 CRP 长度的增长?
上图能够看出,层级很深的时候,用户代理会花费大量的时间在等待依赖文件的传输和解析上,所以这会致使 CRP 长度的增长;不过 http2 push 的魔法使得用户代理下载依赖文件的时间会大幅减小,服务器会分析模块的依赖树,而后在一次请求里回传全部依赖文件给用户代理。具体的讨论能够看 Are ES6 modules in brwosers going to get loaded level-by-level 详细讨论了这个问题。
给出脚本内容的编码方式;没有 src 的 script 不能设置该属性,模块脚本强行按 utf8 来解析。
noscript 标签告诉游览器,若是你不支持脚本或脚本被禁用,那就显示我里面的内容。一般被用做脚本被禁用的回退方案。
script 标签真的使人感到兴奋。
若是你以为个人文章不错,能够关注个人
知乎专栏:挽起袖子搞前端
Segmentfault:mrcode的文章
技术博客:blog.mrcodex.com
推特:mrcodehang
新浪微博:Mr云航