jQuery源码阅读(七)--init()遗留部分buildFragment()函数

本文主要是介绍jQuery源码阅读(七)--init()遗留部分buildFragment()函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在 jQuery源码阅读(五)—init函数中,已经分析了init函数逻辑的大头,即参数selector为字符串的形式,但这里边仍然有两个为深入看的方法,一个是当selector是复杂标签的形式时,调用的bildFragment()方法,另一个是当selector为各种选择器时,调Sizzle模块的find()方法。

这一篇先来看buildFragment()函数的源码,分析该函数在处理参数为复杂标签时是如何做的?至于Sizzle模块的find()方法,打算后面阅读Sizzle选择器部分的源码时再来分析整理。

buildFragment()用法

要分析一个函数怎么实现的,首先得知道这个函数是干嘛的。所以,我们先来看看buildFragment函数是用来干什么的?

//调jQuery的静态方法
jQuery.buildFragment( ['<ul><li>苹果</li><li>柠檬</li></ul>',], [document] )

先来看看函数返回什么?
这里写图片描述
返回了一个对象,有两个属性,一个是为布尔值的cacheable,另一个是为文碎片的fragment。

{cacheable: true,fragment: ducoment-fragment
}

再打开fragment看看
这里写图片描述
可以看到,fragment里面childNodes有一个元素ul, 然后在ul 里面childNodes又有两个li元素,看到这,我们大概就知道buildFragment这个函数的作用了,他就是将我们传入的包含有标签的字符串,转换成对应的文档碎片,并记录下来。

那么到底在源码里面是如何实现的?下来我们一步步分析。

buildFragment()源码

就用我们上面说的jQuery.buildFragment( ['<ul><li>苹果</li><li>柠檬</li></ul>',], [document] )这个例子对照着源码走一遍。

jQuery.buildFragment = function( args, nodes, scripts ) {//这几个变量,先不管,后面用到再分析var fragment, cacheable, cacheresults, doc,//第一个参数(数组)的第一个元素(这里我们例子中是'<ul><li>苹果</li><li>柠檬</li></ul>)first = args[ 0 ];// nodes may contain either an explicit document object, a jQuery collection or context object.// If nodes[0] contains a valid object to assign to doc//这里类似于我们之前的context,一般都是documentif ( nodes && nodes[0] ) {doc = nodes[0].ownerDocument || nodes[0];}// Ensure that an attr object doesn't incorrectly stand in as a document object// Chrome and Firefox seem to allow this to occur and will throw exception// Fixes #8950if ( !doc.createDocumentFragment ) {doc = document;}// 判断当前标签是否可缓存,可缓存就将之前的变量cacheable设为true,这是一个比较重要的标志,后面详细分析如何判断标签是否是可缓存的if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&first.charAt(0) === "<" && !rnocache.test( first ) &&(jQuery.support.checkClone || !rchecked.test( first )) &&(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {cacheable = true;//读取缓存中的该标签cacheresults = jQuery.fragments[ first ];if ( cacheresults && cacheresults !== 1 ) {fragment = cacheresults;}}if ( !fragment ) {  //fragment为空,有两种情况,一种是标签不可缓存,没有进入上一步的判读中;另一种是标签可缓存,但是还没有缓存,即第一次碰到这个标签//这种情况下,先创建一个文档片段,为了存放后面创建的DOM元素,接着去调jQuery.clean函数,这个后面再单独分析fragment = doc.createDocumentFragment();jQuery.clean( args, doc, fragment, scripts );}if ( cacheable ) {//如果可以缓存,存起来jQuery.fragments[ first ] = cacheresults ? fragment : 1;}//最后返回含有两个元素的对象,分别是代码片段和可否缓存标志return { fragment: fragment, cacheable: cacheable };
};

看完上面代码,可以看到主要的操作都是在jQuery.clean函数中进行的,下来我们看看jQuery.clean函数是如何做的?
还是先来看clean函数是干什么的?

jQuery.clean(['<ul><li>苹果</li><li>柠檬</li></ul>', '<div></div>'], [document])     //返回一个数组

这里写图片描述

