欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

css与javascript文件的载入顺序

shiping1 的头像

css与javascript文件的载入顺序

正文索引

本文属于高性能web前端优化系列

通常优秀的网页,都是把css与JS文件独立的,以保持文档结构清晰。这时候载入css与JS文件的顺序,就值得思考一下了。

让我们从实际情况出发,分析一下。

首先,网页对CSS的依赖非常大,如果html载入了而CSS没载入,那网站就会面目全非,所以,CSS必须在html之前载入

html的结构都是在body中,所以css想在他前面的话,就必须在head中载入——这也是所有人都毫不怀疑的。

那么javascript文件呢?该什么时候载入?

我们先想想JS可能会做些什么。

网页中的JS一般用来制作特效,或者动态载入数据,但不论他做什么,最终都会操作DOM元素(即html标签),以便把他的工作成果展示出来。

如果JS执行的时候,html结构没加载出来,那么JS就会因为找不到要操作的元素而报错。

所以,理论上讲,JS应该在html之后加载。

但Javascript是一门很灵活的语言,他可以先载入,再执行,比如,先在head中载入,然后在body之后执行代码,或者在window.onload事件里执行。这样也不会报错。

所以很多人也会把JS放在head中载入。

这种方式一般不需要担心什么,但存在潜在隐患。

因为,大部分浏览器,在载入JS的时候,都不能同时载入其他元素。

这个同时载入是指,比如网页中有CSS与很多图片,这些图片都能同时载入,而css也能和图片一起载入,不会出现必须将一个载入完成才能继续载入下一个的情况。

但很遗憾,javascript就不是这样,在JS载入之时,其他的元素都必须等待。

为什么会这样?我们想想JS的意义,他会操作html和css,浏览器会等待他操作完成后,看看有没有变化再继续——虽然JS并不一定会在载入的时候就操作网页,但浏览器会假定他会。

那么两个JS文件是否可以同时载入呢?也不能,因为两个JS可能互相关联而又冲突,可能A.js需要B.js执行了,才能执行自己,否则就报错。

浏览器对JS文件的处理,可以说很不灵活,而且不人性化,所以一些现代浏览器已经尝试同步载入JS与其他文件了,比如chrome。但其他浏览器可没有这么智能。

可以说浏览器对外来的javascript文件,存在着一种“未知恐惧”。因为JS本来就是未知的,变数太多。

特别是有些人比如我,喜欢在JS里面搞些奇奇怪怪的东西,什么闭包啊,循环引用啊,有的可能出错,有的可能拖慢浏览器执行效率。

当JS载入缓慢时,他后面的元素就会等待。

所以,你知道了吧,最好把JS放在html后面载入,这样,即使JS最终没载入起,也不会影响网页的外观。

当然,现在的web技术日新月异,JS已经发展到用来异步载入网页结构的程度了,此时的JS就必须在html之前载入,但这种情况很少,像淘宝这样流量巨大无比的网站才常用。

最后提醒一句:如果你决定把CSS与JS都放在头部,请确保JS是在CSS的后面。

本文链接: css与javascript文件的载入顺序.转载请保留.
来自 http://www.zhihu.com/question/20531965
 

 

 

阻塞特性

浏览器对javascript的处理主要有2部分:下载和执行

  • 下载在有些浏览器中是并行的,有些浏览器中是串行的,如IE8、Firefox3、Chrome2都是串行下载的
  • 执行在所有浏览器中默认都是阻塞的,当js在执行时不会进行html解析等其它操作

阻塞特性:

javascript有个阻塞特性,当浏览器执行javascript代码时,不能同时做其它任何事情。无论当前javascript代码是内嵌还 是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。浏览器在下载和执行脚本是进出现阻塞的原因在于,脚本可能会改变页面或 javascript的命名空间,它们对后面页面内容造成影响。

一、脚本位置

浏览器在碰到一个引入外部javascript文件的<script>标签时会停下所有工作来下载并解析执行它,在这个过程中,页面渲染和用户交互完全被阻塞了。例:

<html>  
<head>  
    <title>无标题文档</title>  
    <link rel="stylesheet" type="text/css" href="styles.css" />
    <script type="text/javascript" src="file1.js"></script>  
    <script type="text/javascript" src="file2.js"></script>  
    <script type="text/javascript" src="file3.js"></script>  
</body> 
</head>  
<body>  
    <p>页面的内容。。。</p>
</body>
</html>

由于脚本的阻塞特性,页面会在3个javascript文件全部下载执行完成后,页面才会继续渲染,把脚本放在页面顶部会导致明显延迟,通常表现为显示空白页,用户无法浏览内容,也无法与页面交互。

ie8+、firefox 3.5+、safari4+、chrome2+都允许并行下载javascript文件,但是在下载的过程中仍然会阻塞图片等其它资源的下载。

由于脚本会阻塞页面其它资源的下载,因此推荐将javasrcipt尽量放到body标签的底部,以减少对整个页面下载的影响。

二、组织脚本

由于<script>标签在下载时会阻塞页面的渲染,所以减少<script>标签数量有助于改善这一情况。建议将多个javascript文件合并为一个,这样可以减少性能的消耗。同时也可以减少请求的数量。

