内联JavaScript应该放在HTML的哪个位置

5次阅读

共计 2251 个字符,预计需要花费 6 分钟才能阅读完成。

写在前面

本文首发于公众号:符合预期的 CoyPan

内联 JavaScript 在现在的前端项目中是比较常见的,比如一些全局函数,全局统计逻辑等,我们可能会以内联 JavaScript 的方式写在 HTML 里。那么,内联 JavaScript 最好应该放在哪里呢?

页面渲染流程

首先来看看这个问题:页面是怎么渲染的?大致流程如下:

页面的渲染是一个复杂的过程,这里就不再详细说了。简单总结一下,就是使用 HTML 构建 DOM 树,CSS 构建 CSSOM,两者结合生成 Render Tree,然后再进行渲染。

推荐几篇值得收藏,反复阅读的文章:

https://developers.google.com…

https://developers.google.com…

https://developers.google.com…

https://developers.google.com…

https://developers.google.com…

内联 JS 的位置

本文仅仅讨论在一般的情况下,内联 JS 应该放在哪个位置。首先,下面两点可以算是【共识】:

  1. JS 尽量放在 HTML 底部。
  2. CSS 尽量放在 HTML 头部。

JS 放在底部是为了防止阻塞 DOM 的解析,CSS 放在头部是为了尽早完成 CSSOM 的构建,从而尽早 paint 页面。下面是一个常见的页面的 HTML 代码:

<html>
    <head>
        <link href="xxx.css" rel="stylesheet">
    </head>
    <body>
          <main>....</main>
        <script src="xxx.js"></script>
    </body>
</html>

假如我们有一段内联的、需要提前加载的、JS 统计逻辑代码,很自然的,会把内联 JS 放在 head 里。那么是放在 css 前面,还是后面呢?这是本文将要阐述的问题。我做了两个实验来看看两个位置下的页面渲染流程。为了突出实验效果,我模拟的是 3g 下的网速。

内联 JS 在 CSS 之前

代码:

<html>
    <head>
          <script type="text/javascript">
            for (var i = 0; i < 100000000; i++) {}
        </script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap-grid.css" rel="stylesheet">
    </head>
    <body>
        <p>hello word_1</p>
        <p>hello word_2</p>
        <p>hello word_3</p>
        <p>hello word_4</p>
        <p>hello word_5</p>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    </body>
</html>

timeline 如下:

eventlog 如下:

从上面两个图可以看到,开始解析 HTML 后,会开始执行内联 JS,并且发起网络请求下载 CSS 和 JS 文件,当 CSS 下载完成后,便开始进入渲染。

内联 JS 在 CSS 后

代码:

<html>
    <head>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap-grid.css" rel="stylesheet">
        <script type="text/javascript">
            console.log(document.getElementById, window);
            for (var i = 0; i < 100000000; i++) {}
        </script>
    </head>
    <body>
        <p>hello word_1</p>
        <p>hello word_2</p>
        <p>hello word_3</p>
        <p>hello word_4</p>
        <p>hello word_5</p>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    </body>
</html>

timeline 如下:

eventlog 如下:

从上面两个图可以看到,浏览器开始解析 HTML 后,会发起网络请求下载 JS 和 CSS,当 CSS 下载完成后,才会继续执行内联 JS,页面的渲染被推迟了,推迟的时间就是内联 JS 的执行时间。

实验小结
  1. 浏览器解析 HTML 的时候,会对整个 HTML 进行『preload scanner』,提前发起网络请求,下载外链资源,并不是解析到对应标签才会进行下载。而外链下载这个过程是由浏览器进程的网络线程完成的,不会阻塞当前渲染器进程的主线程逻辑。
  2. JS 会阻塞 HTML 的解析。内联 JS 在 CSS 前面的时候,执行内联 JS 可以和网络请求并行执行,执行完内联 JS 后会继续解析 HTML,并且在 CSS 文件下载完成后,选择合适的时机进行渲染。而内联 JS 在 CSS 后面的时候,由于考虑到内联 JS 可能会修改 CSSOM,所以主线程会暂停,直到 CSS 下载完成,生成 CSSOM 后,才会继续执行内联 JS、解析 HTML。

结论

对于一些全局函数,全局统计逻辑等,我们往往会以内联 JS 的形式放在 HTML 内的 head 里。此时,最好把内联 JS 放在外链 CSS 之前,以提高首屏速度,至少不会拖慢首屏速度。

写在后面

本文聚焦探索了内联 JS 在 HTML 内的最佳位置。网上关于页面渲染流程的文章很多,而自己去亲自实践后,才能更好的理解整个渲染流程。符合预期。


正文完
 0