【jQuery源码浅析】(五)--文档加载--$.ready

前言

我们经常会用到$(document).ready(fn)或者$(fn),可是,我们只是用这个函数来代替window.onload么?其实不是的,文档的加载除了DOM结构树的加载之外还包括其他外部资源如图片或脚本的加载,而所有资源的加载会触发window.onload函数,但我们不可能总等所有资源加载出来再执行接下来的代码,有时候加载的外部资源很大的时候,我们就可以先在DOM结构树加载完之后开始做事了,不用等其他资源加载完毕。

我们可以通过反复监听DOMContentLoaded的方式来实现类似ready的功能。但是$(document).ready(fn)实现的方式不是通过直接监听DOMContentLoaded达到目的的。

大多数浏览器提供了 DOMContentLoaded 事件形式的类似功能。 然而,jQuery的 .ready() 方法的不同之处在于它是一个重要并且有效的方法:在代码调用.ready( handler )之前,如果 DOM 已经准备就绪并且浏览器已经触发DOMContentLoaded,handler处理函数仍然会被执行。 相反,如果 DOMContentLoaded 事件侦听器在这个事件触发后才被添加进来,那么这个DOMContentLoaded 事件的处理程序将永远不会被执行。

有的人会问为什么一定要传个document进去,然而,这不是必然的,也可以写成$().ready(fn)或者人们常用的$(fn),document不是必然的,因为高版本的jQuery已经把document传进去了rootjQuery = jQuery( document ),更推荐写法$( handler )的简洁方式。

在jQuery 3.0 中,只建议使用第一种语法(愚人码头注:即 $( handler )); 其他语法仍然能正常工作,但已被标记为弃用(愚人码头注:将来的某个版本会被删除)。

核心源码

        var readyList = jQuery.Deferred();

3865    jQuery.fn.ready = function( fn ) {

            readyList
                .then( fn )

                // Wrap jQuery.readyException in a function so that the lookup
                // happens at the time of error handling instead of callback
                // registration.
                .catch( function( error ) {
                    jQuery.readyException( error );
                } );

            return this;
        };

思路分析

  1. 基本思想:实现文档加载完毕而不是所有资源加载完毕后立刻执行的函数。
  2. 简便写法:实现$(fn) === $().ready(fn) === $(document).ready(fn),由于跟传入的selector无关,只跟selector是否为function有关,因此应该使用简便写法。

源码分析

1)首先$().ready(fn)里面调用的应该是一个延迟对象Deferred或Promise,看源码可知是一个Deferred

        jQuery.Deferred() // jQuery.js加载时候就执行
            .then( fn ) // 先执行完deferred里面的内容在执行fn
            .catch( function( error ) { // 如果fn出错则用jQuery默认的ready异常处理方式
                jQuery.readyException( error );
            } )

        // jQuery使用的ready异常处理方式是保证DOM刷新完毕再把异常抛给window
        jQuery.readyException = function( error ) {
            window.setTimeout( function() {
                throw error;
            } );
        };

readyException表面上看是多此一举,可这恰恰可以避免异常
发现没有?ready里面与selector根本没有任何关系,所以不再推荐传入document了。

2)为什么创造一个Deferred出来就可以保证DOM肯定是完全加载的呢?原因是Deferred内部的then方法做了一个setTimeOut的操作,而已做了递归。所以保证了DOM肯定是完全加载,相当于监听了DOMContentLoaded

        process = special ?
            mightThrow :
            function() {
                ....
                if ( depth ) {  // 递归
                    process();
                } else {
                    ....
                    window.setTimeout( process ); // 等待DOMContentLoaded完成
                }
            }

所以调用$().ready()结果如下:
$().ready()

3)调用$(fn)就等于调用$().ready()

        root = rootjQuery = jQuery( document ); // 默认把document传进去了

        // selector是方法的话,就是我们平时写的$(fn)
        else if ( jQuery.isFunction( selector ) ) {
            return root.ready !== undefined ?
                root.ready( selector ) :  //ready还存在的时候就调用ready

                // Execute immediately if ready is not present
                selector( jQuery );  // ready不存在的时候就直接调用该方法(不需要等待),这就是我们常用的$(function($))
        }

最后总结

由jQuery的版本迭代情况来看,window.onload会逐渐被淘汰,随着网页的体积越来越大和外部资源越来越多,为了用户体验,这个方法基本上是不会使用的了。另外,DOMContentLoaded会逐渐被setTimeout取代。jQuery.Deferred的作用也会越来越强大,关于Deferred的介绍将会在后续文章中进行阐述。

相关资料

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值