我们今天来聊一聊关于JavaScript文件的引入位置的问题;大家在平时的Web开发中有没有想过这样一个问题,那就是我应该在文档的头部(也就是<head>标签内部里面)引入所需要的JavaScript文件还是应该在尾部(也就是</body>之前)引入所需要的JavaScript文件呢?今天我们就来深入的探究一下这个问题。

首先我们需要了解的一点就是,在浏览器渲染页面之前,它需要通过解析HTML标记然后构建DOM树。在这个过程中,如果解析器遇到了一个脚本(script),它就会停下来,并且执行这个脚本,然后才会继续解析HTML。如果遇到了一个引用外部资源的脚本(script),它就必须停下来等待这个脚本资源的下载,而这个行为会导致一个或者多个的网络往返,并且会延迟页面的首次渲染时间。

还有一点是需要我们注意的,那就是外部引入的脚本(script)会阻塞浏览器的并行下载,HTTP/1.1规范表明,浏览器在每个主机下并行下载的组件不超过两个(也就是说,浏览器一次只能够同时从同一个服务器加载两个脚本);如果你网站的图片是通过多个服务器提供的,那么按道理来说,你的网站可以一次并行下载多张图片。但是,当我们网站在加载脚本的时候;浏览器不会再启动任何其它的下载,即使这些组件来自不同的服务器。

看到这里,也许很多开发者都会想:既然把脚本(script)资源放在head里面是个不好的主意,并且可能会阻塞浏览器渲染页面;那我们是不是要把所有的JavaScript文件都放置到文档的底部呢?这个当然也是太过极端了,因为还是有一些情况需要我们在头部引用脚本的;到底是哪些情况需要我们这么做呢,下面我们来看看一些大公司的做法:

我们可以看到,这个搜索页面在头部加载了5个JavaScript文件(箭头标注的地方),其中两个JavaScript文件是内联的(inline),另外三个大家可以看到script标签上都添加了async属性,那这个属性是干嘛的呢?我们会在下面解释。

我们可以看到,百度也在头部引入了一些JavaScript文件,这些文件引入的方式与Google的做法差不多,都在引入外部资源的script标签上添加了async属性,除了第一个JavaScript文件没有那样做。

最后一个是facebook的首页,令我比较出乎意料的是,facebook的首页的头部引入了大量的脚本(script),大家可以看一下截图

不过基本上facebook的script标签上面都添加了async属性,下面我们先来来说一下script标签上面这个async属性的作用。

这个属性是HTML5给script新添加的属性,而且只适用于外部的JavaScript文件,如果在script标签上添加了这个属性,那么表明这个脚本资源就不再是同步加载的了,而是异步加载的,所以不会阻塞浏览器对页面的渲染。当然这个属性会存在一些兼容性问题,一些浏览器还未实现对这个属性的支持。

我们可以看到,虽然这些网站大部分的script标签(针对引入的外部文件)都添加了async属性,但是还是有一些script标签没有添加async属性,那就表示这些资源是同步加载执行的,在这里你可能会问,那这些资源为什么不使用异步加载呢?原因很大程度上是因为,这些脚本需要在浏览器渲染页面之前就执行的;比如Yahoo在Best Practices for Speeding Up Your Web Site中就指出,如果你的脚本中使用了document.write在页面中插入内容的话,那就不能够将这条脚本放置到文档的底部了。类似的还有weibo,weibo的head中也使用了一个要在页面渲染之前就执行的脚本,如下:

  1.  
    <script type="text/javascript">
  2.  
    try {document.execCommand("BackgroundImageCache", false, true);
  3.  
    } catch (e) {}
  4.  
    </script>
  5.  
     

还有百度首页的head中也有两条需要在页面渲染之前就执行的JavaScript文件:

  1.  
    <script data-compress="strip">
  2.  
    function h(obj){
  3.  
    obj.style.behavior='url(#default#homepage)';
  4.  
    var a = obj.setHomePage('//www.baidu.com/');
  5.  
    }
  6.  
    </script>
  7.  
    <script>window._ASYNC_START=new Date().getTime();</script>

