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

来源:互联网 发布:挚爱梵高 知乎 编辑:程序博客网 时间:2024/05/22 00:45

前言

我们经常会用到$(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的介绍将会在后续文章中进行阐述。

相关资料

  • setTimeOut()定时器强大作用
  • .ready( handler ) 中文API
  • jQuery3.1.1.js源码下载
阅读全文
1 0
原创粉丝点击