可以看到,jQuery.clean函数将输入的含有标签的字符串转换成了DOM元素,并存在数组里面。

下来看看它的源码框架:

clean: function( elems, context, fragment, scripts ){var checkScriptType, script, j,ret = [];context = context || document;if ( typeof context.createElement === "undefined" ) {//这里处理了context为DOM元素,jQuery对象和其他情况context = context.ownerDocument || context[0] && context[0].ownerDocument || document;}//对每一个字符串进行处理for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {//元素为数字if ( typeof elem === "number" ) {elem += "";}//元素为undefined或者null或者''if ( !elem ) {continue;}//元素为字符串if ( typeof elem === "string" ) {}//在IE6/7中修正复选框单选按钮的选中状态var len;if ( !jQuery.support.appendChecked ) {if ( elem[0] && typeof (len = elem.length) === "number" ) {for ( j = 0; j < len; j++ ) {findInputs( elem[j] );}} else {findInputs( elem );}}if ( elem.nodeType ) {ret.push( elem );} else {ret = jQuery.merge( ret, elem );}}//元素为HTML代码片段if ( fragment ) {//去除掉script标签部分,其他标签部分插入文档流}return ret;
}

在clean函数源码中,主要是按照参数elems每个元素的类型来分类的,主要分了四大类:

对于数值类型: 转成字符串;
对于值为空的情况: 不做处理;
对于字符串,比较复杂,又要细分;
对于代码片段,将script标签部分分离出来,其他的标签插入到文档中;

下面主要分析参数为字符串时的源码:

if ( typeof elem === "string" ) {//正则表达式rhtml = /<|&#?\w+;/匹配标签,字符代码或字符代码if ( !rhtml.test( elem ) ) {//不是标签,创建文本节点elem = context.createTextNode( elem );} else {//匹配封闭标签//正则表达式rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig匹配除了自不封闭标签之外的标签//并调用字符串的replace方法,用匹配到的第一个分组和第二个分组来替换//比如:'<div/>'会被换成'<div></div>'elem = elem.replace(rxhtmlTag, "<$1></$2>");// 去除空格等,得到标签名var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),//获取最外层包裹元素,对option,legend,thead,tr,td,col,area特殊处理了//wrap 的格式是一个数组,[ 层级数 , 标签名(包含自带的上级标签), 封闭标签 ]wrap = wrapMap[ tag ] || wrapMap._default,depth = wrap[0], div = context.createElement("div"),safeChildNodes = safeFragment.childNodes,remove;// 将创建的div元素追加到html文档片段中if ( context === document ) {safeFragment.appendChild( div );} else {createSafeFragment( context ).appendChild( div );}// 将参数中的标签设为div的HTML文本,这样就将标签转换成了相应的DOM元素div.innerHTML = wrap[1] + elem + wrap[2];// 当elem为tr,td,option,legend等有默认父级标签时while ( depth-- ) {div = div.lastChild;}// 考虑IE浏览器中的tbody不兼容问题,这个暂时没有深究if ( !jQuery.support.tbody ) {var hasBody = rtbody.test(elem),tbody = tag === "table" && !hasBody ?div.firstChild && div.firstChild.childNodes :wrap[1] === "<table>" && !hasBody ?div.childNodes :[];for ( j = tbody.length - 1; j >= 0 ; --j ) {if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {tbody[ j ].parentNode.removeChild( tbody[ j ] );}}}if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );}elem = div.childNodes;//先获取到divd的子节点//再将初始创建的div元素删除掉,只保留后面追加的DOM元素if ( div ) {div.parentNode.removeChild( div );// Guard against -1 index exceptions in FF3.6if ( safeChildNodes.length > 0 ) {remove = safeChildNodes[ safeChildNodes.length - 1 ];if ( remove && remove.parentNode ) {remove.parentNode.removeChild( remove );}}}
}
}

今天主要就整理这么多,其中有些细节并没有完全掌握,不过对于buildFragment函数整体的框架已经有了了解。对于其中不具体或者不正确的部分,希望大家可以指出,欢迎批评指正!

这篇关于jQuery源码阅读(七)--init()遗留部分buildFragment()函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/959395

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能