还有一些比如Google和Baidu他们搜索页面同步加载的那些JavaScrip文件一些是为了在页面渲染之前做一些全局的处理(比如Google)添加了全局变量google。

还有的就是单纯的满足自己业务上的一些需求了,比如百度同步加载的那个JavaScript文件:

所以说,除了上面这些情况外,其它的情况下我们的脚本资源都需要放在文档的底部;当然这里还有一些需要我们注意的问题,首先,脚本加载的顺序很重要,比如如果你的脚本需要使用jQuery库,那么你就应该在加载你的脚本之前先加载jQuery库。其次,有些脚本是需要等到某些元素加载完成之后才可以执行的,那么你可以将你的脚本紧挨在那个元素的后面;还有一些元素是通过脚本动态创建的,所以它们也需要放在合适的位置。比如微博的:

如果使用过一些框架的脚手架你就会发现,这些框架打包后的那个index.html里面引入的外部JavaScript资源都是放在文档的底部的,并且它们也是按照顺序来的,vendor.js文件(项目使用的框架,库打包形成的文件)先引入,然后才是app.js文件(我们写的代码文件打包形成的),这就说明了引入脚本文件的顺序也是很重要的。

到现在为止,我们已经讨论了很多关于把JavaScript文件放在文档的头部还是尾部的原因,那么下面我们可以总结出一些加载JavaScript文件的最佳实践;

  • 对于必须要在DOM加载之前运行的JavaScript脚本,我们需要把这些脚本放置在页面的head中,而不是通过外部引用的方式,因为外部的引用增加了网络的请求次数;并且我们要确保内敛的这些JavaScript脚本是很小的,最好是压缩过的,并且执行的速度很快,不会造成浏览器渲染的阻塞。

  • 对于支持使用script标签的async和defer属性的浏览器,我们可以使用这两个属性;其中需要注意的点就是,async表示的意思是异步加载JavaScript文件,它的下载过程可以在HTML的解析过程中进行,加载完成之后立即执行这个文件的代码,执行文件代码的过程中会阻塞HTML的解析,它不保证文件加载的顺序。defer表示的意思是在HTML文档解析之后在执行加载完成的JavaScript文件,JavaScript文件的下载过程可以在HTML的解析过程中进行,它是按照script标签的先后顺序来加载文件的。更多详细的解释可以参考async vs defer attributes

到这里为止,整篇文章就算是结束了,如果你还想进一步的了解关于JavaScript文件加载的一些知识,可以看看这篇文章

插播一个提问我就想知道,知乎为什么不添加使用Markdown编辑答案和写文章的功能? - 知乎,提问好几天,木有人回答,不知道大家怎么看待这个问题的?

参考的一些资料:

.

