python 的框架基本上都是做了url跟action的映射配置文件
django的权限管理比较强大,如果有权限管理的可以用这个
比较轻量级的 tornado webpy
twisted 是比较全面的框架,比较庞大
有理义以养其心,则虽老而神明不衰。苟为不然,则昏于豢养,败于戕贼,未老而志衰矣。励志之士,可不戒诸。
《High Performance Web Sites》 :Chapter 6
法则6:把script放到页面的下端
《High Performance Web Sites》 :Chapter 5
法则5:将样式表放到HEAD中 Put Stylesheets at the Top
在Yahoo!,有一个小组在他们的项目中使用了一些DHTML特性。其中一个复杂的功能是是在发送邮件时会弹出一个DIV层。其实这个层并不影响其所在页面的渲染,因为它是功能触发式的被渲染的页面部分,所以这个小组的前端工程师就把这个弹出层的CSS样式写到一个独立的样式表文件中,并把对该CSS的引用放在页面的最下方的位置,以确保页面能较快的下载和渲染。
我们知一个页面是由图片,样式表,scripts等组件渲染成的,而组件是按它们在页面中出现的顺序依次下载,把DHTML的动态功能的样式表文件放到后面,可以让更关健的组件先下载,这样能使页面更快的渲染显示给浏览者,好像事实应该是这样,不是吗。
事实是这样吗?
其实采用上面的方式在IE下反而会更慢。经过不断尝试,我们还是把这个DHTML所需的样式表放到了页面的上方HEAD中,这时的页面载入是最快的。这好像和我们所期望的有点相反,为什么我们把暂不渲染页面的CSS放到页面的上方位置,延缓了其它关健组件的下载,反而是加快了页面的载入速度呢?这就是我们马上要开始介绍的法则5。
Progressive Rendering
逐步渲染
前端工程师总是希望他的页面能尽可能快的展示给用户,所以他们希望页面能够随着下载逐步渲染给用户,特别是在页面比较大或是用户使用较慢的网络连接时。有人曾经讨论过给用户一个视觉反馈的重要性,提出要用视觉反馈来作为衡量进度的指标:让用户实实在在的感受到页面一块一块的渲染出来和让用户等着一个空白页让各组件下载到本地再渲染,可能前一种方法的实际组件下载完的速度要慢,但用户会觉得这要快的多。
把样式表放到页面的下方,在一些浏览器中(包括IE)会阻止我们上面所期望的逐步渲染。因为浏览器如果发现页面中的下方有样式表的引用,就会不进行逐步的渲染以避免样式表的的变更所带来的重新渲染,所以这个时候用户可能会看到一个空白页(其实可能网速并不慢,服务响应也够快,但您还是等一个空白页的过程)。Firefox则不会这样。
在HTML规范中就清楚的说明了样式表要声明或引用在页面的HEAD中:”Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.”
下面我们用数据来分析:
CSS在页面的下方:http://stevesouders.com/hpws/css-bottom.php
CSS在HEAD中:http://stevesouders.com/hpws/css-top.php
CSS在HEAD中,并用 @import的方式引入:http://stevesouders.com/hpws/css-top-import.php
(注意,这种用@import引入的方式,即使您将css写在HEAD中,也会出现类似将css放在页面下方的情况,出现一个空白的页等待中)
以上的三个页面中,组件和个数都一样。看看下面的三个页面的组件下载时序图:
第一个和第三个,都是在最后才下载样式表,这样就会导致在IE中,页面在6.3秒前都是不会被渲染的,而第二个页面,会最先下载样式表,这时页面就会开始渲染了。
现在我们知道该怎么做了。把样式表以link文件的形式放进HEAD吧。
相关章节:
《High Performance Web Sites》 :Chapter 4
法则4:使用Gzip压缩组件 Gzip Components
前端开发工程师能力的好坏直接关系着页面的访问快慢(John:要知道,用极其丑陋的table套table做出的页面比简洁的div与table做出的页面无论是从K字节上,还是载入速度上都是极大的消耗),但另一些如用户的带宽等我们无法控制的因素,也会影响到用户访问您web应用的速度。法则1和法则3介绍了如何通过减少不必要的HTTP请求来提升页面加载时间,法则2介绍了用CDN,可以让我们离用户更近,但我们总不能把所有的HTTP请求都优化掉吧。于是,我们这法则4应潮流而生。
法则4,是通过减少HTTP响应的数据量来加快响应速度。要知道如果一个HTTP的响应足够少,那传输的时间多快呀,因为从服务器端传送到浏览器端的数据包小了,也少了,岂有不快之理。这对像那些网络不太好的用户可是特有效。这一章节就会展示如何通过gzip去压缩HTTP响应,这是减少数据量最easy的方法,但有得到就会有付出,gzip也会有一些不好的影响,这个后面介绍。
压缩是如何工作的
很久以前我们就把文件压缩应用在电子邮件和FTP站点当中。直到HTTP/1.1的规范制定,web客户端才开始通过HTTP请求中的 Accept-Encoding 头信息来来表示其支持压缩。
服务器要压缩什么是取决于文件格式。很多网站只压缩HTML文件,其实script和样式表文件的也都值得压缩(事实上,任何文本型的数据都可以压缩,包括XML和JSON数据)。图片和PDF文件的则不必再用gzip压缩,因为它们本身就是压缩格式,再多余的压缩只会浪费CPU资源,也得不到多少的文件压缩量。
gzip压缩需要消耗服务器端的CPU,而客户端也需要执行解压gzip格式的数据。至于是否值得压缩的问题,我觉得任何超过1K或2K的文件都值得压缩。mod_gzip_minimum_file_size 参数就可以配置您想要压缩的文件的大小范围,它的默认值是500 bytes.
老套路,我又调查了美国10大网站使用gzip的情况如下图。有9家都用gzip压缩他们的HTML文件,7家还用gzip压缩了script和样式表文件,其中有5家是完全压缩了所有的script和样式表。一个网站如果压缩所有的HTML文件,样式表,script文件,甚至可以减少70%的数据量,这是我们下面的段落里会提到的。
节省The Savingsgzip压缩后,返回的数据量一般会减少70%,如下图所示,显示了scrtip,样式表文件在压缩前和压缩后的数据量的变化,同时也显示了如果用deflate方式来压缩的数据量对比。
很显然,从上图中就知道我们为什么要选择gzip了。gzip会减少约66%的数据量,而deflate只会减少约60%。
配置 Configuration
配置gzip的模块要取决于您Apache的版本:Apache 1.3使用的是mod_gzip,而 Apache 2.x 使用 的是mod_deflate。这一段落就会讲解如何配置每个模块。
Apache 1.3: mod_gzip
Apache1.3的gzip压缩功能是通过 mod_gzip 模块来现实的。mod_gzip 有很多的配置参数,这些在mod_gzip的站点上有说明,在这里我介绍一些最常用的参数。
mod_gzip_on
开启mod_gzip.
mod_gzip_item_include
mod_gzip_item_exclude
定义要被gzip压缩或不压缩的文件的类型,MIME类型, user agent, 等等。
大部分的web服务器都会打开mod_gzip,并把text/html设为默认文件类型。最重要的是您应该为script与样式表文件也打开gzip功能。您可以参考下面对Apache 1.3 的配置:
Apache 2.x 是通过mod_deflate 模块来实现压缩。尽管它的名字是deflate,但实际它是实现的gzip压缩方式(John:怪异吧!)。如果要实现像对script和样式表的压缩这种基本的配置只需要下面这一行就够了:
与mod_gzip不同,mod_deflate可以通过参数配置来控制压缩级别。要获取更多的信息,可查阅Apache 2.0 mod_deflate的文档。
代理缓存 Proxy Caching
以上介绍的简单配置对直接访问web服务器的方式来说没有一点问题,web服务器通过请求中的Accept-Encoding头信息来判断客户端是否可以支持压缩的数据,然后把压缩或没压缩的数据再返回给客户端,这些都是通过HTTP的header头信息来实现的。
但如果用户是通过代理上网的,那用户的请求也都是通过代理服务器中转发送的,这时就比较麻烦了:假如用户发请求给代理服务器时,并表明其不支持gzip,而代理服务器再中转数据时却将已经缓存过的压缩的数据返回给用户,这时就会造成用户看到的是一堆乱码,反之亦然。
要解决这个问题则要在服务器端加上vary头信息。mod_gzip默认是为所有的响应都增加Vary:Accept Encoding头信息,这样代理服务器就会缓存压缩和未压缩版的数据了。
特殊情况 Edge Cases
目前90%的浏览器都支持gzip。但还是会有一些特殊情况我们不得不考虑,如在早期的IE版本中,特别是IE5.5和IE6.0 sp1存在一些bug,一种比较安全的做法是用”浏览器白名单(browser whitelist)“,让服务器端只针对一部分浏览器发送gzip格式的数据。如下的设置就是只针对IE6~9的版本和Mozila 5~9的版本发送gzip数据。
Apache 1.3使用User-Agent:
而针对代理缓存,一种方法是在Vary的头信息中加上User-Agent标识来告诉代理服务器,我们使用了白名单;在mod_gzip模块中,如果它发现配置了白名单,则会自动为Vary加上上User-Agent标识。但是不要指望代理服务器会缓存所有的浏览器白名单的每一个url副本。所以我们不得不采用另一个方法:为返回的响应中加上Vary : * 或 Cache-Control:private 头信息,以表明完全禁止代理服务器的缓存,不让代理去缓存任何一个组件,让用户通过代理时,都要去请求真正的服务器以获得页面数据,事实上Google和Yahoo!都是使用的这一策略,尽管它会带来更多的流量消耗。
还有一个特殊情况要指出,就是ETags(我们将会在Chapter 13章节单独提到),默认情况下它都不会被压缩,所以最好的办法就是干脆禁用ETags,这个还是放在Chapter 13章节再详谈吧。
例
下面三个链接分别是没有gzip压缩,只压缩了HTML和压缩了全部组件的三个链接页面:
Nothing Gzipped
http://stevesouders.com/hpws/nogzip.html
HTML Gzipped
http://stevesouders.com/hpws/gzip-html.html
Everything Gzipped
http://stevesouders.com/hpws/gzip-all.html
下图就是三者的具体数据量对比和时间对比:
Adobe的Ajax框架spry的正式版还没放出,所以文档是少之又少,在这里接合自己使用的情况总结20个spry的知识点给大家,相信会对大家有一定帮助,至少大家也会对spry有个初步的认识了,这个轻量型的框架就一个字:“易用”。
1,使用spry框架,必须引用的两个核心js文件
<script type="text/javascript" src="../../includes/xpath.js"></script> <script type="text/javascript" src="../../includes/SpryData.js"></script>
2,创建一个数据器dataset
var dsPhotos = new Spry.Data.XMLDataSet("/photos.php", "/gallery/photos/photo", { method: "POST", postData: "galleryid=2000&offset=20&limit=10", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" } }); var dsPhotos2 = new Spry.Data.XMLDataSet("/photos.php?galleryid=2000", "/gallery/photos/photo");
method:为请求发送方式,POST / GET ;默认为GET
postData:为请求参数,可省略,可直接把”/photos.php”换成/photos.php?galleryid=2000&offset=20&limit=10
Content-Type:为头信息
3,不使用缓存
Var dsPhotos = new Spry.Data.XMLDataSet("/photos.php?galleryid=2000", "/gallery/photos/photo", { useCache: false })
这里要重点说明一下,我在应用时,曾经发现设置不用缓存时,页面仍然不是最新数据,我是用一个servlet生成xml,然后对生成的数据进行删除和添加,但页面显示的数据不会自动更新。
后来在adobe的官方论坛上找到了答案,在IE中,使用userCache:false是不够的,还要在生成xml的jsp,php等中设置头不使用cache,如在生成xml的jsp/servlet中是要加上:
response.addHeader(”Cache-Control”,”no-cache”);
以上问题只存在IE中,在firefox和opera中不存在。注,spry对opera9版本支持很好,使用版本8的朋友要注意了。
另外,也可以在构造完数据器后,再设置缓存:
dsData.useCache = false; dsData.loadData();
4,获取数据形式
假如我们上面的请求,返回的xml如下:
<gallery id="12345"> <photographer id="4532">John Doe</photographer> <email>john@doe.com</email> <photos id="2000"> <photo path="sun.jpg" width="16" height="16" /> <photo path="tree.jpg" width="16" height="16" /> <photo path="surf.jpg" width="16" height="16" /> </photos> </gallery>
那么“gallery/photos/photo”返回的数据是下面的数组:
[
{ “@path”: “sun.jpg”, “@width”: 16, “@height”: 16 },
{ “@path”: “tree.jpg”, “@width”: 16, “@height”: 16 },
{ “@path”: “surf.jpg”, “@width”: 16, “@height”: 16 }
]
var rows = dsPhotos.getData(); // 获取所有行. var path = rows[0]["@path"]; // 获取第一行中"@path"的值 dsPhotos.setCurrentRowNumber(2); // 将第3行做为当前处理行,下标以0开始 var id = dsPhotos.getData()[2]["ds_RowID"]; // 获取第3行的ID. dsPhotos.setCurrentRow(id); // 通过第3行的id,将第3行设为当前处理行.
5,排序
dsPhotos.sort("@path"); //以"@path"列的值为关健字对行排序 dsPhotos.sort("@path", "toggle"); //"ascending", "descending"和"toggle",默认是 "ascending"。 var dsPhotos = new Spry.Data.XMLDataSet("/photos.php?galleryid=2000", "/gallery/photos/photo", { sortOnLoad: "@path", sortOrderOnLoad: "descending" });//也可在数据构造器中设置初始排序 dsPhotos.setColumnType("@width", "number");//设置类型 dsPhotos.setColumnType("@height", "number"); ... dsPhotos.sort("@width"); // 对 "@width" 列数据进行排序.
6,去除重复
dsPhotos.distinct(); // Remove all duplicate rows. //distinct()方法是具有破坏性的,多余的行是被删掉的,如果你想再得到所有的包括重复的原始项就得重新载入XML数据。 var dsPhotos = new Spry.Data.XMLDataSet("/photos.php?galleryid=2000", "/gallery/photos/photo", { distinctOnLoad: true });//可在构造时预设
7,设置过滤器
var myFilterFunc = function(dataSet, row, rowNumber) { if (row["@path"].search(/^s/) != -1) //只返回以s开头的行 return row; // Return the row to keep it in the data set. return null; // Return null to remove the row from the data set. } dsPhotos.filterData(myFilterFunc); // Filter the rows in the data set. dsPhotos.filter(myFilterFunc); // 不破坏数据,是建一个新的数组 dsPhotos.filterData(null); // 取消过滤.
8,自动刷新,以毫秒为单位
var dsPhotos = new Spry.Data.XMLDataSet("/photos.php?galleryid=2000", "/gallery/photos/photo", { useCache: false, loadInterval: 10000 });//在构造器设置 dsPhotos.startLoadInterval(10000); // 设置自动刷新 ... dsPhotos.stopLoadInterval(); // 停止自动刷新
9,把类注册成观察器
var myObserver = new Object; myObserver.onDataChanged = function(dataSet, data) //可支持:onPreLoad / onPostLoad / onLoadError / onDataChanged / onPreSort / onPostSort / onCurrentRowChanged //第一个是发送通知的对象,做为数据器观察器,这个值永远都是dataSet对象。第二个参数可以不定义,也可以是一个对象(内置对象) { alert("onDataChanged called!"; }; dsPhotos.addObserver(myObserver); dsPhotos.removeObserver(myObserver);
10,把函数方法注册为观察器
function myObserverFunc(notificationType, dataSet, data) //notfication,是通知器类型,dataSet是数据器对象,data是要观察的数据 { if (notificationType == "onDataChanged") alert("onDataChanged called!"; else if (notificationType == "onPostSort") alert("onPostSort called!"; }; dsPhotos.addObserver(myObserverFunc);
11,动态区域块
所有使用Spry动态区域块的HTML页面都要在它们的<html>标签中加入xmlns:spry=http://ns.adobe.com/spry
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry"> .... .... .... .... <ul spry:region="dsPhotos"> <li>{dsPhotos::path}</li> </ul>
COL, COLGROUP, FRAMESET, HTML, IFRAME, STYLE, TABLE, TBODY, TFOOT, THEAD, TITLE, TR 不能设为动态区域
12,数据引用
{<数据器名>::<数据器列名>}
如果一个动态区域块只和一个数据器相关联,则你甚至可以省略掉数据器的名字
<li>{@path}</li>
ds_RowID – 这是数据器的行id。这个id可以帮我们指定数据器中的一个数据。它和数据是对应的,即使数据执行了排序操作,这个id和数据的对应关系也不会变化。
ds_RowNumber – 这是数据器当前数据的行号。
ds_RowNumberPlus1 – 这个与上面的ds_RowNumber相同,只不过它规定了数据的行号从1开始,而不是从0开始。
ds_RowCount – 它是数据器中数据的行的数量。如果使用了一个非破坏性的过滤器,则它的值是这个过滤器执行后得到的行的数量。
ds_UnfilteredRowCount – 在执行非破坏性过滤器前,数据器中行的数量。
ds_CurrentRowID – 当前行的id。这个值不会改变,除非使用了一个循环的构造。
ds_CurrentRowNumber – 当前行的行号。这个值不会改变,除非使用了一个循环的构造。
ds_SortColumn – 上一次排序所依赖的列名。如果这个数据器的数据还未进行过排序,则返回一个空字符串。
ds_SortOrder – 数据器中数据排序的参数,将返回三种字符串”ascending”, “descending”, 或空字符串.。
ds_EvenOddRow – 它返回的是”even”或”odd”,告诉我们ds_RowNumber的值是奇数还是偶数。
13,循环
//方式一: <li spry:repeat="dsPhotos">{@path}</li> //方式二: <ul spry:repeatchildren="dsPhotos"> <li>{@path}</li> </ul> //只输出以s开头的 spry:test属性的值可以是任何等于0或非0值的JavaScript表达示。如果这个表达示返回非0值,这个内容将会被输出,相当于if <>0就输出后面元素。 <li spry:repeat="dsPhotos" spry:test="'{@path}'.search(/^s/) != -1;">{@path}</li>
14,if 条件
<li spry:if="'{@path}'.search(/^s/) != -1;">{@path}</li> //if/else的形式,要使用"spry:choose"属性 <div spry:choose="spry:choose"> <div spry:when="'{@path}' == 'surf.gif'">{@path}</div> <div spry:when="'{@path}' == 'undefined'">Path was not defined.</div> <div spry:default="spry:default">Unexpected value for path!</div> </div>
15,状态
<div spry:region="dsEmployees"> <div spry:state="loading">正在载入数据 ...</div> <div spry:state="error">数据载入失败!</div> <ul spry:state="ready"> <li spry:repeat="dsEmployees">{firstname} {lastname}</li> </ul> </div>
16,通过对象将区域注册成观察者
myObserver = new Object; myObserver.onPostUpdate = function(notifier, data) { alert("onPostUpdate called for " + data.regionID); }; ... // 调用addObserver() 将类注册为观察者. Spry.Data.Region.addObserver("employeeListRegion", myObserver); ... //注销 Spry.Data.Region.removeObserver("employeeListRegion", myObserver); ... <ul id="employeeListRegion" spry:region="dsEmployees"> ... </ul>
17,以函数将区域注册成观察者
function myRegionCallback(notificationState, notifier, data) { if (notificationType == "onPreUpdate") //onLoadingData / onPreUpdate / onPostUpdate / onError alert(regionID + " is starting an update!"); else if (notificationType == "onPostUpdate") alert(regionID + " is done updating!"); } ... // 注册 Spry.Data.Region.addObserver("employeeListRegion", MyRegionCallback); ... // 注销 Spry.Data.Region.removeObserver("employeeListRegion", MyRegionCallback); ... <ul id="employeeListRegion" spry:region="dsEmployees"> ... </ul>
18,主细节模式,同一数据器
<span spry:region="dsEmployees"> <select spry:repeatchildren="dsEmployees" onchange="dsEmployees.setCurrentRow(this.value)"> <option spry:if="{ds_RowNumber} == 0" value="{ds_RowID}" selected="selected">{username}</option> <option spry:if="{ds_RowNumber} != 0" value="{ds_RowID}">{username}</option> </select> </span> <span spry:detailregion="dsEmployees">{@id} - {firstname} {lastname} - {phone} </span> //spry:detailregion"会在接收到"CurrentRowChanged"通知后改变自己的展示形式。
19,主细节模式,多个数据器
var dsStates = new Spry.Data.XMLDataSet("../../data/states/states.xml", "states/state"); var dsCities = new Spry.Data.XMLDataSet("../../data/states/{dsStates::url}", "state/cities/city"); //两个数据器有依赖关系,也可以用:"/webapp/cities.php?stateid={dsStates::@id}". <form name="selectForm"> //State: <span spry:region="dsStates" id="stateSelector"> <select spry:repeatchildren="dsStates" name="stateSelect" onchange="document.forms[0].citySelect.disabled = true; dsStates.setCurrentRow(this.value);"> <option spry:if="{ds_RowNumber} == 0" value="{ds_RowID}" selected="selected">{name}</option> <option spry:if="{ds_RowNumber} != 0" value="{ds_RowID}">{name}</option> </select> </span> //City: <span spry:region="dsCities" id="citySelector"> <select spry:repeatchildren="dsCities" name="citySelect"> <option spry:if="{ds_RowNumber} == 0" value="{name}" selected="selected">{name}</option> <option spry:if="{ds_RowNumber} != 0" value="{name}">{name}</option> </select> </span> </form>
20,改变数据源
<select onchange="dsEmployees.setURL(this.value); dsEmployees.loadData();"> <option value="../../data/employees-01.xml" selected>Set 1</option> <option value="../../data/employees-02.xml">Set 2</option> </select> <th scope="col" onclick="dsEmployees.sort('@id');">Employee ID </th>
最近看完了Adobe的AJAX框架Spry的所有文档和Demo,觉得这东西挺有意思的,在这里介绍给大家。
Spry框架的开发人员是来自于DreamWeaver开发组,他们把Spry框架做为DreamWeaver的一个完美补充为设计者和开发者提供对AJAX技术的支持。Spry框架是一个轻量级的AJAX框架,它的代码和标签十分的简洁和优雅,以保证让用户能便捷的使用,并不会为过繁杂的标签所惑。
Spry框架的官方网址:
http://labs.adobe.com/technologies/spry
在这里你能找到最新的文档和下载最新的Spry版本,目前版本是预览版1.3_08-11。
大家可以先在下面的看到Spry的示例和Demo:
http://labs.adobe.com/technologies/spry/samples/
http://labs.adobe.com/technologies/spry/demos/
Spry框架其实就是一个客户端的JavaScript类库,包含了一组JavaScript文件,CSS,图片文件,通过官方的框架结构图,我们能看出Spry框架的核心是四部分:XML数据器(XML Data Sets),动态区域(Dynamic Regions),装饰器库(Widgets)和变化效果库(Transition Effects)。
我们可以看出,Spry框架接收的数据格式只是XML数据格式。
一,XML数据器(XML Data Sets)
XML数据器是一个提供了从XML文档中载入和管理数据的JavaScript对象。它是Spry框架中处理XML格式数据的一个JavaScript功能实现。通过它,我们可以从XML中直接得到转换成表格数据格式的行和列的值,其实就是数组。它封装了获取XMLhttpRequest的方法,和发送并接收数据等一系列获取数据的方法。
要创建一个XML数据器,你必须在你的HTML文件中加入两行引入JavaScript文件的代码:
<script type=”text/javascript” src=”../../includes/xpath.js”></script>
<script type=”text/javascript” src=”../../includes/SpryData.js”></script>
上面引入的是Spry框架的核心js文件之一。”xpath.js”是Google基于XPath 1.0标准的JavaScript功能实现。你如果想获得更多的关于它的信息,可以访问Google的开源项目 google-ajaxslt project page .
”SpryData.js”则包含了定义XML数据器和动态区域的代码。
构造XML数据器就好像新建一个类一样,用”new”关键字即可:
<script type=”text/javascript”>
var dsPhotos = new Spry.Data.XMLDataSet(”/photos.php?galleryid=2000″, “/gallery/photos/photo”);
</script>
Spry框架为XML数据器提供了一些有特色的功能:如数据排序,数据过滤,按指定时间间隔自动更新,并引入了观察者通知模式以支持事件的触发。
关于XML数据器更多资料,大家可以参考http://labs.adobe.com/technologies/spry/articles/data_set_overview/,这一部分笔者已经翻译完成了,但英文原文其实写的通俗易懂,如果对一些名词翻译的不准确反倒会误导了大家,所以还是不贴出来了,大家可以参考官方英文文档。
二,动态区域(Dynamic Regions)
一旦你建立了XML数据器,你就可以在动态区域中去显示这个数据器的数据了。
创建动态区域块很简单,只要在html标签的相应位置加上”spry:region”属性就可以了,Spry框架就会知道这一块是被标识成动态区域了。
在动态区域,你可以有条件的选择要输出的数据,也可用循环输出。
动态区域另一个特点就是,它分为master和detail两个区域类型。
如下面的Demo截图:
master区域的数据改变会使detail区域的数据相应发生改变
两者都注册成XML数据器的观察者
当master区域的数据变化时,触发detail区域的响应事件,从面达到更新相应数据。
大家可参考http://labs.adobe.com/technologies/spry/samples/DataSetMasterDetailSample.html所示的功能。
这只是动态区域简单的示例,复杂的情况可能会有多个XML数据器与多个动态区域相互关联触发。
三,装饰器库(Widgets)
一个装饰器是由一组HTML,CSS,JavaScript封装成的高级UI。最常见的装饰器有可折叠的菜单,树型菜单和选项table面板等。这些对象都比较难于创建,需要一些更高级的编程经验。Spry的开发组在创建装饰器这一概念就是希望开发者们能相互协作,共享各自的设计,把这些高级界面元素用在自己的页面上。
Spry框架下的装饰器是易于编辑的。这种模型非常适合于设计者和编辑人员:要改变外观,只要改变CSS就可以了,要增加一个可折叠菜单只要copy和paste一个代码块就够了。
如,你能看懂的这段代码是创建了一个可折叠菜单吗?
<div id=”Acc1″ >
<div>
<div>Panel Header 1</div>
<div>Panel 1 Content </div>
</div>
<div>
<div>Panel Header 2</div>
<div>Panel 2 Content</div>
</div>
<div>
<div>Panel Header 3</div>
<div>Panel 3 Content</div>
</div>
</div>
<script>
var acc1 = new Hanzo.Widget.Accordion(”Acc1″);
</script>
这段代码是非常简洁清晰的,没有什么繁杂的标签,这样设计是为了易于阅读。
关于这个例子,可以参考http://labs.adobe.com/technologies/spry/samples/accordion/AccordionSample.html
四,变化效果库(Transition Effects)
Spry框架的变化效果库都存于SpryEffects.js文件中,是基于JavaScript的一些动态变化效果,如,淡出,改变形状等。
Spry框架在设计时,曾考虑直接用第三方的效果库,如Script.aculo.us,但后来开发小组觉得要保证框架代码和标签的一致性,还是选择了自已开发,但是也基本上是以Script.aculo.us为原型进行设计,因为Script.aculo.us 本身就是一个非常优秀的变化效果库框架。
由于Spry框架现在只是预览版,所以目前只支持七种变换:
Appear/Fade Makes an element appear or fade away
Highlight Flashes a color as the background of an element
BlindUp/BlindDown Simulates a window blind, up or down, where the contents of the affected elements stay in place
SlideUp/SlideDown Simulates a window blind, where the contents of the affected element scroll accordingly
Grow/Shrink Increases/reduces the size of the element
Shake Moves the element slightly to the left, then to the right, repeatedly
Squish Reduces the element to its top-left corner and disappears
可能到这大家对Spry框架已经有了大致的了解,其实这个东西已经足够我们的大多数应用的开发了,笔者也十分期待着正式版能早日放出,并打算在自己现在的项目中试一试了。