-
asp.net教程之用ASP和JavaScript实现网页上的动态分级
作者: 王欣
一、动态分级目录及其特点
所谓分级目录,其实大家并不陌生,熟悉Windows操作系统的人,一定对Windows下文件系统的资源管理器有印象,分级目录的显示(如下图所示)就象资源管理器一样是一个树形结构。
通过点击“文件夹”图标,你就可以看到这个类别下的子类别,如此递归直到最末梢的结点。
这种分级目录在许多网络应用中都很重要,它主要有以下优点:
1.直观性强:即使一个不了解计算机,不经常上网的人通过这种分级目录显示也不会在超文本的海洋中迷失方向,而能够方便地找到自己要去的地方。
2.便于管理:对于系统的编写和维护人员而言,这种结构也更有利于程序模块化的实现。
总之,一个界面友好的网页或应用程序是离不开分级目录的,目前许多网站也在自己的页面上采用了这种形式,但是大多数的分级目录都是静态的,即它们都是用单纯的javascript或html写成的,因此当网页的内容有所变化时,必须修改网页的源程序代码,这样做的弊端是显而易见的,但最主要的缺点是给页面的维护人员增加了许多不必要的工作量,降低了工作的效率,因此在这里主要介绍一种用ASP技术和javascript来实现动态分级目录的方法。动态分级目录,即网页上所显示分级目录的各个结点是以一定的数据结构存储在数据库中的,因此就可以通过对数据库进行相应的增、删、改操作来控制网页上的显示内容,达到方便、直观、快捷的目的。
用一句话来概括动态分级目录的特点,那就是在充分发扬原有静态分级目录优点的基础上弥补了它在灵活性和管理维护性上的不足。
二、系统总体介绍
1.系统环境
服务器:WindowsNT,安装有IIS5.0或IIS6.0,Oracle8数据库,ODBC
用户端:任何操作系统,装有TCP/IP协议,有Internet浏览器(如IE、NC)
2.相关技术:ASP(Active Server Pages),Oracle数据库,SQL语言,JavaScript,HTML,CSS等。
3.相关开发工具:DreamWeaver,PhotoShop等。
4.相关的数据结构
文件类别名称数据库:law_type_name
字段名称 字段含义
Typekey 类别关键字(主键)
Typename 类别名称
F_point 前向指针,指示该类别上一级类别的关键字
B_point 后向指针,指示该类别的下属是类别还是具体文件
0:说明其下属也是类别,在本库中寻找
1:说明其下属为具体文件,应在法律法规数据库
(law_rule)中寻找
法律法规数据库:law_rule
字段名称 字段含义
id 文件序号(主键)
Title 文件标题
Content 文件内容
Pubdate 发布日期
Puborg 发布单位
Regdate 登记日期
Update_date 最后修改时间
Typekey 文件所属类别
动态分级目录的数据结构决定了它主要由两种不同的节点构成,一种是具体的文档节点,一种是复合型的文件夹节点。文档节点是动态分级目录的神经末梢,当浏览者单击这样的节点时,相当于单击一个超级链接,浏览器会在右边的帧中自动装入相应类别的法律法规文件供用户查询。文件夹节点下可以有若干个子节点,这些子节点可以是文档节点,也可以是文件夹节点,这样就可以形成一个完整的分级目录了。
动态分级目录中的每一个节点都是一个拥有4+n个元素的数组,其中记录了节点的类型、名称、当前状态以及从属关系等信息,其中各个元素的含义如下所示:
node[0]=0/1 代表这个节点是关闭还是打开的,0代表关闭,1代表打开;
node[1]=0/1 代表这个文件夹是关闭还是打开的,0代表关闭,1代表打开;
node[2] 暂时没有意义;
node[3] 代表该节点的名称;
node[4]......node[n] 代表了该节点的n个子节点。
总之,一个动态分级目录的数据结构可用以下的树形结构来表示:
三、相关算法和函数说明
当将default.asp这个页面载入浏览器时,首先调用initializetree()函数来完成动态分级目录的创建和显示。这个函数进一步调用由ASP生成的generatetree()函数,该函数通过ADO从头到尾扫描law_type_name中的数据,并由此生成包含所有节点的数组,在数组已经存在的基础上调用Redrawtree()等函数就可以递归地在页面上显示分级目录了。
下面是对主要用到的一些函数的详细介绍:
generatetree()函数,利用ASP创建每个节点的数组,并建立起各节点之间的联系,程序代码如下:
<%
set myconn=server.createobject("adodb.connection")
//创建ADO的connection对象的实例myconn
set rs=server.createobject("adodb.recordset")
//创建ADO的recordset对象的实例rs
rs.open "select * from law_type_name order by
typekey",myconn,adopenstatic //将law_type_name中的数据赋给rs
cr=chr(13)&chr(10)//回车换行符
response.write("<script languang=Javascript>"&cr)
response.write("function generatetree(){"&cr)
response.write("var aux;"&cr)
response.write("aux=new Array();"&cr)
i=1
while not rs.eof //开始逐个扫描law_type_name中的数据
if rs("f_point")=0 then //生成最根本的节点,其他节点都是它的子孙
response.write("folderstree=foldernode('"
&trim(rs("typename"))&"');"&cr)
else
if rs("b_point")=0 then //若该节点为文件夹节点
if rs("f_point")=1 then //若该节点是根的直接子节点
response.write("aux["&rs("typekey")&"]=appendchild
(folderstree,foldernode('"&trim(rs("typename"))&"'));"&cr
//加入文件夹节点
else
response.write("aux["&rs("typekey")&"]=
appendchild(aux["&rs("f_point")&"],
foldernode('"&trim(rs("typename"))&"'));"&cr
//加入文件夹节点
end if
else
if rs("f_point")=1 then
response.write("appendchild(folderstree,generatedocentry(0,'"&
trim(rs("typename"))&"','',"&trim(rs("typekey"))&"));"&cr)
//加入文档节点
else
response.write("appendchild(aux["&rs("f_point")&"],
generatedocentry(0,'"&trim(rs("typename"))&"','',"
&trim(rs("typekey"))&"));"&cr) //加入文档节点
end if
end if
end if
rs.movenext
wend
response.write("}")
response.write("</script>")
rs.close //释放对象资源
myconn.close
%>
//******************************
//下面是构造节点的辅助函数
function foldernode(name) //用来生成文件夹节点
{
var arrayaux;
arrayaux=new Array();
arrayaux[0]=0;
arrayaux[1]=0;
arrayaux[2]=0;
arrayaux[3]=name;
return arrayaux;
}
function generatedocentry(icon,docdescription,link,num)
//用来生成文档节点
{
var retstring="";
retstring="<img src='../law_rule/pic/doc.gif'";
//用来显示文档节点前的小图标
retstring=retstring+"border=0>"+" "+"<td nowrap>"+
"<a href=typeinquest.asp?typekey="+num+
"target='folderframe'>"+docdescription+"</a>"+" ";
//显示带超链接的文档节点
return retstring;
}
function appendchild(parent,child)
//建立文件夹节点与文档节点之间的关系
{
parent[parent.length]=child;
return child;
}
//*************************************
//当网页载入浏览器时调用这个函数
function initializetree() //页面的初始化
{
generatetree();
top.openbranch("法律法规"); //显示根节点
redrawtree(); //显示整个动态分级目录
}
//**************************************
//显示动态分级目录
function redrawtree()
{
var doc=top.treeframe.window.document;
//doc代表了显示动态分级目录的分页面
doc.clear();
doc.writeln("<head>");
doc.writeln("<stype>");
doc.writeln("<!--");
doc.writeln("a:link{text-decoration:none; color:blue;font-size:9pt;
font-family:宋体; line-height:11pt;}");//页面的个性设置
doc.writeln("-->");
doc.writeln("</style>");
doc.writeln("</head>");
doc.writeln("<body bgcolor='#ffffdd'>");
redrawnode(folderstree,doc,0,1,"");
doc.close();
}
function redrawnode(foldersnode,doc,level,lastnode,leftside)
//具体显示一个节点
{
var j=0;
var i=0;
doc.writeln("<table boder=0 cellspacing=0 celpadding=0>");
doc.writeln("<tr height=15><td valign=bottom alig=left nowrap>");
doc.writeln(leftside);
if(level>0)
if(lastnode)
{
doc.writeln("<img src='../law_rule/pic/line0.gif'
width=50 height=16>");
leftside=leftside+"<img src='../law_rule/pic/blank.gif'
width=50 height=16>";
}
else
{
doc.writeln("<img src='../law_rule/pic/line1.gif'
width=50 height=16>");
leftside=leftside+"<img src='../law_rule/pic/vertline.gif'
width=50 height=16 align=left>";
}
displayiconandlabel(foldersnode,doc);
doc.writeln("</table>");
if(foldersnode.length>4&&foldersnode[0])
//如果该节点是文件夹节点且已被打开
{
if(!foldersnode[2])
{
level=level+1;
for(i=4;i<foldersnode.length;i++)
if(i==foldersnode.length-1)
redrawnode(foldersnode[i],doc,level,1,leftside);
else
redrawnode(foldersnode[i],doc,level,0,leftside);
}
else
{
for(i=4;i<foldersnode.length;i++)
{
doc.writeln("<table border=0 cellspacing=0
cellpadding=0 valign=center>");
doc.writeln("<tr height=15><td valign=bottom
align=left nowrap>");
doc.write(leftside);
if(i==foldersnode.length-1)
doc.writeln("<img src='../law_rule/pic/line0.gif'
width=50 height=16>");
else
doc.writeln("<img src='../law_rule/pic/line1.gif'
width=50 height=16>");
doc.write(foldersnode[i]);
doc.write("</td></tr>");
doc.writeln("</table>");
}
}
}
}
function displayiconandlabel(foldersnode,doc)
{
if(foldersnode[0]<2)
{
doc.writeln("<a href='' onClick='top.openbranch(\""+foldersnode[3]
+"\"; return false;'target=_self><img src=");
if(foldersnode[1])
{
doc.writeln("../law_rule/pic/minus.gif
width=18 height=16 border=0></a>");
doc.writeln("<img src=../law_rule/pic/pad.gif
width=18 height=16 border=0>");
}
else
{
doc.writeln("../law_rule/pic/plus3.gif width=18
height=16 border=0></a>");
doc.writeln("<img src=../law_rule/pic/pad.gif width=18
height=16 border=0>");
}
doc.writeln("<td valign=bottom align=left nowrap>");
doc.writeln("<span class='p1'>"+foldersnode[3]+"</span>");
}
else
{
doc.writeln("<td valign=bottom align=left nowrap>");
doc.writeln(foldersnode);
}
}
//**********************************************
//事件处理函数
//当用户单击一个文件夹节点时调用这个函数
function openbranch(branchname)
{
clickonfolderrec(folderstree,branchname);
if(branchname=="法律法规"&&folderstree[0]==0)
{
top.folderframe.location="basefolder2.htm";
}
timeoutid=setTimeout("redrawtree()",100);
//一定时间后自动刷新页面,重画动态分级目录
}
//递归函数用来打开和关闭节点及其子节点
function clickonfolderrec(foldersnode,foldername)
{
var i=0;
if(foldersnode[3]==foldername)
{
if(foldersnode[0]) //如果该节点是打开的
closefolders(foldersnode);//将其关闭
else
{
foldersnode[0]=1;
foldersnode[1]=1; //打开该文件夹和文档
}
}
else
{
if(!foldersnode[2]) //如果该节点的孩子是文件夹节点
for(i=4;i<foldersnode.length;i++)
if(foldersnode[0]<1) //判断节点的类型,文档或文件夹
clickonfolderrec(foldersnode[i],foldername);
}
}
//递归函数用来关闭节点及其子节点
function closefolders(foldersnode)
{
var i=0;
if(!foldersnode[2])
{
for(i=4;i<foldersnode.length;i++)
if(foldersnode[0]<1) //判断节点的类型,文档或文件夹
closefolders(foldersnode[i]);
}
foldersnode[0]=0;
foldersnode[1]=0;
}
四、结束语
同样的算法只需将个别语句稍加改动还可以利用Jsp+JavaBean在Java系统平台上实现动态分级目录。以上算法和源程序已在网站www.e_fdc.com中得到了实现,感兴趣的话可以访问该站点并对本文的不妥之处提出您宝贵的意见。
一、动态分级目录及其特点
所谓分级目录,其实大家并不陌生,熟悉Windows操作系统的人,一定对Windows下文件系统的资源管理器有印象,分级目录的显示(如下图所示)就象资源管理器一样是一个树形结构。
通过点击“文件夹”图标,你就可以看到这个类别下的子类别,如此递归直到最末梢的结点。
这种分级目录在许多网络应用中都很重要,它主要有以下优点:
1.直观性强:即使一个不了解计算机,不经常上网的人通过这种分级目录显示也不会在超文本的海洋中迷失方向,而能够方便地找到自己要去的地方。
2.便于管理:对于系统的编写和维护人员而言,这种结构也更有利于程序模块化的实现。
总之,一个界面友好的网页或应用程序是离不开分级目录的,目前许多网站也在自己的页面上采用了这种形式,但是大多数的分级目录都是静态的,即它们都是用单纯的javascript或html写成的,因此当网页的内容有所变化时,必须修改网页的源程序代码,这样做的弊端是显而易见的,但最主要的缺点是给页面的维护人员增加了许多不必要的工作量,降低了工作的效率,因此在这里主要介绍一种用ASP技术和javascript来实现动态分级目录的方法。动态分级目录,即网页上所显示分级目录的各个结点是以一定的数据结构存储在数据库中的,因此就可以通过对数据库进行相应的增、删、改操作来控制网页上的显示内容,达到方便、直观、快捷的目的。
用一句话来概括动态分级目录的特点,那就是在充分发扬原有静态分级目录优点的基础上弥补了它在灵活性和管理维护性上的不足。
二、系统总体介绍
1.系统环境
服务器:WindowsNT,安装有IIS5.0或IIS6.0,Oracle8数据库,ODBC
用户端:任何操作系统,装有TCP/IP协议,有Internet浏览器(如IE、NC)
2.相关技术:ASP(Active Server Pages),Oracle数据库,SQL语言,JavaScript,HTML,CSS等。
3.相关开发工具:DreamWeaver,PhotoShop等。
4.相关的数据结构
文件类别名称数据库:law_type_name
字段名称 字段含义
Typekey 类别关键字(主键)
Typename 类别名称
F_point 前向指针,指示该类别上一级类别的关键字
B_point 后向指针,指示该类别的下属是类别还是具体文件
0:说明其下属也是类别,在本库中寻找
1:说明其下属为具体文件,应在法律法规数据库
(law_rule)中寻找
法律法规数据库:law_rule
字段名称 字段含义
id 文件序号(主键)
Title 文件标题
Content 文件内容
Pubdate 发布日期
Puborg 发布单位
Regdate 登记日期
Update_date 最后修改时间
Typekey 文件所属类别
动态分级目录的数据结构决定了它主要由两种不同的节点构成,一种是具体的文档节点,一种是复合型的文件夹节点。文档节点是动态分级目录的神经末梢,当浏览者单击这样的节点时,相当于单击一个超级链接,浏览器会在右边的帧中自动装入相应类别的法律法规文件供用户查询。文件夹节点下可以有若干个子节点,这些子节点可以是文档节点,也可以是文件夹节点,这样就可以形成一个完整的分级目录了。
动态分级目录中的每一个节点都是一个拥有4+n个元素的数组,其中记录了节点的类型、名称、当前状态以及从属关系等信息,其中各个元素的含义如下所示:
node[0]=0/1 代表这个节点是关闭还是打开的,0代表关闭,1代表打开;
node[1]=0/1 代表这个文件夹是关闭还是打开的,0代表关闭,1代表打开;
node[2] 暂时没有意义;
node[3] 代表该节点的名称;
node[4]......node[n] 代表了该节点的n个子节点。
总之,一个动态分级目录的数据结构可用以下的树形结构来表示:
三、相关算法和函数说明
当将default.asp这个页面载入浏览器时,首先调用initializetree()函数来完成动态分级目录的创建和显示。这个函数进一步调用由ASP生成的generatetree()函数,该函数通过ADO从头到尾扫描law_type_name中的数据,并由此生成包含所有节点的数组,在数组已经存在的基础上调用Redrawtree()等函数就可以递归地在页面上显示分级目录了。
下面是对主要用到的一些函数的详细介绍:
generatetree()函数,利用ASP创建每个节点的数组,并建立起各节点之间的联系,程序代码如下:
<%
set myconn=server.createobject("adodb.connection")
//创建ADO的connection对象的实例myconn
set rs=server.createobject("adodb.recordset")
//创建ADO的recordset对象的实例rs
rs.open "select * from law_type_name order by
typekey",myconn,adopenstatic //将law_type_name中的数据赋给rs
cr=chr(13)&chr(10)//回车换行符
response.write("<script languang=Javascript>"&cr)
response.write("function generatetree(){"&cr)
response.write("var aux;"&cr)
response.write("aux=new Array();"&cr)
i=1
while not rs.eof //开始逐个扫描law_type_name中的数据
if rs("f_point")=0 then //生成最根本的节点,其他节点都是它的子孙
response.write("folderstree=foldernode('"
&trim(rs("typename"))&"');"&cr)
else
if rs("b_point")=0 then //若该节点为文件夹节点
if rs("f_point")=1 then //若该节点是根的直接子节点
response.write("aux["&rs("typekey")&"]=appendchild
(folderstree,foldernode('"&trim(rs("typename"))&"'));"&cr
//加入文件夹节点
else
response.write("aux["&rs("typekey")&"]=
appendchild(aux["&rs("f_point")&"],
foldernode('"&trim(rs("typename"))&"'));"&cr
//加入文件夹节点
end if
else
if rs("f_point")=1 then
response.write("appendchild(folderstree,generatedocentry(0,'"&
trim(rs("typename"))&"','',"&trim(rs("typekey"))&"));"&cr)
//加入文档节点
else
response.write("appendchild(aux["&rs("f_point")&"],
generatedocentry(0,'"&trim(rs("typename"))&"','',"
&trim(rs("typekey"))&"));"&cr) //加入文档节点
end if
end if
end if
rs.movenext
wend
response.write("}")
response.write("</script>")
rs.close //释放对象资源
myconn.close
%>
//******************************
//下面是构造节点的辅助函数
function foldernode(name) //用来生成文件夹节点
{
var arrayaux;
arrayaux=new Array();
arrayaux[0]=0;
arrayaux[1]=0;
arrayaux[2]=0;
arrayaux[3]=name;
return arrayaux;
}
function generatedocentry(icon,docdescription,link,num)
//用来生成文档节点
{
var retstring="";
retstring="<img src='../law_rule/pic/doc.gif'";
//用来显示文档节点前的小图标
retstring=retstring+"border=0>"+" "+"<td nowrap>"+
"<a href=typeinquest.asp?typekey="+num+
"target='folderframe'>"+docdescription+"</a>"+" ";
//显示带超链接的文档节点
return retstring;
}
function appendchild(parent,child)
//建立文件夹节点与文档节点之间的关系
{
parent[parent.length]=child;
return child;
}
//*************************************
//当网页载入浏览器时调用这个函数
function initializetree() //页面的初始化
{
generatetree();
top.openbranch("法律法规"); //显示根节点
redrawtree(); //显示整个动态分级目录
}
//**************************************
//显示动态分级目录
function redrawtree()
{
var doc=top.treeframe.window.document;
//doc代表了显示动态分级目录的分页面
doc.clear();
doc.writeln("<head>");
doc.writeln("<stype>");
doc.writeln("<!--");
doc.writeln("a:link{text-decoration:none; color:blue;font-size:9pt;
font-family:宋体; line-height:11pt;}");//页面的个性设置
doc.writeln("-->");
doc.writeln("</style>");
doc.writeln("</head>");
doc.writeln("<body bgcolor='#ffffdd'>");
redrawnode(folderstree,doc,0,1,"");
doc.close();
}
function redrawnode(foldersnode,doc,level,lastnode,leftside)
//具体显示一个节点
{
var j=0;
var i=0;
doc.writeln("<table boder=0 cellspacing=0 celpadding=0>");
doc.writeln("<tr height=15><td valign=bottom alig=left nowrap>");
doc.writeln(leftside);
if(level>0)
if(lastnode)
{
doc.writeln("<img src='../law_rule/pic/line0.gif'
width=50 height=16>");
leftside=leftside+"<img src='../law_rule/pic/blank.gif'
width=50 height=16>";
}
else
{
doc.writeln("<img src='../law_rule/pic/line1.gif'
width=50 height=16>");
leftside=leftside+"<img src='../law_rule/pic/vertline.gif'
width=50 height=16 align=left>";
}
displayiconandlabel(foldersnode,doc);
doc.writeln("</table>");
if(foldersnode.length>4&&foldersnode[0])
//如果该节点是文件夹节点且已被打开
{
if(!foldersnode[2])
{
level=level+1;
for(i=4;i<foldersnode.length;i++)
if(i==foldersnode.length-1)
redrawnode(foldersnode[i],doc,level,1,leftside);
else
redrawnode(foldersnode[i],doc,level,0,leftside);
}
else
{
for(i=4;i<foldersnode.length;i++)
{
doc.writeln("<table border=0 cellspacing=0
cellpadding=0 valign=center>");
doc.writeln("<tr height=15><td valign=bottom
align=left nowrap>");
doc.write(leftside);
if(i==foldersnode.length-1)
doc.writeln("<img src='../law_rule/pic/line0.gif'
width=50 height=16>");
else
doc.writeln("<img src='../law_rule/pic/line1.gif'
width=50 height=16>");
doc.write(foldersnode[i]);
doc.write("</td></tr>");
doc.writeln("</table>");
}
}
}
}
function displayiconandlabel(foldersnode,doc)
{
if(foldersnode[0]<2)
{
doc.writeln("<a href='' onClick='top.openbranch(\""+foldersnode[3]
+"\"; return false;'target=_self><img src=");
if(foldersnode[1])
{
doc.writeln("../law_rule/pic/minus.gif
width=18 height=16 border=0></a>");
doc.writeln("<img src=../law_rule/pic/pad.gif
width=18 height=16 border=0>");
}
else
{
doc.writeln("../law_rule/pic/plus3.gif width=18
height=16 border=0></a>");
doc.writeln("<img src=../law_rule/pic/pad.gif width=18
height=16 border=0>");
}
doc.writeln("<td valign=bottom align=left nowrap>");
doc.writeln("<span class='p1'>"+foldersnode[3]+"</span>");
}
else
{
doc.writeln("<td valign=bottom align=left nowrap>");
doc.writeln(foldersnode);
}
}
//**********************************************
//事件处理函数
//当用户单击一个文件夹节点时调用这个函数
function openbranch(branchname)
{
clickonfolderrec(folderstree,branchname);
if(branchname=="法律法规"&&folderstree[0]==0)
{
top.folderframe.location="basefolder2.htm";
}
timeoutid=setTimeout("redrawtree()",100);
//一定时间后自动刷新页面,重画动态分级目录
}
//递归函数用来打开和关闭节点及其子节点
function clickonfolderrec(foldersnode,foldername)
{
var i=0;
if(foldersnode[3]==foldername)
{
if(foldersnode[0]) //如果该节点是打开的
closefolders(foldersnode);//将其关闭
else
{
foldersnode[0]=1;
foldersnode[1]=1; //打开该文件夹和文档
}
}
else
{
if(!foldersnode[2]) //如果该节点的孩子是文件夹节点
for(i=4;i<foldersnode.length;i++)
if(foldersnode[0]<1) //判断节点的类型,文档或文件夹
clickonfolderrec(foldersnode[i],foldername);
}
}
//递归函数用来关闭节点及其子节点
function closefolders(foldersnode)
{
var i=0;
if(!foldersnode[2])
{
for(i=4;i<foldersnode.length;i++)
if(foldersnode[0]<1) //判断节点的类型,文档或文件夹
closefolders(foldersnode[i]);
}
foldersnode[0]=0;
foldersnode[1]=0;
}
四、结束语
同样的算法只需将个别语句稍加改动还可以利用Jsp+JavaBean在Java系统平台上实现动态分级目录。以上算法和源程序已在网站www.e_fdc.com中得到了实现,感兴趣的话可以访问该站点并对本文的不妥之处提出您宝贵的意见。
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式