参考:在服务端合并和压缩javascript和CSS文件

三、无阻塞脚本

1、延迟脚本

HTML4 为<script>标签定义了一个defer 属性,它能使这段代码延迟执行,然而该属性只有IE4+支持,因此它不是一个理想的跨浏览器解决方案。声明了defer 属性的script会在DOM加载完成,window.onload 事件触发前被解析执行:

<html>  
<head>  
    <title>script defer example</title>  
</body> 
</head>  
<body>
<script defer>
    alert('defer');
</script>
<script>
    alert('script');
</script>
<script>
    window.onload = function(){
        alert('load');
    }
</script>
</body>
</html>

这段代码在支持defer属性的浏览器弹出顺序是:script、defer、load;不支持defer属性的浏览器弹出的顺序是defer、script、load。

2、动态脚本元素

<script type="text/javascript">
function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //for ie
        script.onreadystatechange = function() {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //other browser
        script.onload = function() {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}
</script>

loadscript函数用法

<script type="text/javascript">
    //单个文件
    loadScript('file1.js', function(){
        alert('loaded!');
    });


    //多个文件
    loadScript('file1.js', function(){
        loadScript('file2.js',function(){
            loadScript('file3.js', function(){
                alert('all files loaded!');
            });
        });
    });
</script>

这种技术的重点在于:无论何时启动下载,文件的下载和执行过程不会阻塞页面其它进程,你甚至可以将代码放在页面的head区域而不影响页面的其它部分(下载该文件的http链接除外)。

3、XMLHttpRequest 脚本注入

此技术会先创建一个XHR对象,然后用它下载javascript文件,最后创建动态的script元素将代码注入到页面中。

<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open('get', 'file1.js', true);
xhr.onreadystatechange = function() {
    if (xhr.status >= 200 && xhr.status <300 || xhr.status == 304) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.text = xhr.responseText;
        document.body.appendChild(script);
    }
};
xhr.send(null);
</script>

这种方法优点是可以直接下载javascript代码但不立即执行。由于代码是在<script>标签之外返回的,因此下载后不会自动 执行,这使得是可以把脚本推迟到你准备好的时候。这种方法的局限性在于javascript文件必须与所请求的页面处于相同的域,这意味着 javascript文件不能从cdn下载,因此不适合大型网站或项目。

四、推荐的无阻塞加载方式

1、YUI3的方式

2、LazyLoad(1.5k)

Yahoo!Search工程师Ryan Grove创建的一个通用的延迟加载工具,是loadScript()函数的增强版。

用法示例:

<script type="text/javascript" src="lazyload-min.js"></script>
<script type="text/javascript">
    LazyLoad.js('the-reset.js', function(){
        Application.init();
    });
</script>

LazyLoad同样支持多个javascript文件,并能保证在所有浏览器中都可以按正确的顺序执行。要加载多个javscript文件,只需要给LazyLoad.js()y方法传入一个url数组:

<script type="text/javascript" src="lazyload-min.js"></script>
<script type="text/javascript">
    LazyLoad.js(['first.js', 'the-reset.js'], function(){
        Application.init();
    });
</script>

项目地址:https://github.com/rgrove/lazyload

3、LABjs(4.7k)

LABjs是Kyle Simpson受Steve Sounders的启发实现的无阻塞加载工具。用法示例:

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
    $LAB.script('the-reset.js')
        .wait(function(){
            Application.init();
        });
</script>

$LAB.script()方法用来定义需要下载的javascript文件,$LAB.wait()用来指定文件下载并执行完毕后所调用的函数。

要下载多个javscript文件,只需链式调用另一个$LAB.script()方法:

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
    $LAB.script('first.js')
        .script('the-reset.js')
        .wait(function(){
            Application.init();
        });
</script>

LABjs与众不同的是它管理依赖关系的能力。通常来说,连续的<script>标签意味着文件逐个下载并按顺序执行。

LABjs允许使用wait()方法来指定哪些文件需要等待其它文件。上面的例子中first.js不能保证会在the-reset.js的代码前执行,为了确保这一点,必须在第一个script()方法后调用wait():

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
    $LAB.script('first.js').wait()
        .script('the-reset.js')
        .wait(function(){
            Application.init();
        });
</script>

项目地址:http://labjs.com/

4、SeaJS(7.5k)

SeaJS 是淘宝玉伯开发的一个遵循 CommonJS 规范的模块加载框架,可用来轻松愉悦地加载任意 javascript 模块。详细请参考:http://seajs.com/docs/

5、do 框架(3.5k)

Do是豆瓣网kejun开发的一个很轻量的Javascript开发框架。目前do.min.js。它的核心功能是对模块进行组织和加载。加载采取并行异步队列的策略,并且可以控制执行时机。Do可以任意置换核心类库,默认是jQuery。

项目地址:https://github.com/kejun/Do

6、RequireJS(13.1k)

项目地址:http://requirejs.org/
来自 http://wlog.cn/javascript/javascript-load-optimization.html

普通分类: