zoukankan      html  css  js  c++  java
  • JQuery源码解析-Dom加载过程

    下面的几个工具方法都是和dom加载有关,所以先从dom加载开始。

    用到的方法:

      isReady:DOM是否已经加载完(内部使用)

      readyWait():等待多少文件的计时器(内部使用)

      holdReady()::推迟DOM触发

      ready():准备DOM触发

      jQuery.ready.promise = function( obj ) {}检测dom的异步操作

    先看一下jQuery和原生js加载方式有什么不同:

         $(function () {
            });
            window.onload = function () {
            };

    jQuery是等待页面中所有的dom加载完毕,而原生JavaScript的onload方法,则是等待页面所有的元素加载完毕,

    例如:一个img标签,jQuery只等这个img标签加载完毕,不必等到src中所引用的图片加载完毕,但onload方法,则必须等到src中的图片加载完毕才可以。

    下面看一下jQuery的加载流程图:

    前面已经说过  $(function () {})和rootjQuery.ready( selector )是相等的,也就是说$(function () {})这种形式写法,其实最后也被转换成rootjQuery.ready( selector )。

    而rootjQuery.ready( selector )这个实例方法里:

    ready: function( fn ) {
            // Add the callback
            jQuery.ready.promise().done( fn );
    
            return this;
        },

    又是调用的工具方法中的jQuery.ready.promise().done( fn ),所以先从这个方法开始分析,代码如下:

    jQuery.ready.promise = function( obj ) {
        if ( !readyList ) {
    
            readyList = jQuery.Deferred();
    
            // Catch cases where $(document).ready() is called after the browser event has already occurred.
            // we once tried to use readyState "interactive" here, but it caused issues like the one
            // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
            if ( document.readyState === "complete" ) {
                // Handle it asynchronously to allow scripts the opportunity to delay ready
                setTimeout( jQuery.ready );
    
            } else {
    
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", completed, false );
    
                // A fallback to window.onload, that will always work
                window.addEventListener( "load", completed, false );
            }
        }
        return readyList.promise( obj );
    };

    这个方法中有判断:

    首先判断如果document.readyState === "complete" ,那么已经代表了在运行到这时,页面中的dom已经加载完毕了,然后运行setTimeout( jQuery.ready )

    这里用到了setTimeout方法,是为了兼容ie的hack方法,具体原因可以看一下注释中的网址。

    如果readyState 不等于complete,那么就会添加两个回调方法,这里添加两个的原因,是因为在火狐等浏览器中,会缓存load方法,所以就会先调用load方法,所以这里添加了两个回调,保证了在第一时间内调用complete方法。

    下面在看一下complete方法中写了什么:

    // The ready event handler and self cleanup method
        completed = function() {
            document.removeEventListener( "DOMContentLoaded", completed, false );
            window.removeEventListener( "load", completed, false );
            jQuery.ready();
        };

    通过源码可以看到,这里无论哪种方法进行了回调,都会在这里将事件解绑,所以这里也就保证了只回调一次,最后又调用了jQuery.ready()方法。

    这也说明,其实在jQuery.ready.promise方法中,无论条件如何,都会调用jQuery.ready()方法。

    在说ready()方法前,先来说明一下holdReady方法。

    这个方法是阻塞dom加载的,看一下这个例子:

    a.js:

    alert(1);

    调用页面:

    $.getScript("a.js", function () {
            })
     $(function () {
      alert(2);
    });

    这时运行,查看结果发现,其实先弹出的是2,然后才是1,这显然和我们的预期不一致,一般我们在页面中引用js的时候,都是想使用这个js文件,然后在进行下面的操作。

    但因为是异步,所以当前的结果表明,是最后才运行了a.js中的内容。

    所以这时就用到了holdReady方法:

                $.holdReady(true);
    
                $.getScript("a.js", function () {
                     $.holdReady(false)
                })
                $(function () {
                   
                    alert(2);
                });

    这时页面的运行结果就正确了。

    源码:

    // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,
    
        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,
    
        // Hold (or release) the ready event
        holdReady: function( hold ) {
            if ( hold ) {
                jQuery.readyWait++;
            } else {
                jQuery.ready( true );
            }
        },

    可以看到,方法内部有个计数器,这也就说明了,当页面加载多个js文件时,可以进行多次调用,保证所有文件都加载完毕,才可以继续运行。

    如果传入的参数为false,则执行ready方法,并传入true:

    ready: function( wait ) {
    
            // Abort if there are pending holds or we're already ready
            if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
                return;
            }
    
            // Remember that the DOM is ready
            jQuery.isReady = true;
    
            // If a normal DOM Ready event fired, decrement, and wait if need be
            if ( wait !== true && --jQuery.readyWait > 0 ) {
                return;
            }
    
            // If there are functions bound, to execute
            readyList.resolveWith( document, [ jQuery ] );
    
            // Trigger any bound ready events
            if ( jQuery.fn.trigger ) {
                jQuery( document ).trigger("ready").off("ready");
            }
        },

    第一个判断,如果为true,则将计数器减一,否则判断isReady变量

    第二个判断,如果计数器还大于0的话,则继续等待。

    到readyList.resolveWith( document, [ jQuery ] ); 这句代码,就开始运行延时的方法了,第一个参数指定节点,第二个是将jQuery传入

    可以通过下面的代码查看:

     $(function (arg) {
                    console.log(this);
                    console.log(arg);
                })

    运行结果可以看到 this:document arg:jQuery对象。

    下面看最后一段代码

            // Trigger any bound ready events
            if ( jQuery.fn.trigger ) {
                jQuery( document ).trigger("ready").off("ready");
            }

    这段代码是考虑到另一种加载方式:

     $(document).on('ready', function () {
                    alert(1);
                })

    如果将这段代码注释,可以看到,这种方式就无法运行了。

    所以这也说明了,jQuery其实有三种写法:

             $(function () {})
                $(document).ready(function () { })
                $(document).on('ready', function () {})
  • 相关阅读:
    本地项目上传到github
    linux 常用命令
    mysql 查询日志基本操作
    js 短信60秒倒计时
    windows下 mysql 移库
    INSERT INTO table(xxx) VALUES (xxx)
    springboot 项目接口调用失败
    P1093 奖学金
    P1403约数研究
    P1147连续自然数和
  • 原文地址:https://www.cnblogs.com/y8932809/p/5869288.html
Copyright © 2011-2022 走看看