最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一文搞懂 script 标签

    正文概述 掘金(an Onion)   2021-01-15   573

    前端开发应该都知道 HTML 中 <script> 标签的作用——引入 JS 代码,不过由于脚手架和打包工具的普及,我想很少有人再亲手写 <script> 了。本期就借机写一下这个快被遗忘了的 <script> 教程,看看大家是否真的掌握了这个元素。

    基本功能

    废话不多说了,直入正题。<script> 引入 JS 的方式主要有三种:内联、外置,以及动态引入。

    内联

    直接将 JS 代码写到 script 标签内:

    <html>
      <head>
        <script>
          console.log("Hello");
        </script>
      </head>
    </html>
    

    外置脚本

    即通过 src 属性引入外部 URL 或 JS 文件:

    <html>
      <body>
        <script src="http://www.example.com/example.js"></script>
        <!-- 只有加载完并执行完 example.js 后,才开始加载 0.js -->
        <script src="./js/0.js"></script>
      </body>
    </html>
    

    script 标签可以放置在 html 任意位置,head、body,甚至是 div 里。它们——无论是内联还是外置——的执行顺序基本上(async 和 defer 除外)秉承由上至下串行执行的原则。浏览器首次加载 script 期间,还会阻塞 HTML 页面解析;尤其是外置引入 JS,需要经历网络传输、解析和执行,有时候会导致浏览器白屏。所以谈到首屏渲染的时候,我们往往会建议将 script 标签放到 <body> 元素的最下方——先呈现页面再执行 JS。

    <html>
      <head>
        <script>
          var x = "onion";
          console.log(document.head); // <head>...</head>
          console.log(document.body); // null
        </script>
      </head>
      <body>
        <script>
          console.log(x); // onion
          console.log(document.head); // <head>...</head>
          console.log(document.body); // <body>...</body>
        </script>
      </body>
    </html>
    

    此外,在 script 执行期间,它可以获取到所有出现在它上方的 JS 全局变量和 DOM 元素;这导致在一些垃圾代码里,全局元素经常无缘无故被其他代码块污染了。

    动态引入

    我们也可以在 JS 代码里动态添加 script 标签。方法很简单,就是追加一个 script 元素:

    var myScript = document.createElement("script");
    myScript.textContent = 'alert("✋")';
    document.head.appendChild(myScript);
    

    还有,通过 innerHTML 方式其实也能添加 script 标签,只是该标签下的 JS 不会运行——很有趣的冷知识。

    document.head.innerHTML += '<script>alert("✋")';
    

    加载(async & defer)

    上文提到为了加快首屏渲染,我们通过把 script 标签放到 <body> 底部加快首屏渲染速度。现代浏览器还可以使用其他的手段,比如 defer(延迟加载)和 async(异步加载)

    defer

    defer 是 script 里的一个布尔属性,设计目的是将该脚本的执行放到文档完成解析后、DOMContentLoaded(约等于 jQuery.ready)事件前。举个例子,下方的 example.js 文件虽然放在了 head 里,但是它有 defer 属性,不会阻塞下方的 <body> 解析。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <scrip defer src="http://www.example.com/example.js"></scrip>
      </head>
    
      <body>
        <!-- content -->
      </body>
    </html>
    

    此外,当存在多个 defer 脚本时,html5 标准要求按出现顺序执行脚本;但在现实中,浏览器厂商并不那么遵循标准:defer 脚本不一定顺序执行,甚至不一定会排在 DOMContentLoaded 事件前。因此通常的建议是:最好只含一个延迟脚本。

    async

    async 也是 script 标签里的一个属性,该属性也能够消除部分 JS 阻塞。当加上 async 属性后,script 脚本的网络请求便可以并行于 HTML 页面解析发生;并尽快解析和执行该 JS 脚本。也许你会有疑问,async 和 defer 似乎差不多呀,那它们的区别到底是什么?一图胜千言:

    一文搞懂 script 标签

    模块化

    type

    先说一个叫 type 的属性,该属性原本是用来指定 script 脚本的 MIME 类型,默认值是 text/javascript,其他值还有诸如:text/ecmascriptapplication/ecmascriptapplication/javascript 等等。不过,现代浏览器很多都不再鸟这些值了;而是把 type 用来支持 es6 的模块功能:

    <!-- index.html -->
    <scrip type="module">
      import { sayHi } from "./hello.js"; document.body.innerHTML = sayHi("Onion");
    </scrip>
    

    用法很简单,在 script 标签里指定 type="module",当脚本使用 import 指令时,浏览器会自动请求并加载相关的 JS 文件。

    // hello.js
    export function sayHi(user) {
      return `Hello, ${user}!`;
    }
    

    这里再提一下,module 的默认加载机制就是 defer,只不过下载过程中会顺道把 import 导入的文件也给下载了;如果和 async 属性一起使用,其加载方式就是 async 形式了,大同小异,就不再赘述了。

    nomodule

    除此之外,我们常常会看到 module script 下方还会跟一个 nomodule 的 script:

    <scrip type="module" src="app.js"></scrip>
    <scrip nomodule src="classic-bundle.js"></scrip>
    

    这个功能主要是用来兼容一些老版本的浏览器:

    • 支持 module 的浏览器,设定上就不会执行 nomodule 属性的 script 脚本,所以它只会跑上方的 app.js 脚本

    • 而老破旧的浏览器不支持 type="module",会跳过这个 script 标签;同时又由于它不认识 nomodule 属性,反倒会执行 nomodule script 里的 classic-bundle.js 文件了

    一个小技巧就解决了浏览器兼容方面的问题。

    安全机制

    integrity

    该属性允许 script 标签提供一个 hash 值,用于检验加载的 JS 文件是否完整。比如,如下便签的 integrity 值就是告诉浏览器:使用 sha256 算法计算 JS 文件的摘要签名,然后对比 integrity 值,如果不一致就不执行该资源。它的主要功能就是防止托管在 CDN 上的资源被篡改。

    <scrip
      src="//code.jquery.com/jquery.js"
      integrity="sha256-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
    ></scrip>
    

    nonce

    nonce 在我之前的文章——《CSP 101》——提到过:它是一个加密数字,需要配合 Content-Security-Policy 的 script-src 使用。举个例子,http 头的 CSP 属性如下:

    Content-Security-Policy: script-src 'nonce-EfNBf03nceIOAn39fn389h3sdfa';
    

    只有在 script 标签内带有相同 nonce 值的脚本才能执行:

    <script nonce="nonce-EfNBf03nceIOAn39fn389h3sdfa" src="./hello.js"></script>
    

    referrerPolicy

    该属性主要和 HTTP 头里的 Referer 配合使用。有些服务器审查比较严格,需要知道请求的“引荐人”(Referrer);客户请求 API 时,需要同时发送引荐人信息。最简单的使用方式就是给相关的 script 标签添加 referrerPolicy 属性:

    <scrip referrerpolicy="origin" src="./js/hello.js"></scrip>
    

    如上代码中,hello.js 里的所有 api 请求都会在头信息里加上相应 URL 的域(origin)。referrerPolicy 的值很多,也很琐碎,有兴趣的朋友可以去MDM 相关页面查看。

    冷知识:HTTP 头的 Referer 有拼写错误,正确的写法是 Referrer;但是标准提案里写错了,结果大家就将错就错了

    crossorigin

    在 HTML5 中,<script> 与其他一些元素(<audio><img><link>、和 <video>)提供了对 CORS 的支持; 他们均有一个跨域属性——crossorigin——来配置元素获取数据的 CORS 请求。一旦启用 crossorigin,http 头里须包含 Access-Control-Allow-Origin 属性,若该属性不存在或是源不必配,则不能加载资源。

    Crossorigin 的默认值是 anonymous(空值或是无效值都等于 anonymous),表示对跨域请求不设置凭据标志;相反,想要提供该凭证,就需要设置 crossorigin="use-credentials"。(这里的凭据,指的就是 cookies、http 里的 auth,以及客户端的 SSL 证书

    onload & onerror

    onload 和 onerror 算是两个隐藏属性吧,因为只能在动态引入时使用。顾名思义,onload 会指向成功加载时的事件,onerror 就是失败时触发的事件。用法也很简单,就是给这两个属性赋值某个事件函数。现实操作中常配合 crossorigin 使用,打印出三方源的一些错误信息。

    let script = document.createElement("script");
    
    script.src = "http://www.example.com/example.js";
    document.head.append(script);
    
    script.onload = function () {
      alert("Success Loading");
    };
    
    script.onerror = function () {
      alert("Error Loading");
    };
    

    扩展小知识:基本上所有包含 src 属性的 HTML 元素都有 onload 和 onerror 这两个隐藏属性,如: <img><iframe>

    其他

    • language: 早年间用来指定脚本语言的属性,如 Javascript、JavaScript1.2、VBScript,不过现在已弃用

    • charset:指定代码的字符集,如charset="UTF-8",可惜也已经过时了

    小结

    <script> 一直是我的知识盲点,网上除了 MDM 这种艰涩难懂的标准文档外,竟然很难再找到相关的教程了。本文整理了我见到过的所有 script 属性,并加了一点小小的知识延伸,希望能给大家查漏补缺予以一定帮助。


    起源地下载网 » 一文搞懂 script 标签

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元