ScriptLoader断断续续也开发了半年多了, 很多朋友都觉得这样的项目花俏多于实用, 其实有一个很重要的功能一直都没有开发,那就是我在[OpenSource]ScriptLoaderV2:彻底改变你的编程习惯提到过的ClientCacheManager.
在开发中,每个人都或多或少的使用了Cache,为了减少数据库的负担,或者为了增加处理请求的速度.但是使用Cache也是一件很麻烦的事情,必须维护真实数据与缓存的同步,必须根据查询命中率判断Cache优先级有的放矢的删除低效缓存项,必须维护所有缓存对象的可序列化,必须维护服务器群集间的缓存同步......
只不过,这些缓存的处理大部分都是在Server端.在RIA横行的今天,我们是否忽视了成千上万的客户端?当Ajax+Json支撑起很多系统的时候,我们是否应该可以更好的利用Browser的缓存机制来减少请求的次数,降低资源的消耗,获得更快的速度?
客户端缓存的真正意义,在于减少网络请求,降低数据库查询,充分利用Server的内存,增加Application的吞吐量和请求处理效率,同时也可以提高客户端的响应速度,因为数据本来就存在客户端嘛^_^. 同时,缓存在客户端的东西,即使服务器重启亦可以持久保留.在现在这个大硬盘高内存为标配的PC时代,已经没啥人去手工清理IE缓存了,所以搞不好1年下来,它都一直用着同一个缓存呢:)
基于ScriptLoader的缓存控制原理,ScriptLoaderV5,将带入走入这样一个被忽视的世界,带给你一个简单但用处颇多的客户端分布式缓存平台.
插件接口
ScriptLoader在V5之前,只能算是一个Library,但现在它已经成为一个小的Framework,它同样可以管理外部插入的ScriptObject对象,并管理其版本的更新.
IPlugin的定义很简单.
{
public interface IPlugin
{
// Get the json data
string GetContent();
// Get the last modified datetime of the live data
DateTime GetLastModified();
}
}
公共方法:
为了对Plugin进行管理,ScriptLoader同样新增了几个方法
1.LoadPlugin
看得出来,跟以前版本中的LoadStylesheet一样,只是封装了下Load的方法而已
服务端:
{
Load(fileName + " .Plugin ");
}
客户端:
Load(name+".plugin");
}
2.RefreshPlugin:
手工更新某个指定的Plugin的数据和版本信息. 当然ScriptLoader还有一个自动的维护Plugin版本信息的轮询机制.
3.配置项:
Plugin版本更新的轮询间隔时间:如果不配置,默认是180000;//30 minutes
< add key ="VersionUpdateTime" value ="50000" />
</ appSettings >
Plugin部署配置
< sectionGroup name ="LangZi" >
< section name ="ScriptLoaders" type ="System.Configuration.NameValueSectionHandler" />
</ sectionGroup >
</ configSections >
< LangZi >
< ScriptLoaders >
< add key ="Locations" value ="ScriptLoader.Plugins.Location,ScriptLoader.Plugins" />
</ ScriptLoaders >
</LangZi>
< add verb ="GET" path ="*.plugin" type ="LangZi.Scripts.ScriptLoaderHandler,LangZi.ScriptLoader" />
</ httpHandlers >
应用场景演示:
我相信很多人都做过省市联动控件,举这样一个例子,每个人都会比较熟悉.所以我开发了一个演示性的Plugin:
Location.cs
using System.Collections.Generic;
using System.Text;
using LangZi.Scripts;
using System.Xml;
using System.Web;
using System.IO;
namespace ScriptLoader.Plugins
{
public class Location:IPlugin
{
#region IPlugin Members
public string GetContent()
{
StringBuilder data = new StringBuilder();
/*
var locations = [{Name:'FuJian',City:[{Name:'XiaMen'},{Name:'FuZhou'}] } ,{Name:'BeiJing',City:[{Name:'ChongWen'},{Name:'ChaoYang'}]}];
*/
data.Append( " var locations = [ ");
XmlDocument doc = new XmlDocument();
var filename = System.AppDomain.CurrentDomain.BaseDirectory+ " location.xml ";
doc.Load(filename);
XmlNode node = doc.SelectSingleNode( " /China ");
for( int i= 0;i<node.ChildNodes.Count;i++)
{
XmlNode item = node.ChildNodes[i];
if(i== 0)
{
data.AppendFormat( " {{Name:'{0}',City:[ ", item.Name);
for ( int n = 0; n < item.ChildNodes.Count; n++)
{
XmlNode city = item.ChildNodes[n];
if (n == 0)
{
data.AppendFormat( " {{Name:'{0}'}} ", city.Name);
}
else
{
data.AppendFormat( " ,{{Name:'{0}'}} ", city.Name);
}
}
data.Append( " ]} ");
}
else
{
data.AppendFormat( " ,{{Name:'{0}',City:[ ", item.Name);
for ( int n = 0; n < item.ChildNodes.Count; n++)
{
XmlNode city = item.ChildNodes[n];
if (n == 0)
{
data.AppendFormat( " {{Name:'{0}'}} ", city.Name);
}
else
{
data.AppendFormat( " ,{{Name:'{0}'}} ", city.Name);
}
}
data.Append( " ]} ");
}
}
data.Append( " ]; ");
return data.ToString();
}
public DateTime GetLastModified()
{
var filename = System.AppDomain.CurrentDomain.BaseDirectory + " location.xml ";
FileInfo fi = new FileInfo(filename);
return fi.LastWriteTime;
}
#endregion
}
}
Location.xml
< China >
< FuJian >
< FuZhou ></ FuZhou >
< XiaMen >
< SiMing ></ SiMing >
< HuLi ></ HuLi >
< TongAn ></ TongAn >
< JiMei ></ JiMei >
< HaiCang ></ HaiCang >
</ XiaMen >
< QuanZhou >
< AnXi ></ AnXi >
< NanAn ></ NanAn >
< YongChun ></ YongChun >
< JinJiang ></ JinJiang >
< ShiShi ></ ShiShi >
</ QuanZhou >
< ZhangZhou ></ ZhangZhou >
</ FuJian >
< Shanghai >
< HuangPuQu ></ HuangPuQu >
< PuTuoQu ></ PuTuoQu >
</ Shanghai >
< BeiJing >
< ChongWen ></ ChongWen >
< ChaoYang ></ ChaoYang >
</ BeiJing >
</ China >
消费Plugin:
{
LangZi.Scripts.ScriptLoader loader = LangZi.Scripts.ScriptLoader.RegisterInstance( this);
loader.LoadPlugin( " Locations ");
}
< head runat ="server" >
< title >Untitled Page </ title >
< script type ="text/javascript" >
function Get(id){
return document.getElementById(id);
}
function SelectedProvinceOnChange(value){
var currentProvince;
for (i = 0 ;i < locations.length;i ++ ){
if (locations[i].Name == value){
currentProvince = locations[i];
break ;
}
}
var cities = Get( " cities " );
for (i = cities.options.length - 1 ;i >- 1 ;i -- ){
cities.options.remove(i)
}
if (currentProvince != null ){
for (i = 0 ;i < currentProvince.City.length;i ++ ){
var option = new Option(currentProvince.City[i].Name);
cities.options.add(option);
}
}
}
function OnInit(){
var Provinces = Get( " provinces " );
for (i = 0 ;i < locations.length;i ++ ){
var option = new Option(locations[i].Name);
Provinces.options.add(option);
}
SelectedProvinceOnChange(locations[ 0 ].Name);
}
</ script >
</ head >
< body onload ="OnInit()" >
< form id ="form1" runat ="server" >
< div >
Province:
< select id ="provinces" onchange ="SelectedProvinceOnChange(this.options[this.selectedIndex].text)" >
</ select >
City:
< select id ="cities" >
</ select >
< asp:Button ID ="Button1" runat ="server" onclick ="Button1_Click" Text ="Button" />
</ div >
</ form >
</ body >
</ html >
远期计划
ScriptLoader开发到这个版本,已经接近我的思维极限了,后续的开发暂时找不出更大的突破,或许只是小修小补,比如对缓存的控制进一步精细,把目前的缓存控制周期进行分割,新增其他的周期,例如基于Page的周期(比Context长,但是比Session短,离开页面即失效).或者将ScriptObject的范畴扩展出去,包括任何Broswer可缓存的对象(image......).
当然,基于组件的应用,那可想像的空间还是很大的,但这不能算是ScriptLoader的原生功能.
所以说,如果到如今,你还找不到ScriptLoader在你的项目中的应用场景的话,那请忽略它,因为暂时我无能力再突破它,增加更新鲜的功能.
09年,或许是我开源的一年,如果你对ScriptLoader不感兴趣,可以关注我的另一个开源项目LangZi.QuickCMS,当然目前这个项目只完成了一个组件LangZi.Web.UrlEngine,如果对UrlRewriter比较在乎的朋友,小瞄一下或许你会有不一样的收获^_^.
资源下载
1.svn: https://scriptloader.svn.sourceforge.net/svnroot/scriptloader
2.packages: http://code.google.com/p/langzi/downloads/list