JS在HTML文档引入位置的更多相关文章

  1. js介绍,js三种引入方式,js选择器,js四种调试方式,js操作页面文档DOM(修改文本,修改css样式,修改属性)

    js介绍 js运行编写在浏览器上的脚本语言(外挂,具有逻辑性) 脚本语言:运行在浏览器上的独立的代码块(具有逻辑性) 操作BOM 浏览器对象盒子 操作DOM 文本对象 js三种引入方式 (1)行间式: ...

  2. jQuery全屏滚动插件fullPage.js中文帮助文档API

    jQuery全屏滚动插件fullPage.js中文帮助文档API   发现了一个fullPage.js插件,于是百度了一下,还就是这个插件的作用,其实有很多网站都做了全屏滚动的特效,效果也很好看,今天 ...

  3. Ext JS 6学习文档-第7章-图表

    Ext JS 6学习文档-第7章-图表 使用图表 本章中将探索在 ExtJS 中使用不同类型的图表并使用一个名为费用分析的示例项目结束本章所学.以下是将要所学的内容: 图表类型 条形图 和 柱形图 图 ...

  4. Ext JS 6学习文档-第6章-高级组件

    Ext JS 6学习文档-第6章-高级组件 高级组件 本章涵盖了高级组件,比如 tree 和 data view.它将为读者呈现一个示例项目为 图片浏览器,它使用 tree 和 data view 组 ...

  5. Ext JS 6学习文档-第5章-表格组件(grid)

    Ext JS 6学习文档-第5章-表格组件(grid) 使用 Grid 本章将探索 Ext JS 的高级组件 grid .还将使用它帮助读者建立一个功能齐全的公司目录.本章介绍下列几点主题: 基本的 ...

  6. 获取div相对文档的位置

    获取div相对文档的位置,两个方法 经测试 document.getElementById("btn").getBoundingClientRect() 在IE6下有2像素的bug ...

  7. Sencha Cmd 6 和 Ext JS 6 指南文档(部分官方文档中文翻译)

    近期组织了几个程序员网友,正在翻译一部分官方的Sencha Cmd 6 和 Ext JS 6 指南文档. 眼下还没翻译完,大家能够先看看 Sencha Cmd 6 和 Ext JS 6 指南文档  ( ...

  8. Ext JS 6学习文档-第8章-主题和响应式设计

    Ext JS 6学习文档-第8章-主题和响应式设计 主题和响应式设计 本章重点在 ExtJS 应用的主题和响应式设计.主要有以下几点内容: SASS 介绍和入门 主题 响应式设计 SASS 介绍和入门 ...

  9. Ext JS 6学习文档-第4章-数据包

    Ext JS 6学习文档-第4章-数据包 数据包 本章探索 Ext JS 中处理数据可用的工具以及服务器和客户端之间的通信.在本章结束时将写一个调用 RESTful 服务的例子.下面是本章的内容: 模 ...

随机推荐

  1. Linux性能优化实战学习笔记:第十讲

    一.坏境准备 1.拓扑图 2.安装包 在第9节的基础上 在VM2上安装hping3依奈包 wget http://www.tcpdump.org/release/libpcap-1.9.0.tar.g ...

  2. SpringCloud-ZUUL网关Cookie被拦截

    在application.properties文件中添加配置(注意后面的值为空) zuul.sensitiveHeaders= org.springframework.cloud.netflix.zu ...

  3. oracle 数据库创建用户并授权

    oracle 数据库创建用户并授权 备注: userName 为用户名,123456 为密码 drop user userName cascade; create user userName iden ...

  4. Ajax 跨域请求,Chrome 无法显示 Set-Cookie

    在使用 Ajax 进行跨域请求时,前后端均已设置 withCredentials = true,但 Chrome 前端响应无法显示 Set-Cookie. 一开始以为 Cookie 并没有设置成功,但 ...

  5. WPF 页面导航

    <Button x:Name="btnReset" Click="btnReset_Click" Content="重 置" Grid ...

  6. sql server 分页总结

    1.第一种方式:使用 ROW_NUMBER() OVER(ORDER BY ID) …… BETWEEN AND 的方式SELECT * FROM( SELECT ROW_NUMBER() OVER( ...

  7. C# 中 ==和equals的区别

    不想说太多,直接上代码,这两个就没什么联系,有自己独立的规则.联系在一起其实不利于记忆. 下面是测试代码 Console.WriteLine("--equals和==的区别--") ...

  8. Dos.ORM修改数据遇到的问题

    2019年11月6日,今天使用Dos.ORM进行数据的批量修改,出现修改一条数据造成所有数据相应状态改变的情况,代码如下: 按照一步步调试的方式,排查出原因:生成的orm实体类缺少 主键 的标识,该原 ...

  9. SpringBoot项目解决全局响应返回中文乱码问题

    一.问题 新建的基于SpringBoot的MVC项目,在请响应体中,如果有中文,会显示为乱码. 二.解决方案 1.在application.properties中设置: spring.http.enc ...

  10. MySQL——数据库操作

    1.创建数据库 登录MySQL服务后,使用create命令创建数据库 # 登录MySQL进入终端 mysql -u root -p # 之后输入命令 create database database_ ...