- 浏览: 520903 次
- 性别:
- 来自: 北京
博客专栏
-
jQuery技术内幕
浏览量:200675
最新评论
-
青春依旧:
学习html5哪里好?当然华清远见是首选!
[原创] jQuery源码分析-01总体架构 -
追梦1819:
[size=x-small][color=red][/colo ...
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路 -
niuqiang2008:
学习学习
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理 -
liuweihug:
jquery 解析正则表达式及常见的Regex规则和表达式 - ...
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式 -
liang8768:
mark!!!
[原创] jQuery源码分析-00前言开光
作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com
声明:本文为原创文章,如需转载,请注明来源并保留原文链接。
读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF下载在最后。
7. 数据缓存 Cache
jQuery提供了jQuery.data()和jQuery.fn.data(),实现对缓存的操作。jQuery.fn.data()内部通过jQuery.data()实现。jQuery.data()有三种用法:
/** * jQuery.data( elem, key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题 * jQuery.data( elem, key ) 返回指定元素上name指定的值 * jQuery.data( elem ) 返回全部数据 */ |
7.1 实现思路
l 写入(引用jQuery.data()接口的三个参数:elem,key,value)
在匹配的DOM元素(elem)上附加一个唯一ID,在$.cache中添加同样的ID属性,该ID属性的值是一个对象,其中存储了key/value映射。
其中有几个关键点:
n 所有数据都存储在全局变量$.cache中
n 唯一ID是一个整型值,初始为0,调用data接口时自动加一,生成新的唯一ID
n 唯一ID附加在以$.expando命名的属性上,$.expando是动态生成的,类似于一个时间戳,以尽可能的避免与用户变量冲突
l 读取
从匹配的DOM元素(elem)上取到唯一ID,在$.cache中找到唯一ID对应的对象,再从对应的对象中找到key对应的值
7.2 验证
下面用代码验证一下(在火狐中用firebug调试):
// 查看存储唯一ID的属性名 $.expando // 返回 "jQuery161013869011191548697" // 在body节点上写入数据 $('body').data( 'a', 1 ) $('body').data( 'b', 2 ) // 读取body节点的唯一ID $('body')[0][ $.expando ] // 返回5 $('body')[0][ "jQuery161013869011191548697" ] // 返回5 // 读取数据 $.cache[5] // 返回 Object { a=1, b=2},验证成功! |
7.3 源码分析
7.3.1 jQuery.data
// 数据缓存
var rbrace = /^(?:\{.*\}|\[.*\])$/, // 花括号或方括号 rmultiDash = /([a-z])([A-Z])/g; // 驼峰写法,大小写之间会被插入破折号
jQuery.extend({ cache: {},
// Please use with caution uuid: 0, // 唯一id种子,DOM元素第一次调用data接口存储数据时,会用uuid++的方式,生成一个新的唯一id
// Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery // 页面上jQuery副本的唯一标识 // 非数字符号被移除以匹配rinlinejQuery expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
// The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. // // YunG: // 如果尝试在embed、object、applet上附加属性值,将会抛出未捕获的异常 // // embed: // embed标签用于播放一个多媒体对象,包括Flash、音频、视频等 // http://221.199.150.103/jsj/html/page/book/xhtml/m_embed.htm // // object: // object元素用于向页面添加多媒体对象,包括Flash、音频、视频等。它规定了对象的数据和参数,以及可用来显示和操作数据的代码。 // <object>与</object>之间的文本是替换文本,如果用户的浏览器不支持此标签会显示这些文本。 // object元素中一般会包含<param>标签,<param>标签可用来定义播放参数。 // http://221.199.150.103/jsj/html/page/book/xhtml/m_object.htm?F=14,L=1 // // <embed>和<object>标签的区别:两者都是用来播放多媒体文件的对象,object元素用于IE浏览器,embed元素用于非IE浏览器,为了保证兼容性,通常我们同时使用两个元素,浏览器会自动忽略它不支持的标签。同时使用两个元素时,应该把<embed>标签放在<object>标签的内部。 // // applet: // 不赞成使用 noData: { "embed": true, // Ban all objects except for Flash (which handle expandos) "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", "applet": true }, // 判断一个元素是否有与之关联的数据(通过jQuery.data设置),用在事件处理中 hasData: function( elem ) { // 如果是DOM元素,则从jQuery.cache中读取,关联jQuery.cache和DOM元素的id存储在属性jQuery.expando中 // 如果是非DOM元素,则直接从elem上取,数据存储在 jQuery.expando 属性中 // elem的属性jQuery.expando,要么值是id,要么值是要存储的数据 // elem被替换为所存储的数据 elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem ); }, /** * jQuery.data( elem, key, value ) 在指定元素上存储/添加任意的数据,处理了循环引用和内存泄漏问题 * jQuery.data( elem, key ) 返回指定元素上name指定的值 * jQuery.data( elem ) 返回全部数据 */ /** * pvt 私有的,是否是内部使用的独立对象,pvt为true时用于事件处理 */ data: function( elem, name, data, pvt /* Internal Use Only */ ) { // 是否可以附加数据,不可以则直接返回 if ( !jQuery.acceptData( elem ) ) { return; }
var internalKey = jQuery.expando, // 内部key? getByName = typeof name === "string", // name必须是字符串? thisCache,
// We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary // 必须区分处理DOM元素和JS对象,因为IE6-7不能垃圾回收对象跨DOM对象和JS对象进行的引用属性 isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically // 如果是DOM元素,则使用全局的jQuery.cache(为什么?DOM元素不能存储非字符串?无法垃圾回收?) // 如果是JS对象,则直接附加到对象上 cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache // 如果JS对象的cache已经存在,则需要为JS对象定义一个ID // 如果是DOM元素,则直接取elem[ jQuery.expando ],返回id(有可能是undefined) // 如果是JS对象,且JS对象的属性jQuery.expando存在,返回jQuery.expando(有可能是 undefined) id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
// Avoid doing any more work than we need to when trying to get data on an // object that has no data at all // 避免做更多的不必要工作,当尝试在一个没有任何数据的对象上获取数据时
// name是字符串,data未定义,说明是在取数据 // 但是对象没有任何数据,直接返回 // ?id不存在,说明没有数据;或者,id存在,但是属性internalKey不存在,也说明没有数据 // ?internalKey到底是干什么用的? if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { return; } // id不存在的话就生成一个 if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache // 只有DOM元素需要一个唯一ID,因为DOM元素的数据是存储在全局cache中的。 // 用uuid种子递增分配唯一ID if ( isNode ) { elem[ jQuery.expando ] = id = ++jQuery.uuid; } else { // JS对象则直接使用jQuery.expando,既然是直接附加到对象上,又何必要id呢? // 避免与其他属性冲突! id = jQuery.expando; } }
// 数据存储在一个映射对象中({}) if ( !cache[ id ] ) { cache[ id ] = {}; // 初始化存储对象
// 这是什么东东?既然是一个“恶作剧”,那就就忽略它,不深究。 // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery // metadata on plain JS objects when the object is serialized using // JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; } }
// An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache // data接口接收对象和函数,浅拷贝 if ( typeof name === "object" || typeof name === "function" ) { // 私有数据,存储在cache[ id ][ internalKey ]中 // 什么类型的数据算私有数据呢?事件处理函数,还有么? if ( pvt ) { cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); } else { cache[ id ] = jQuery.extend(cache[ id ], name); } } // 存储对象,存放了所有数据的映射对象 thisCache = cache[ id ];
// Internal jQuery data is stored in a separate object inside the object's data // cache in order to avoid key collisions between internal data and user-defined // data // jQuery内部数据存在一个独立的对象(thisCache[ internalKey ])上,为了避免内部数据和用户定义数据冲突 // // 如果是私有数据 if ( pvt ) { // 存放私有数据的对象不存在,则创建一个{} if ( !thisCache[ internalKey ] ) { thisCache[ internalKey ] = {}; } // 使用私有数据对象替换thisCache thisCache = thisCache[ internalKey ]; }
// 如果data不是undefined,表示传入了data参数,则存储data到name属性上 // 这里为什么要统一为驼峰写法呢? // 这里的问题是:如果传入的是object/function,不做转换,只有传入的name是字符串才会转换。 if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; }
// 又是一个hack,忽略它,不研究 // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should // not attempt to inspect the internal events object using jQuery.data, as this // internal data object is undocumented and subject to change. if ( name === "events" && !thisCache[name] ) { return thisCache[ internalKey ] && thisCache[ internalKey ].events; } // 如果name是字符串,则返回data // 如果不是,则返回整个存储对象 return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache; }, // 在指定元素上移除存放的数据 removeData: function( elem, name, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; }
var internalKey = jQuery.expando, isNode = elem.nodeType,
// See jQuery.data for more information cache = isNode ? jQuery.cache : elem,
// See jQuery.data for more information id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; }
if ( name ) { var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
if ( thisCache ) { delete thisCache[ name ];
// If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !isEmptyDataObject(thisCache) ) { return; } } }
// See jQuery.data for more information if ( pvt ) { delete cache[ id ][ internalKey ];
// Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject(cache[ id ]) ) { return; } }
var internalCache = cache[ id ][ internalKey ];
// Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care // 如果不支持在DOM元素上delete,设置为null if ( jQuery.support.deleteExpando || cache != window ) { delete cache[ id ]; } else { cache[ id ] = null; }
// We destroyed the entire user cache at once because it's faster than // iterating through each key, but we need to continue to persist internal // data if it existed // // 如果还有数据,就清空一次再设置,增加性能 if ( internalCache ) { cache[ id ] = {}; // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery // metadata on plain JS objects when the object is serialized using // JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; }
cache[ id ][ internalKey ] = internalCache;
// Otherwise, we need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist // 已经没有任何数据了,就全部删除 } else if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases // 如果支持delete,就删除。 // IE使用removeAttribute,所以尝试一次。再失败就只能设置为null了。 if ( jQuery.support.deleteExpando ) { delete elem[ jQuery.expando ]; } else if ( elem.removeAttribute ) { elem.removeAttribute( jQuery.expando ); } else { elem[ jQuery.expando ] = null; } } },
// For internal use only. // 内部使用 _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); },
// A method for determining if a DOM node can handle the data expando // YunG: // 判断一个DOM元素是否可以附加数据 acceptData: function( elem ) { if ( elem.nodeName ) { // embed object applet var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
if ( match ) { // return match === true // return elem.getAttribute("classid") !== match return !(match === true || elem.getAttribute("classid") !== match); } }
return true; } }); |
7.3.2 jQuery.fn.data
jQuery.fn.extend({ // 在匹配的元素上存储任意数据,解决了循环引用和内存泄漏(TODO)? // 支持HTML5 data-Attributes
// data( key, value ) data( object ) // 返回jQuery对象中指定key的值,或者返回含有全部数据的结合
// data( key ) data( ) // HTML5? http://api.jquery.com/data/ data: function( key, value ) { var data = null; // 守护方法,用于处理特殊的key // key是undefined,则认为是取当前jQuery对象中第一个元素的全部数据 if ( typeof key === "undefined" ) { if ( this.length ) { data = jQuery.data( this[0] ); // 如果是Element if ( this[0].nodeType === 1 ) { var attr = this[0].attributes, name; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name;
if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) );
dataAttr( this[0], name, data[ name ] ); } } } }
return data; // key是对象,则对当前jQuery对象迭代调用$.fn.each, // 在每一个匹配的元素上存储数据key } else if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); }
// 到这列,key是字符串 var parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; // 如果value为undefined,则任务是取当前jQuery对象中第一个元素指定名称的数据 if ( value === undefined ) { data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
// Try to fetch any internally stored data first // 优先取内部数据 if ( data === undefined && this.length ) { data = jQuery.data( this[0], key ); // 读取HTML5的data属性 data = dataAttr( this[0], key, data ); }
return data === undefined && parts[1] ? this.data( parts[0] ) : data; // key是字符串,value不是undefined,则存储 } else { return this.each(function() { var $this = jQuery( this ), args = [ parts[0], value ]; // 触发事件 $this.triggerHandler( "setData" + parts[1] + "!", args ); jQuery.data( this, key, value ); $this.triggerHandler( "changeData" + parts[1] + "!", args ); }); } },
removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); |
7.3.3 支持HTML5的data- 属性
// HTML 5 data- Attributes http://ejohn.org/blog/html-5-data-attributes/ // 如果在指定元素elem没有找到key对应的数据data,就尝试读取HTML5的data属性 function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : !jQuery.isNaN( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {}
// Make sure we set the data so it isn't changed later jQuery.data( elem, key, data );
} else { data = undefined; } }
return data; } |
- _原创__jQuery源码分析-07数据缓存-Cache.pdf (423.7 KB)
- 下载次数: 114
发表评论
-
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路
2011-11-14 20:59 6845作者:nuysoft/高云 QQ:47214707 Em ... -
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理
2011-11-13 23:45 7879作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式
2011-10-27 01:29 46630作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-jQuery中的循环技巧
2011-10-27 00:36 14545作者:nuysoft/JS攻城师/高云 QQ:47214707 ... -
[原创] jQuery源码分析-10事件处理-Event-DOM-ready
2011-10-20 01:20 10596作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-Java工程师应该向jQuery学习的8点建议
2011-10-18 23:56 12244作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
2011-10-18 22:31 15662作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-源码结构
2011-10-17 01:01 12115作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-06浏览器测试-Support
2011-10-13 19:19 9894作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析系列目录(持续更新)
2011-10-12 12:30 29348作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-10事件处理-Event-概述和基础知识
2011-10-12 00:16 12004作者:nuysoft/高云 Q ... -
[原创] jQuery源码分析-08队列 Queue
2011-10-10 23:48 10786作者:nuysoft/高云 Q ... -
[原创] jQuery源码分析-03构造jQuery对象-工具函数
2011-09-29 23:21 29776作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-15AJAX-类型转换器
2011-09-29 02:25 7400作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-03构造jQuery对象-源码结构和核心函数
2011-09-28 02:20 52955作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-如何做jQuery源码分析
2011-09-27 00:22 14994近期在ITEYE陆续写了几篇jQuery源码分析,乐在其 ... -
[原创] jQuery源码分析-17尺寸和大小 Dimensions & Offset
2011-09-25 22:05 7045边读边写,不正确的地方,还请各位告诉我,多多交流共同学习, ... -
[原创] jQuery源码分析-15AJAX-前置过滤器和请求分发器
2011-09-23 00:09 13272边读边写,不正确的 ... -
[原创] jQuery源码分析-00前言开光
2011-09-21 23:42 46997jQuery源码分析 - 前言 jQuery凭借简洁的语法和 ... -
[原创] jQuery源码分析-01总体架构
2011-09-21 23:31 751231. 总体架构 1.1 自调用匿名函 ...
相关推荐
《jQuery源码分析》 jQuery,作为一款广泛使用的JavaScript库,极大地简化了DOM操作、事件处理、动画制作以及Ajax交互。深入理解其源码对于提升JavaScript编程技能和优化前端性能至关重要。本文将从核心概念、设计...
### jQuery源码分析系列_1.6 #### 一、前言 在现代Web开发领域,jQuery无疑是一款具有里程碑意义的JavaScript库。它通过简洁、强大的API极大地简化了DOM操作、事件处理、Ajax交互以及动画等功能,使得前端开发变得...
**jQuery 1.2.6 源码分析教程** jQuery 是一个广泛使用的 JavaScript 库,因其简洁的语法和强大的功能而备受开发者喜爱。在 jQuery 1.2.6 版本中,虽然它已经相对老旧,但其背后的实现原理和优化策略仍然值得深入...
- 缓存机制:对于频繁访问的平均评分,可以考虑使用缓存技术(如Redis或ASP.NET的Output Cache),减少数据库访问压力。 - 异步处理:对于大量评分操作,可以采用异步处理,提高系统响应速度。 综上所述,这个...
10. **缓存策略**:提高性能可能通过缓存常见查询结果,如Redis或ASP.NET的Output Cache。 11. **部署与配置**:IIS或ASP.NET Core的Kestrel服务器用于运行应用程序,了解部署和配置策略也是重要的。 深入研究这个...
源码分析与工具 对于开发者来说,理解 AutoComplete 的源码可以帮助定制更复杂的功能。你可以下载提供的 `jquery.autocomplete.min.js` 文件,解压并查看源码。此外,利用调试工具(如 Chrome DevTools)可以帮助...
### ABP源码分析 #### 一、整体项目结构及目录 ABP作为一个高度模块化且灵活可扩展的Web应用程序框架,其设计思路和技术选型都极具参考价值。本章节将着重探讨ABP项目的整体结构及其各个组成部分的功能定位。 ###...
6. **数据缓存(Data Cache)**:为了提高性能,可以使用`data()`方法来存储和检索附加到元素的数据,避免重复查询DOM。 7. **事件委托(Event Delegation)**:如果动态添加或删除元素,使用事件委托如`on()`方法...
> .pytest_cache:系统缓存文件 > > migrations/:此目录包含Django应用程序的数据库迁移文件。每当您更改数据库表时,都应该创建一个新的迁移文件,并运行migrate命令来将更改应用于数据库。 > > app.py:此文件...
- 使用缓存(如Redis或ASP.NET内置的HttpRuntime.Cache)来减少数据库查询。 - 使用负载均衡和分布式架构,以应对高并发访问。 - 采用MVVM(Model-View-ViewModel)或MVC(Model-View-Controller)设计模式,提高...
#### 三、源码结构分析 NopCommerce 的源码结构清晰有序,主要分为以下几个部分: 1. **Libraries** - **Nop.Core**:包含基础核心类和接口,如领域对象、缓存等。 - **Nop.Data**:负责数据访问层的封装,其中...
通过指定哪些文件需要缓存,开发者可以创建一个离线可用的应用。 2. `index.html`:这是网页的主入口文件,包含了HTML结构,包括游戏的页面布局、JavaScript引用和CSS样式链接等。 3. `2017-02-17-00-01-21.jpg`:...
- **缓存机制**:ASP.NET提供了Output Cache等功能,可以缓存页面或部分页面,提高响应速度。 - **数据库优化**:可能采用索引、存储过程等方式优化数据库查询性能。 通过分析和理解"桃源网络硬盘.Net 5.4"的源码...
此外,HTML5还提供了离线存储(Web Storage)和离线应用程序缓存(App Cache)等功能,提高用户体验。 响应式设计是该项目的核心特性之一,它允许网页根据用户设备的不同屏幕尺寸和方向自动调整布局。通过使用媒体...
- **缓存策略**:提高性能,可能会使用ASP.NET的Output Cache或Memory Cache。 6. **学习价值** 这个源码实例对于学习ASP.NET开发电子商务系统非常有价值。通过阅读和分析源码,开发者可以了解如何整合多种ASP...
ASP.NET是一种由微软开发的服务器端Web应用程序框架,主要用于构建动态网站、Web应用程序和Web服务。这个源码包“ASP.NET源码——[聊天...开发者可以通过学习和分析此源码,加深对ASP.NET的理解,并提升实际开发技能。
- **缓存机制**:可能利用ASP.NET的Output Cache或其他缓存策略减少数据库查询,提高页面加载速度。 - **负载均衡**:对于大型电商平台,可能采用负载均衡技术分发用户请求,提升系统可用性。 通过分析这套源码,...
- 可能使用缓存技术,如Page Output Cache,减少数据库查询,提高响应速度。 学习这个源码,对于初学者来说,不仅可以理解ASP.NET的基础概念,还能了解到如何结合数据库进行实际应用开发。同时,它还提供了一个很...
【ASP.NET 源码分析—— TriptychBlog v.9.0.6】 ASP.NET 是微软公司开发的一款用于构建动态网站、Web 应用程序和Web 服务的框架。它基于.NET Framework,提供了一种高效、易于使用的编程模型,支持多种编程语言,...