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