Kevin-moon

学习在于分享
随笔 - 34, 文章 - 3, 评论 - 485, 阅读 - 21万
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

JQuery另类视角-动态执行脚本的BUG

Posted on   Kevin-moon  阅读(3580)  评论(5编辑  收藏  举报

  最近再用JQuery写一些东西,昨天突然出了个很奇怪的问题:

  我通过Ajax请求服务器上的数据(包括html和script),然后将请求的数据动态加载到页面的一个div中,这其实是个很简单的程序,首次执行是很顺利,但是当你执行这个相同动作两次之后,html元素依然可以顺利加载,不过script脚本就失效了!

  使用的是JQuery的1.4.1版本,加载的程序是用JQuery中的$(...).append()方法。类试代码如下:

$("#testdiv").append("<script type='text/javascript'> a = 1 ;alert(a);" + "<" + "/script>");

  你执行后会发现,前两次能很成功的弹出提示框,但是从第三次开始就失效了!

  为什么?javascript本身不可能不支持这种程序的,难道是JQuery程序的BUG吗?!是的,该BUG已经在1.4.2版本中进行了修复,你可以换成JQuery-1.4.2版本来试下。真是郁闷,这个问题害的我查了一个晚上才发现.....

  下面让我们分析下这个BUG的原因:

   首先看下append函数中主要的执行流程,

  

 

  执行script脚本的程序是在domManip函数中,大概的逻辑如下:

  导致该问题的函数是buildFragment函数,对比下该函数在这两个版本的代码,

复制代码
jquery-1.4.1
function buildFragment( args, nodes, scripts ) {
    var fragment, cacheable, cacheresults, doc;

    
// webkit does not clone 'checked' attribute of radio inputs on cloneNode, so don't cache if string has a checked
    if (args.length === 1 && typeof args[0=== "string" && args[0].length < 512 && args[0].indexOf("<option"< 0 
        
&& (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
        cacheable 
= true;
        cacheresults 
= jQuery.fragments[ args[0] ];
        
if ( cacheresults ) {
            
if ( cacheresults !== 1 ) {
                fragment 
= cacheresults;
            }
        }
    }

    
if ( !fragment ) {
        doc 
= (nodes && nodes[0? nodes[0].ownerDocument || nodes[0] : document);
        fragment 
= doc.createDocumentFragment();
        jQuery.clean( args, doc, fragment, scripts );
    }

    
if ( cacheable ) {
        jQuery.fragments[ args[
0] ] = cacheresults ? fragment : 1;
    }

    
return { fragment: fragment, cacheable: cacheable };
}
复制代码
复制代码
jquery-1.4.2
function buildFragment( args, nodes, scripts ) {
    var fragment, cacheable, cacheresults,
        doc 
= (nodes && nodes[0? nodes[0].ownerDocument || nodes[0] : document);

    
// Only cache "small" (1/2 KB) strings that are associated with the main document
    
// Cloning options loses the selected state, so don't cache them
    
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
    
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
    if ( args.length === 1 && typeof args[0=== "string" && args[0].length < 512 && doc === document &&
        
!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {

        cacheable 
= true;
        cacheresults 
= jQuery.fragments[ args[0] ];
        
if ( cacheresults ) {
            
if ( cacheresults !== 1 ) {
                fragment 
= cacheresults;
            }
        }
    }

    
if ( !fragment ) {
        fragment 
= doc.createDocumentFragment();
        jQuery.clean( args, doc, fragment, scripts );
    }

    
if ( cacheable ) {
        jQuery.fragments[ args[
0] ] = cacheresults ? fragment : 1;
    }

    
return { fragment: fragment, cacheable: cacheable };
}
复制代码

   仔细看完这两段代码后,会发现这个函数内设置scripts的程序是jQuery.clean( args, doc, fragment, scripts ),这行代码在执行之前有个判断:如果该传入的脚本数据允许支持缓存,并且数据在缓存jQuery.fragments中存在,那么这行代码是跳过的!否则,该行代码执行,设置scripts数据。

  现在把关注点放到"允许支持缓存"这句话上,比较这两个版本对于该程序的不同。!rnocache.test( args[0] ),这是1.4.2版本中多加的一行代码。终于找到它,它是这个BUG解决的关键所在。让我们看下这个rnocache的正则表达式:

rnocache = /<script|<object|<embed|<option|<style/i,

 

   看了这么多发现原因了吗!原来1.4.1版本会对script脚本进行缓存,一旦script缓存在脚本后,该脚本中的程序是不会执行的,而1.4.2多加了上面的正则,目的就是对script等这些数据不进行缓存,只要存在就会去执行!

 

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示