食物禁忌

虾+维C=中毒   感冒药+可乐=中毒
还有一些,你知道么?!
1、鸡蛋忌糖精┄┄同食中毒、死亡
2、豆腐忌蜂蜜┄┄同食耳聋
3、海带忌猪血┄┄同食便秘
4、土豆忌香蕉┄┄同食生雀斑
5、牛肉忌红糖┄┄同食胀死人
6、狗肉忌黄鳝┄┄同食则死
7、羊肉忌田螺┄┄同食积食腹胀
8、芹菜忌兔肉┄┄同食脱头发
9、番茄忌绿豆┄┄同食伤元气
10、螃蟹忌柿子┄┄同食腹泻
11、鹅肉忌鸭梨┄┄同食伤肾脏
12、洋葱忌蜂蜜┄┄同食伤眼睛
13、黑鱼忌茄子┄┄同食肚子痛
14、甲鱼忌苋菜┄┄同食中毒
15、皮蛋忌红糖┄┄同食作呕
16、人参忌萝卜┄┄同食积食滞气
17、白酒忌柿子┄┄同食心闷

不能一起吃的食物:
1.红薯和柿子——会得结石
2.鸡蛋和糖精——容易中毒
3.洋葱和蜂蜜——伤害眼睛
4.豆腐和蜂蜜——引发耳聋
5.萝卜和木耳——皮肤发炎
6.芋头和香蕉——腹胀
7.花生和黄瓜——伤害肾脏
8.牛肉和栗子——引起呕吐
9.兔肉和芹菜——容易脱发
10.螃蟹和柿子——腹泻
11.鲤鱼和甘草——会中毒
以下食物在两小时内一定不要同吃:
羊肉忌西瓜———同食 伤元气
牛肉忌栗子———同食呕吐
柿子忌螃蟹———同食腹泻
鸡蛋忌糖精———同食中毒
兔肉忌芹菜———同食脱发
鹅肉忌鸡蛋———同食伤元气
洋葱忌蜂蜜———同食伤眼睛
黄瓜忌花生———同食伤身
香蕉忌芋头———同食腹胀
猪肉忌菱角———同食肚子痛
豆腐忌蜂蜜———同食耳聋
萝卜忌木耳———同食得皮炎
狗肉忌绿豆———同食多吃易中毒
马肉忌木耳———同食得霍乱
牛肉忌毛姜———同食中毒死亡
羊肉忌梅干菜——同食生心闷
鸡肉忌芥菜———同食伤元气
驴肉忌黄花———同食心痛致命
兔肉忌小白菜——同食易呕吐
鹅肉忌鸭梨———同食好生热病
黑鱼忌茄子———同食易得霍乱
海蟹忌大枣———同食易得疟疾
芥菜忌鸭梨———同食发呕
马铃薯忌香蕉———同食面部生斑

一些蔬菜不能搭配吃 :
猪肉菱角同食会肝疼,羊肉西瓜相会定互侵;
狗肉如遇绿豆会伤身,萝卜水果不利甲状腺;
鲤鱼甘草加之将有害,蟹与柿子结伴会中毒;
甲鱼黄鳝与蟹孕妇忌,鸡蛋再吃消炎片相冲;
柿子红薯搭配结石生,豆浆营养不宜冲鸡蛋;
洋葱蜂蜜相遇伤眼睛,萝卜木耳成双生皮炎;
豆腐蜂蜜相拌耳失聪,菠菜豆腐色美实不宜;
胡萝卜白萝卜相互冲,蕃茄黄瓜不能一起食;
香蕉芋艿入胃酸胀痛,马铃薯香蕉面部起斑。

不宜吃的食物 :
1、发芽、发青的土豆有毒,不能吃。
2、新鲜的黄花菜(金针菜)有毒,不能吃。
3、没有炒透的四季豆、扁豆有毒,吃不得。
4、老鸡头(5年以上鸡头)有大毒,吃不得。
5、嫩炒猪肝,含有毒素,不宜吃。
6、皮蛋、爆米花含铅特别多,儿童不宜吃。
7、烤焦的食物不能吃,吃后易患癌。
8、烂姜有极毒,能坏死肝细胞,切不可吃。
9、生豆油含有苯,会破坏造血系统,不可吃。
10、久煮的水含有亚硝酸盐,吃则易生癌。
11、太烫食物不能吃,易烫伤消化道引起癌变。
12、未熟透的豆浆不能吃,吃易中毒。
13、腌制的食物含有致癌物质,不宜多吃。
14、烘烤的肉串类、鱼片含致癌物,不宜多吃。
15、柿子空服易患胃内柿结石,千万不要吃。
16、食品添加剂、人造食素、香料、香精、皮蛋、方便面、午餐肉、油炸食物不宜多吃。

posted @ 2010-03-31 10:14 小马歌 阅读(244) | 评论 (0)编辑 收藏
 

我曾经爱过这样一个男人
他的名字是被叫做马化腾
他的公司很赚钱就是腾讯
一直靠抄袭创新,
也是这个被我深爱的男人
把中国互联网变成了孤坟
他却赚了很多很多money
每一个创新他都会去抄袭
不管创业者心疼

我的要求并不高
别把产品都抄掉
夺走创业者佳肴
可是每一天都在做同样的抄 _
最近他们又买下了开心农场
要挟进入他怀抱

你身上有太多的铜臭
是我鼻子犯的罪,
不该看到企鹅腿
世界就此一起碎

你想独霸中国互联网
仗着产品线很多
你的口吻太吓人
我永远都别想睡

posted @ 2010-03-31 10:13 小马歌 阅读(166) | 评论 (0)编辑 收藏
 
 
1、精心编辑采集内容,或以专题的方式介绍采集的内容。

2、加上自己的观点,评点内容的精华。

3、以专家的身份去写原创内容,强调你的专业性。

4、内容的可读性,不要口水话太多。

5、网站保持更新频率,不管你内容多好,一个持久不更新的网站,最终会留失很多用户。

6、让用户可以和你的网站有互动的窗口,如百谷精SEO论坛、评论等,现在流行豆瓣和digg模式也不错。

7、让用户和你分享网站收入,这招也不错的,例如帮你推广网站或写文章,用户都有一定比例的收入。

8、让你的网站与众不同,独特的美工,独特的内容,引用zac的一句话“没有创意,没有眼球”。
posted @ 2010-03-15 11:03 小马歌 阅读(187) | 评论 (0)编辑 收藏
 
下述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的。对于这些经验的运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握。并逐步做到:在应用中发展,在发展中应用。

  1. 原始单据与实体之间的关系

  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对应多个实体,或多张原始单证对应一个实体。这里的实体可以理解为基本表。明确这种对应关系后,对我们设计录入界面大有好处。

  〖例1〗:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表、社会关系表、工作简历表。这就是“一张原始单证对应多个实体”的典型例子。

  2. 主键与外键

  一般而言,一个实体不能既无主键又无外键。在E?R 图中, 处于叶子部位的实体, 可以定义主键,也可以不定义主键(因为它无子孙), 但必须要有外键(因为它有父亲)。

  主键与外键的设计,在全局数据库的设计中,占有重要地位。当全局数据库的设计完成以后,有个美国数据库设计专家说:“键,到处都是键,除了键之外,什么也没有”,这就是他的数据库设计经验之谈,也反映了他对信息系统核心(数据模型)的高度抽象思想。因为:主键是实体的高度抽象,主键与外键的配对,表示实体之间的连接。

  3. 基本表的性质

  基本表与中间表、临时表不同,因为它具有如下四个特性:

  (1) 原子性。基本表中的字段是不可再分解的。

  (2) 原始性。基本表中的记录是原始数据(基础数据)的记录。

  (3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。

  (4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。

  理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

  4. 范式标准

  基本表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。

  〖例2〗:有一张存放商品的基本表,如表1所示。“金额”这个字段的存在,表明该表的设计不满足第三范式,因为“金额”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加“金额”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。

  在Rose 2002中,规定列有两种类型:数据列和计算列。“金额”这样的列被称为“计算列”,而“单价”和“数量”这样的列被称为“数据列”。

  表1 商品表的表结构

  商品名称 商品型号 单价 数量 金额

  电视机 29? 2,500 40 100,000

  5. 通俗地理解三个范式

  通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):

  第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;

  第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;

  第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余.

  没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是:在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。

  6. 要善于识别与正确处理多对多的关系

  若两个实体之间存在多对多的关系,则应消除这种关系。消除的办法是,在两者之间增加第三个实体。这样,原来一个多对多的关系,现在变为两个一对多的关系。要将原来两个实体的属性合理地分配到三个实体中去。这里的第三个实体,实质上是一个较复杂的关系,它对应一张基本表。一般来讲,数据库设计工具不能识别多对多的关系,但能处理多对多的关系。

  〖例3〗:在“图书馆信息系统”中,“图书”是一个实体,“读者”也是一个实体。这两个实体之间的关系,是一个典型的多对多关系:一本图书在不同时间可以被多个读者借阅,一个读者又可以借多本图书。为此,要在二者之间增加第三个实体,该实体取名为“借还书”,它的属性为:借还时间、借还标志(0 表示借书,1表示还书),另外,它还应该有两个外键(“图书”的主键,“读者”的主键),使它能与“图书”和“读者”连接。

  7. 主键PK的取值方法

  PK是供程序员使用的表间连接工具,可以是一无物理意义的数字串, 由程序自动加1来实现。也可以是有物理意义的字段名或字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空间大,而且速度也慢。

  8. 正确认识数据冗余

  主键与外键在多表中的重复出现, 不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚。非键字段的重复出现, 才是数据冗余!而且是一种低级冗余,即重复性的冗余。高级冗余不是字段的重复出现,而是字段的派生出现。

  〖例4〗:商品中的“单价、数量、金额”三个字段,“金额”就是由“单价”乘以“数量”派生出来的,它就是冗余,而且是一种高级冗余。冗余的目的是为了提高处理速度。只有低级冗余才会增加数据的不一致性,因为同一数据,可能从不同时间、地点、角色上多次录入。因此,我们提倡高级冗余(派生性冗余),反对低级冗余(重复性冗余)。

  9. E--R图没有标准答案

  信息系统的E--R图没有标准答案,因为它的设计与画法不是惟一的,只要它覆盖了系统需求的业务范围和功能内容,就是可行的。反之要修改E-- R图。尽管它没有惟一的标准答案,并不意味着可以随意设计。好的E?R图的标准是:结构清晰、关联简洁、实体个数适中、属性分配合理、没有低级冗余。

  10. 视图技术在数据库设计中很有用

  与基本表、代码表、中间表不同,视图是一种虚表,它依赖数据源的实表而存在。视图是供程序员使用数据库的一个窗口,是基表数据综合的一种形式, 是数据处理的一种方法,是用户数据保密的一种手段。为了进行复杂处理、提高运算速度和节省存储空间, 视图的定义深度一般不得超过三层。若三层视图仍不够用, 则应在视图上定义临时表, 在临时表上再定义视图。这样反复交迭定义, 视图的深度就不受限制了。

  对于某些与国家政治、经济、技术、军事和安全利益有关的信息系统,视图的作用更加重要。这些系统的基本表完成物理设计之后,立即在基本表上建立第一层视图,这层视图的个数和结构,与基本表的个数和结构是完全相同。并且规定,所有的程序员,一律只准在视图上操作。只有数据库管理员,带着多个人员共同掌握的“安全钥匙”,才能直接在基本表上操作。请读者想想:这是为什么?

  11. 中间表、报表和临时表

  中间表是存放统计数据的表,它是为数据仓库、输出报表或查询结果而设计的,有时它没有主键与外键(数据仓库除外)。临时表是程序员个人设计的,存放临时记录,为个人所用。基表和中间表由DBA维护,临时表由程序员自己用程序自动维护。

  12. 完整性约束表现在三个方面

  域的完整性:用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Check按钮,通过它定义字段的值城。参照完整性:用PK、FK、表级触发器来实现。用户定义完整性:它是一些业务规则,用存储过程和触发器来实现。

  13. 防止数据库设计打补丁的方法是“三少原则”

  (1) 一个数据库中表的个数越少越好。只有表的个数少了,才能说明系统的E--R图少而精,去掉了重复的多余的实体,形成了对客观世界的高度抽象,进行了系统的数据集成,防止了打补丁式的设计;

  (2) 一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的外键,所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;

  (3) 一个表中的字段个数越少越好。只有字段的个数少了,才能说明在系统中不存在数据重复,且很少有数据冗余,更重要的是督促读者学会“列变行”,这样就防止了将子表中的字段拉入到主表中去,在主表中留下许多空余的字段。所谓“列变行”,就是将主表中的一部分内容拉出去,另外单独建一个子表。这个方法很简单,有的人就是不习惯、不采纳、不执行。

  数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。“三少”是一个整体概念,综合观点,不能孤立某一个原则。该原则是相对的,不是绝对的。“三多”原则肯定是错误的。试想:若覆盖系统同样的功能,一百个实体(共一千个属性) 的E--R图,肯定比二百个实体(共二千个属性) 的E--R图,要好得多。

  提倡“三少”原则,是叫读者学会利用数据库设计技术进行系统的数据集成。数据集成的步骤是将文件系统集成为应用数据库,将应用数据库集成为主题数据库,将主题数据库集成为全局综合数据库。集成的程度越高,数据共享性就越强,信息孤岛现象就越少,整个企业信息系统的全局E?R图中实体的个数、主键的个数、属性的个数就会越少。

  提倡“三少”原则的目的,是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据库变成了随意设计数据库表的“垃圾堆”,或数据库表的“大杂院”,最后造成数据库中的基本表、代码表、中间表、临时表杂乱无章,不计其数,导致企事业单位的信息系统无法维护而瘫痪。

  “三多”原则任何人都可以做到,该原则是“打补丁方法”设计数据库的歪理学说。“三少”原则是少而精的原则,它要求有较高的数据库设计技巧与艺术,不是任何人都能做到的,因为该原则是杜绝用“打补丁方法”设计数据库的理论依据。

  14. 提高数据库运行效率的办法

  在给定的系统硬件和系统软件条件下,提高数据库系统的运行效率的办法是:

  (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。

  (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。

  (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表。

  (4) 对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。

  (5) 在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。

  总之,要提高数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化,这三个层次上同时下功夫。
posted @ 2010-03-10 14:55 小马歌 阅读(149) | 评论 (0)编辑 收藏
 
 /***                 phpall技术交流群:75345798

                英文原文:http://net.tutsplus.com/tutorials/php/6-codeigniter-hacks-for-the-masters/
                翻译原文:http://www.phpall.cn/forum/read.php?tid=389
          ***/
      Codeigniter是一个简洁而又强大的开源web应用框架。今天,我们将会使用一些核心的技巧来对这个框架做一些小的改变并提高它的性能。
在这过程之中,你将会对框架得到一个更好的理解。
作者:burak guze


      他是一个住在亚利桑那州的全职php web开发者,从伊斯坦布尔来的。他获得了计算机科学与工程的学士学                 位。他拥有8年的phpmysql工作经验。你能阅读他的更多的文章在PHPandStuff.com,他的twitter
here


声明

1.不推荐在已经存在的项目中使用这些技巧。因为他们改变了ci的核心功能,可能会使已经写的代码崩溃。
2.我写这篇文章的时候,ci1.7.2版本是最新的发行版本,这些技巧对以前的版本没有作用。
3.尽管ci是被设计php4兼容的。但是这些技巧需要你安装php5.
4.当你对cisystem文件做的任何更改,你都应该对这些改变做好记录,供以后参考。下次当你升级ci的时候,你可能需要重新再处理这些变化。

1.大师的6个codeigniter技巧——(1)自动加载models——php5风格


这个技巧希望达到的目标是:

      在左边,你会看到在controller里面通常加载model的方法。使用这个技巧以后,我们将能够直接创建这个model对象。
这个代码是简洁的并且容易理解对象。
    使用这个技巧以后会有2个影响。首先你不再需要继承model类了。


这个技巧

我们需要做的就是添加一个php5风格的 autolader 函数

添加这些代码到system/application/config/config.php:
<?php  
// ...  
function __autoload($class) {  
    if (file_exists(APPPATH."models/".strtolower($class).EXT)) {  
        include_once(APPPATH."models/".strtolower($class).EXT);  
    }  
}  
?>
如果你也有兴趣运用这个技巧到controller,你只需要添加以下代码来代替上面的代码。

<?php  
// ...  
function __autoload($class) {  
    if (file_exists(APPPATH."models/".strtolower($class).EXT)) {  
        include_once(APPPATH."models/".strtolower($class).EXT);  
    } else if (file_exists(APPPATH."controllers/".strtolower($class).EXT)) {  
        include_once(APPPATH."controllers/".strtolower($class).EXT);  
    }  
}  
?>
任何时候,你试着使用一个没有定义的类时候,这个__autoload函数将会被调用,它将会加载这个类文件。

2.大师的6个codeigniter技巧——(2)防止model-controller名字冲突

使用这个技巧要达到的目标:

一般来说,模型和控制器你都不会有相同的类名字。让我先创建一个取名为postmodel
class Post extends Model {  

    // ...  

}
现在你就不能有一个像这样的url
http://www.mysite.com/post/display/13
这个原因是因为你也需要有一个名字为postcontroller,如果创建了这样的一个类的话将会引起致命错误。
但是使用了这个技巧一般,一切皆有可能。那个url的控制器看起来是这样的:

// application/controllers/post.php  
class Post_controller extends Controller {  

    // ...  

}
注意这个“__controller”后缀
技巧:

为了避免这个问题,通常大多数人都是添加‘_model’后缀到model名字(例如命名Post_model)。
在所有的应用程序Model对象都被创建和引用,所以在所有的model名字后面跟上‘_model’有些无聊。
我认为最好的办法就是在controller上来添加后缀,因为在代码中controller的名字几乎从来不会被引用。
首先我们需要继承Router class。创建这样一个文件:"application/libraries/MY_Router.php"
class MY_Router extends CI_Router {  
    var $suffix = '_controller';  

    function MY_Router() {  
        parent::CI_Router();  
    }  
    function set_class($class) {  
        $this->class = $class . $this->suffix;  
    }  
    function controller_name() {  
        if (strstr($this->class, $this->suffix)) {  
            return str_replace($this->suffix, '', $this->class);  
        }  
        else {  
            return $this->class;  
        }  
    }  
}  
现在编辑"system/codeigniter/CodeIgniter.php"
153
if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT))  
然后第158

include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT);  

然后编辑"system/libraries/Profiler.php"的第323

$output .= "<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0">".$this->CI->router->controller_name()."/".$this->CI->router->fetch_method()."</div>";  
大功告成。使用这个技巧一定需要记住的是要把‘_controller’后缀放到你的controller的类的名字后面,不是放在你的控制器文件名中。


大师的6个codeigniter技巧——(3)表单验证“唯一值”(如注册用户名email)
      Ci有一个完美的表单验证类。但是这个验证有一点不足,例如当大部分用户注册用户名的时候,需要验证用户名没有被占用,或者邮箱是否存在在系统中。
使用这个技巧,你将能够非常容易添加这项验证规则到你的表单提交中。

注意最后一部分"unique[User.username]."这个新的验证规则叫做“unique”,并且带了一个方框在这个中括号中,它们是“tablename.filedname.所以它将会检测数据库的“user数据表的“username”列确定提交的值在数据库里面不存在。
同理,你也能利用这个规则验证相同的邮件地址。
$this->form_validation->set_rules('email', 'E-mail',  
        'required|valid_email|unique[User.email]');  
你的应用程序将会得到以下错误信息

与其说这是一个技巧而不如说是一个扩展。虽然如此,我们将要添加一个核心ci类库并且改善它。
创建 "application/libraries/MY_Form_validation.php"文件

class MY_Form_validation extends CI_Form_validation {  

    function unique($value, $params) {  

        $CI =& get_instance();  
        $CI->load->database();  

        $CI->form_validation->set_message('unique',  
            'The %s is already being used.');  

        list($table, $field) = explode(".", $params, 2);  

        $query = $CI->db->select($field)->from($table)  
            ->where($field, $value)->limit(1)->get();  

        if ($query->row()) {  
            return false;  
        } else {  
            return true;  
        }  

    }  
}
现在你就能使用这个“唯一”验证规则了。

4.大师的6个codeigniter技巧——(4)从命令行运行codeigniter

目标:
就像这个标题所说,我们的目标是能够从命令行运行ci。对于创建定时任务,这个是必须的,或者是运行更多的特别的操作,
所以你没有web脚本的资源限制,就像最大执行时间一样。
这就是本地windows机器上面运行的效果

技巧
创建一个“cli.php”文件在ci的根目录(即与index.php在同一目录)
if (isset($_SERVER['REMOTE_ADDR'])) {  
    die('Command Line Only!');  
}  
set_time_limit(0);  
$_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1];  
require dirname(__FILE__) . '/index.php';

如果你在linux环境下使用,并且想要让这代码自动执行,你能添加以下代码在cli.php文件的第一行

#!/usr/bin/php  
如果你需要一个仅仅能在命令行下使用的控制器,你能阻止web调用控制器构造函数
class Hello extends Controller {  
    function __construct() {  
        if (isset($_SERVER['REMOTE_ADDR'])) {  
            die('Command Line Only!');  
        }  
        parent::Controller();  
    }  

    // ...  
}

5.大师的6个codeigniter技巧——(5)添加Doctrine orm 到codeigniter
(Doctrine orm是一个比较复杂的东西,我们将会继续出一些相关教程
目标
Doctine是一个流行的php的关系对象映射(orm)。添加它到ci里面以后,你将会一个更加强大的模型层。

技巧:
其实安装Doctrine不是那么难,就像安装插件一样。然后一旦安装成功,你的model类将需要继承Doctrine基类,而不是继承cimodel类。这将会完全改变model层的工作方式。你建立的对象将会有持久的数据库连接并且也将能有其他对象的数据库关系。
按照以下几步:
1. 建立文件夹:application/plugins
2. 创建文件夹:application/plugins/doctrine
3. 下载文件(1.2版本的)
4. Doctrine复制“lib”文件夹到“application/plugins/doctrine
5. 创建“application/plugins/doctrine_pi.php

// system/application/plugins/doctrine_pi.php  
// load Doctrine library  
require_once APPPATH.'/plugins/doctrine/lib/Doctrine.php';  
// load database configuration from CodeIgniter  
require_once APPPATH.'/config/database.php';  
// this will allow Doctrine to load Model classes automatically  
spl_autoload_register(array('Doctrine', 'autoload'));  
// we load our database connections into Doctrine_Manager  
// this loop allows us to use multiple connections later on  
foreach ($db as $connection_name => $db_values) {  
    // first we must convert to dsn format  
    $dsn = $db[$connection_name]['dbdriver'] .  
        '://' . $db[$connection_name]['username'] .  
        ':' . $db[$connection_name]['password'].  
        '@' . $db[$connection_name]['hostname'] .  
        '/' . $db[$connection_name]['database'];  
    Doctrine_Manager::connection($dsn,$connection_name);  
}  
// CodeIgniter's Model class needs to be loaded  
require_once BASEPATH.'/libraries/Model.php';
// telling Doctrine where our models are located
Doctrine::loadModels(APPPATH.'/models');  

然后,编辑‘application/config/autoload.php’自动加载Doctrine插件
$autoload['plugin'] = array('doctrine');
你也要确定在“application/config/database.php的数据库配置好了,”
就这样,现在你就能使用ci应用程序创建Doctrine模型了。阅读更多的资源在这里

6.大师的6个codeigniter技巧——(6)运行多个站点
目标:
这个技巧将会使安装一个codeigniter就能运行多个站点成为可能,每个站点有它自己的application文件夹,但是他们共享这相同的系统文件夹。

技巧
安装ci服务器的任何位置。然后将application文件夹从system文件夹拿出来。放在外面,请看上面的图片。
现在复制index.php文件到每个站点的跟目录下面(即图中的application_site1application_site2等)
然后编辑它:
在第26行,给出system文件夹的完整路径
$system_folder = dirname(__FILE__) . '../codeigniter/system';  
在第43行,给出application文件夹的文章路径
$application_folder = dirname(__FILE__) . '../application_site1';  
现在你就能使用独立的application文件夹来建立不同的站点了,而只是共享同一个system文件夹
这里有一个相似的操作在ci用户手册



7.大师的6个codeigniter技巧——(7)允许所有类型的文件上传
目标:
当使用ci的上传类的时候,你必须指明哪些文件类型允许上传。

复制代码
  • $this->load->library('upload');   $this->upload->set_allowed_types('jpg|jpeg|gif|png|zip');

如果你没有指明特定的上传类型,你将会从ci那里得到一个错误信息"Youhave not specified any allowed file types."
所有,默认的方式,将没有办法允许所有的文件上传。我们需要做一些小的改变来处理这个限制。我们设定“*”将能够运行所有类型的文件上传。

复制代码
  • $this->load->library('upload');   $this->upload->set_allowed_types('*');
技巧:
我们需要修改上传文件类。
创建文件:application/librarys/My_upload.php
class MY_Upload extends CI_Upload {  
    function is_allowed_filetype() {  
        if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))  
        {  
            $this->set_error('upload_no_file_types');  
            return FALSE;  
        }  
        if (in_array("*", $this->allowed_types))  
        {  
            return TRUE;  
        }  
        $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');  
        foreach ($this->allowed_types as $val)  
        {  
            $mime = $this->mimes_types(strtolower($val));  

            // Images get some additional checks  
            if (in_array($val, $image_types))  
            {  
                if (getimagesize($this->file_temp) === FALSE)  
                {  
                    return FALSE;  
                }  
            }  
            if (is_array($mime))  
            {  
                if (in_array($this->file_type, $mime, TRUE))  
                {  
                    return TRUE;  
                }  
            }  
            else  
            {  
                if ($mime == $this->file_type)  
                {  
                    return TRUE;  
                }  
            }  
        }  
        return FALSE;  
    }  
posted @ 2010-01-11 16:09 小马歌 阅读(2478) | 评论 (0)编辑 收藏
 
from : http://blog.csdn.net/phphot/archive/2009/04/29/4134913.aspx

最近发现两台MySQL server在中午的时候忽然(很突然的那种)发飙,不断的挂掉。重启mysql也尽是失败,看mysql的errorlog,只能看到类似如下的信息:

Forcing close of thread 12232 user: 'root'

用mysqladmin 简单的监控了下mysql的情况:

mysqladmin -uroot -p******** status -i 1

发现Queries per second avg只有200左右,可以说很低,但是Threads 确非常不稳定,居然会瞬间升级200以上,一般情况下这个线程这个值都是不会高于5的个位数!

然后继续看

mysqladmin -uroot -p******** processlist

居然有大量的unauthenticated user?? 如下情况

+------+-----------+---------+----+---------+------+-------+------------------+
[root@app028 ~]# mysqladmin -uroot -p************ processlist
+------+-----------+---------+----+---------+------+-------+------------------+
| Id   | User                 | Host               | db | Command | Time | State | Info       |
+------+-----------+---------+----+---------+------+-------+------------------+
| 2007 | unauthenticated user | 192.168.4.29:58519 |    | Connect |     | login | |
| 2008 | unauthenticated user | 192.168.4.29:58553 |    | Connect |     | login | |
| 2009 | unauthenticated user | 192.168.4.29:58571 |    | Connect |     | login | |
| 2010 | unauthenticated user | 192.168.4.29:58577 |    | Connect |     | login | |
| 2011 | unauthenticated user | 192.168.4.29:58579 |    | Connect |     | login | |
| 2012 | unauthenticated user | 192.168.4.29:58589 |    | Connect |     | login | |

google了一下,

发现这算属MySQL的一个bug,不管连接是通过hosts还是ip的方式,MySQL都会对DNS做反查,IP到DNS,由于反查的接续速度过慢(不管是不是isp提供的dns服务器的问题或者其他原因),大量的查询就难以应付,线程不够用就使劲增加线程,但是却得不到释放,所以MySQL会“假死”。

解决的方案很简单,结束这个反查的过程,禁止任何解析。

打开mysql的配置文件(my.cnf),在[mysqld]下面增加一行:

skip-name-resolve

重新载入配置文件或者重启MySQL服务即可。


==================================

看下手册中的解释是:unauthenticated user refers to a thread that has become associated with a client connection but for which authentication of the client user has not yet been done。意即:有一个线程在处理客户端的连接,但是该客户端还没通过用户验证。
原因可能有:
1、        服务器在做DNS反响解析,解决办法有2:
1、) 在 hosts 中添加客户端ip,如
192.168.0.1  yejr
2、) MySQL启动参数增加一个skip-name-resolve,即不启用DNS反响解析
2、服务器的线程还处于排队状态,因此可以加大 back_log
posted @ 2009-11-24 16:55 小马歌 阅读(501) | 评论 (0)编辑 收藏
 
APNIC是管理亚太地区IP地址分配的机构 ,它有着丰富准确的IP地址分配库,同时这些信息也是对外公开的!下面就让我们看看如何在Linux下获得一些电信运营商的IP地址分配情况:
shell> wget http://ftp.apnic.net/apnic/dbase/tools/ripe-dbase-client-v3.tar.gz
shell> tar xzvf ripe-dbase-client-v3.tar.gz
shell> cd whois-3.1
shell> ./configure
shell> make

完成上述编译安装工作后,我们开始获取IP地址段;

  中国网通:shell> ./whois3 -h whois.apnic.net -l -i mb MAINT-CNCGROUP > /var/cnc

  中国电信:shell> ./whois3 -h whois.apnic.net -l -i mb MAINT-CHINANET > /var/chinanet

  中国铁通:shell> ./whois3 -h whois.apnic.net -l -i mb MAINT-CN-CRTC > /var/crtc

  打开获取后的文件可以看到里面的信息非常详细,甚至可以看到各个分公司的负责人、电话、电子邮件等等信息。如果想得到一份整齐干净的IP地址段文件,只要用grep和awk简单过滤就可以了。

posted @ 2009-11-11 10:11 小马歌 阅读(254) | 评论 (0)编辑 收藏
 

转载的一篇REST介绍,欢迎大家去原文出处去看精彩评论:
http://www.javaeye.com/topic/70113

REST的基本思想。[Fielding]把REST形式化地定义为一种架构风格(architecture style),它有架构元素(element)和架构约束(constraint)组成。这些概念比较晦涩难懂,而且我们做工程的往往并不需要形而上的理解。我们只知道,REST是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。REST提出了一些设计概念和准则:

网络上的所有事物都被抽象为资源(resource);
每个资源对应一个唯一的资源标识(resource identifier);
通过通用的连接器接口(generic connector interface)对资源进行操作;
对资源的各种操作不会改变资源标识;
所有的操作都是无状态的(stateless)。
对于当今最常见的网络应用来说,resource identifier是url,generic connector interface是HTTP,第4条准则就是我们常说的url不变性。这些概念中的resouce最容易使人产生误解。resouce所指的并不是数据,而是数据+特定的表现形式(representation),这也是为什么REST的全名是Representational State Transfer的原因。举个例子来说,“本月卖得最好的10本书”和“你最喜欢的10本书”在数据上可能有重叠(有一本书即卖得好,你又喜欢),甚至完全相同。但是它们的representation不同,因此是不同的resource。

REST之所以能够简化开发,是因为其引入的架构约束,比如Rails 1.2中对REST的实现默认把controller中的方法限制在7个:index、show、new、edit、create、update和 destory,这实际上就是对CURD的实现。更进一步讲,Rails(也是当今大部分网络应用)使用HTTP作为generic connector interface,HTTP则把对一个url的操作限制在了4个之内:GET、POST、PUT和DELETE。

REST之所以能够提高系统的可伸缩性,是因为它强制所有操作都是stateless的,这样就没有context的约束,如果要做分布式、做集群,就不需要考虑context的问题了。同时,它令系统可以有效地使用pool。REST对性能的另一个提升来自其对client和server任务的分配: server只负责提供resource以及操作resource的服务,而client要根据resource中的data和 representation自己做render。这就减少了服务器的开销。

既然REST有这样的好处,那我们应该义无反顾地拥抱它啊!目前一些大牛(像DHH)都已经开始投入到了REST的世界,那我们这些人应该做什么或者说思考写什么你呢?我觉得我们应该思考两个问题:

如何使用REST;
REST和MVC的关系。
第一个问题假设REST是我们应该采用的架构,然后讨论如何使用;第二个问题则要说明REST和当前最普遍应用的MVC是什么关系,互补还是取代?

我们先来谈谈第一个问题,如何使用REST。我感觉,REST除了给我们带来了一个崭新的架构以外,还有一个重要的贡献是在开发系统过程中的一种新的思维方式:通过url来设计系统的结构。根据REST,每个url都代表一个resource,而整个系统就是由这些resource组成的。因此,如果 url是设计良好的,那么系统的结构就也应该是设计良好的。对于非高手级的开发人员来说,考虑一个系统如何架构总是一个很抽象的问题。敏捷开发所提倡的 Test Driven Development,其好处之一(我觉得是最大的好处)就是可以通过testcase直观地设计系统的接口。比如在还没有创建一个class的时候就编写一个testcase,虽然设置不能通过编译,但是testcase中的方法调用可以很好地从class使用者的角度反映出需要的接口,从而为 class的设计提供了直观的表现。这与在REST架构中通过url设计系统结构非常类似。虽然我们连一个功能都没有实现,但是我们可以先设计出我们认为合理的url,这些url甚至不能连接到任何page或action,但是它们直观地告诉我们:系统对用户的访问接口就应该是这样。根据这些url,我们可以很方便地设计系统的结构。

让我在这里重申一遍:REST允许我们通过url设计系统,就像Test Driven Development允许我们使用testcase设计class接口一样。
(我的观点:测试驱动开发,通过测试来设计接口是个方法。通过url设计系统,需要考虑并抽象出所有用户的需求。这需要很强的抽象能力。。。并不像TDD那样简单。)

OK,既然url有这样的好处,那我们就着重讨论一下如何设计url。网络应用通常都是有hierarchy的,像棵大树。我们通常希望url也能反映出资源的层次性。比如对于一个blog应用:/articles表示所有的文章,/articles/1表示id为1的文章,这都比较直观。遗憾的是,网络应用的资源结构永远不会如此简单。因此人们常常会问这样一个问题:RESTful的url能覆盖所有的用户请求吗?比如,login如何RESTful?search如何RESTful?

从REST的概念上来看,所有可以被抽象为资源的东东都可以使用RESTful的url。因此对于上面的两个问题,如果login和search可以被抽象为资源,那么就可以使用RESTful的url。search比较简单,因为它会返回搜索结果,因此可以被抽象为资源,并且只实现index方法就可以了(只需要显示搜索结果,没有create、destory之类的东西)。然而这里面也有一个问题:search的关键字如何传给server? index方法显然应该使用HTTP GET,这会把关键字加到url后面,当然不符合REST的风格。要解决这个问题,可以把每次search看作一个资源,因此要创建create和 index方法,create用来在用户点击“搜索”按钮是通过HTTP POST把关键字传给server,然后index则用来显示搜索结果。这样一来,我们还可以记录用户的搜索历史。使用同样的方法,我们也可以对 login应用REST,即每次login动作是一个资源。

现在,我们来复杂一些的东东。如何用url表达“category为ruby的article”?一开始可能想到的是 /category/ruby/articles,这种想法很直观。但是我觉得里面的category是不需要的,我们可以直接把“/ruby”理解为 “category是ruby”,也就是说“ruby”出现的位置说明了它指的就是category。OK,/ruby/articles,单单从这个 url上看,我们能获得多少关于category的信息呢?显然category隐藏在了url后面,这样做到底好不好,应该是仁者见仁,智者见智了。对于如何表达category这样的东西,我还没想出很好的方式,大家有什么好idea,可以一起讨论。

另外还有一种url形式,它对应到程序中的继承关系。比如product是一个父类,book和computer是其子类。那么所有产品的url应该是 /products,所有书籍的url应该是/books,所有电脑的url应该是/computers。这一想法就比较直观了,而且再次验证了url可以帮助我们进行设计的论点。

让我再说明一下我的想法:如果每个用户需求都可以抽象为资源,那么就可以完全使用REST。
(我的观点:每个用户需求都可以抽象为资源吗?是不是有点困难了,MVC和REST混合模式有什么不好,Rails做的挺好。)
由此看来,使用REST的关键是如何抽象资源,抽象得越精确,对REST的应用就越好。因此,如何改变我们目前根深蒂固的基于action的思想是最重要的。
(也许是该换一种思维模式,但是哪有那么简单)
有了对第一个问题的讨论,第二个问题就容易讨论多了。REST会取代MVC吗?还是彼此是互补关系(就像AOP对于OOP)?答案是It depends!如果我们可以把所有的用户需求都可以抽象为资源,那么MVC就可以推出历史的舞台了。如果情况相反,那么我们就需要混合使用REST和 MVC。
(也许在小的项目中,需求简单的项目中可以完全用REST替代,但是需求变更以后呢,我们当初抽象的资源就不能用了吧,应付变更的情况会不会很复杂?)
当然,这是非常理想的论断。可能我们无法找到一种方法可以把所有的用户需求都抽象为资源,因为保证这种抽象的完整性(即真的是所有需求都可以)需要形式化的证明。而且即使被证明出来了,由于开发人员的能力和喜好不同,MVC肯定也会成为不少人的首选。但是对于希望拥抱REST的人来说,这些都没有关系。只要你开发的系统所设计的问题域可以被合理地抽象为资源,那么REST就会成为你的开发利器。

所以,所有希望拥抱REST的朋友们,赶快训练自己如何带上资源的眼镜看世界吧,这才是REST的核心所在

posted @ 2009-11-10 12:31 小马歌 阅读(243) | 评论 (0)编辑 收藏
 
< 期刊
跳轉到: 導航 , 搜索跳转到: 导航 , 搜索

目錄目录

[隱藏]

[ 編輯 ] Single Page Application [ 编辑 ] Single Page Application

[ 編輯 ] 所依賴的Web技術 [ 编辑 ] 所依赖的Web技术

AjaxAsynchorous Javascript And XML ,早期Ajax被用作來傳輸單純的HTML或是XML,並且利用DOM的innerHTML屬性來更新部分HTML內容。 AjaxAsynchorous Javascript And XML ,早期Ajax被用作来传输单纯的HTML或是XML,并且利用DOM的innerHTML属性来更新部分HTML内容。 如今Ajax在SPA中被當作重要的傳輸媒介(Transport),無論範本資料到應用程式資料,都是利用Ajax在背景傳輸完後,再由Javascript Template來產生HTML。如今Ajax在SPA中被当作重要的传输媒介(Transport),无论范本资料到应用程式资料,都是利用Ajax在背景传输完后,再由Javascript Template来产生HTML。

JSONJavascript Simple Object Notation ,在標準的Javascript語法中,以{ }及[ ]這兩個語法,可以宣告物件與陣列,並可以使用eval函式將他轉換為Javascript物件。 JSONJavascript Simple Object Notation ,在标准的Javascript语法中,以{ }及[ ]这两个语法,可以宣告物件与阵列,并可以使用eval函式将他转换为Javascript物件。 例如object=eval("({a:'b'})"),此時object物件便有一個屬性"a"其值為"b"。例如object=eval("({a:'b'})"),此时object物件便有一个属性"a"其值为"b"。 在SPA中,JSON被運用來作為一種資料格式,藉此取代複雜的XML,以節省頻寬。在SPA中,JSON被运用来作为一种资料格式,借此取代复杂的XML,以节省频宽。 而傳輸的JSON資料又可以快速還原為javascrip物件,又更節省程式執行的時間。而传输的JSON资料又可以快速还原为javascrip物件,又更节省程式执行的时间。

在SPA中, HTML DOM是一個最重要的元素,尤其是DIV及SPAN等Container的操作更是。在SPA中, HTML DOM是一个最重要的元素,尤其是DIV及SPAN等Container的操作更是。 由於絕大多數的畫面都不進行任何換頁的動作,程式裡大部分都是在控制DOM及Container。由于绝大多数的画面都不进行任何换页的动作,程式里大部分都是在控制DOM及Container。 而對於A (Anchor)而言,href裡的URL也沒有太大意義,多半都是在onclick裡寫javascript,或是用Javascript函式庫去bind onclick事件。而对于A (Anchor)而言,href里的URL也没有太大意义,多半都是在onclick里写javascript,或是用Javascript函式库去bind onclick事件。 由於直接呼叫瀏覽器提供的DOM函式庫功能,會遇到像IE一樣不符合W3C規格的問題。由于直接呼叫浏览器提供的DOM函式库功能,会遇到像IE一样不符合W3C规格的问题。 要選用一個合適的Javascript擴充函式庫,如Prototype.js或是jQuery,如此才不會有太多跨瀏覽器問題。要选用一个合适的Javascript扩充函式库,如Prototype.js或是jQuery,如此才不会有太多跨浏览器问题。

CSS在以往的Web應用程式中,多半都拿來當作畫面的修飾,布景主題或是顏色特校。 CSS在以往的Web应用程式中,多半都拿来当作画面的修饰,布景主题或是颜色特校。 但在SPA中,必須要熟悉CSS的Dimentation (長寬控制), Classification (顯示行為), Positioning (定位)。但在SPA中,必须要熟悉CSS的Dimentation (长宽控制), Classification (显示行为), Positioning (定位)。 在無法換頁的狀況下, 只能靠著移動,隱藏,顯示這些方法來控制畫面的元素。在无法换页的状况下,只能靠着移动,隐藏,显示这些方法来控制画面的元素。 如果要瞭解這些進階CSS的主題,都可以在w3school裡的教學找到。如果要了解这些进阶CSS的主题,都可以在w3school里的教学找到。

Trimpath是Google為了SPA而開發出來的一個函式庫集合,也可以說是Rails的Javascript版。 Trimpath是Google为了SPA而开发出来的一个函式库集合,也可以说是Rails的Javascript版。 如果要撰寫下面所說第一種SPA,就必須利用到Trimpath的全部,而第二種只需要用到Trimpath Javascript Template即可。如果要撰写下面所说第一种SPA,就必须利用到Trimpath的全部,而第二种只需要用到Trimpath Javascript Template即可。 Javascript Template(JST)如同PHP的Smarty一樣,是標準的範本技術,只是採用的語言是Javascript。 Javascript Template(JST)如同PHP的Smarty一样,是标准的范本技术,只是采用的语言是Javascript。 為了撰寫SPA,必須要好好地運用JST。为了撰写SPA,必须要好好地运用JST。

快取的機制在SPA也相當的重要,為了達到讓使用者感受到程式的反應快速,就必須應用多方面的快取。快取的机制在SPA也相当的重要,为了达到让使用者感受到程式的反应快速,就必须应用多方面的快取。

  • View Cache:SPA中的View就是指已經顯示出來的HTML,很多常用,而不需要經常改變的HTML,就可以將放在Container(DIV或SPAN)裡。 View Cache:SPA中的View就是指已经显示出来的HTML,很多常用,而不需要经常改变的HTML,就可以将放在Container(DIV或SPAN)里。 不用的時候就隱藏,需要的時候就顯示。不用的时候就隐藏,需要的时候就显示。 而另一種方式是可以用z-index將選單或是清單的Container放在最下層,而要回到這個清單的時候就將蓋在其上的container隱藏。而另一种方式是可以用z-index将选单或是清单的Container放在最下层,而要回到这个清单的时候就将盖在其上的container隐藏。
  • Template Cache:一般來說JST的範本資料只要讀取一次就可以,又因為這些只是字串,可以直接就存在Hash裡。 Template Cache:一般来说JST的范本资料只要读取一次就可以,又因为这些只是字串,可以直接就存在Hash里。
  • Javascript Cache:在我提出的SPA實做中,有一個特性是將各個JST的「行為」程式碼分開,就如同sap.net將aspx與cs檔分開的作法一樣。 Javascript Cache:在我提出的SPA实做中,有一个特性是将各个JST的「行为」程式码分开,就如同sap.net将aspx与cs档分开的作法一样。 而如果採用這個作法,不需要重複讀取的javascript就必須要快取。而如果采用这个作法,不需要重复读取的javascript就必须要快取。
  • Data Cache:資料快取是最難的一部份,牽扯到了快取一致性的問題。 Data Cache:资料快取是最难的一部份,牵扯到了快取一致性的问题。 而現在在javascript中並沒有對於XML或JSON的資料快取解決方案,未來如果能夠有這樣的函式庫,就能夠更提升整體的效率。而现在在javascript中并没有对于XML或JSON的资料快取解决方案,未来如果能够有这样的函式库,就能够更提升整体的效率。

以上說明的都是Client Side所必須要使用到的技術,而Server Side的技術多半與Service Design息息相關。以上说明的都是Client Side所必须要使用到的技术,而Server Side的技术多半与Service Design息息相关。

RESTRepresentational State Transfer ,他比較像是一種設計樣式(Design Pattern),而不是Web技術。 RESTRepresentational State Transfer ,他比较像是一种设计样式(Design Pattern),而不是Web技术。 在以往Web應用程式的規劃中,URL並不完全具有意義,傳輸的內容型態多半是HTML,而HTTP的各種動作也並未完全利用。在以往Web应用程式的规划中,URL并不完全具有意义,传输的内容型态多半是HTML,而HTTP的各种动作也并未完全利用。 在SPA中,由於需要在不同時間傳輸各種資料,如HTML範本,JS範本,或是XML及JSON資料。在SPA中,由于需要在不同时间传输各种资料,如HTML范本,JS范本,或是XML及JSON资料。 此時REST的設計技巧就可以節省下很多重複的命名,而讓程式碼整體更有意義。此时REST的设计技巧就可以节省下很多重复的命名,而让程式码整体更有意义。 支援REST設計樣式的Web Framework如Ruby On Rails,讓整體設計較為簡單。支援REST设计样式的Web Framework如Ruby On Rails,让整体设计较为简单。

[ 編輯 ] SPA的架構 [ 编辑 ] SPA的架构

SPA就分類而言,算是RIA的一種,只是不採用任何的sandbox而已。 SPA就分类而言,算是RIA的一种,只是不采用任何的sandbox而已。 典型的SPA是不需要任何的後端的Web Service或是Offline Database,只需要一個htm檔或是一個網頁就能夠運作,如微軟的HyperText Application(HTA)就是,但還是缺乏完善的資料儲存能力。典型的SPA是不需要任何的后端的Web Service或是Offline Database,只需要一个htm档或是一个网页就能够运作,如微软的HyperText Application(HTA)就是,但还是缺乏完善的资料储存能力。

spa_01.png

第一種SPA程式,如同著名的Google Reader離線版,具有一個離線資料庫與一個同步管理程式,在有網路連線的時候,會將資料同步回線上的資料庫。第一种SPA程式,如同著名的Google Reader离线版,具有一个离线资料库与一个同步管理程式,在有网路连线的时候,会将资料同步回线上的资料库。 這個最大的優點就是完全利用了Client的CPU資源,使用者雖然看見的是網頁,但卻是在使用在本機執行的獨立應用程式,因此速度是相當流暢。这个最大的优点就是完全利用了Client的CPU资源,使用者虽然看见的是网页,但却是在使用在本机执行的独立应用程式,因此速度是相当流畅。 比起一般的動態網頁,這樣子的使用體驗更能夠顛覆一般人對於「網頁」的看法,而逐漸瞭解何謂Web應用程式。比起一般的动态网页,这样子的使用体验更能够颠覆一般人对于「网页」的看法,而逐渐了解何谓Web应用程式。 另一個例子是使用Trimpath函式庫撰寫成的NextAction,是一個多功能的ToDo List。另一个例子是使用Trimpath函式库撰写成的NextAction,是一个多功能的ToDo List。

spa_02.png

另一種就是較簡單的SPA,不具有離線瀏覽的能力,但是承襲了使用javascript的高效能。另一种就是较简单的SPA,不具有离线浏览的能力,但是承袭了使用javascript的高效能。 必須提及的是Server Side並不是採用XML,而是可以快速轉換為Javascript物件的JSON,來當作Web Service。必须提及的是Server Side并不是采用XML,而是可以快速转换为Javascript物件的JSON,来当作Web Service。 如此Server Side的語言只需要具備能夠快速將物件serialize成JSON的能力即可。如此Server Side的语言只需要具备能够快速将物件serialize成JSON的能力即可。

[ 編輯 ] Rails與SPA [ 编辑 ] Rails与SPA

這個小節所要說明的是相當技術性的部分,無法說明的太詳細,有興趣的讀者可以寫mail一起討論。这个小节所要说明的是相当技术性的部分,无法说明的太详细,有兴趣的读者可以写mail一起讨论。 為了簡化觀念,我使用Sequence Diagram來說明Rails要如何應用在SPA上:为了简化观念,我使用Sequence Diagram来说明Rails要如何应用在SPA上:

  • URLRequester是一個javascript函式,主要的工作就是以REST方式對一個URL進行不同Content-Type的Request,並且將回傳的資料產生HTML,並填到container裡顯示出來。 URLRequester是一个javascript函式,主要的工作就是以REST方式对一个URL进行不同Content-Type的Request,并且将回传的资料产生HTML,并填到container里显示出来。
  • HTTPRequest在這裡作為進行Ajax呼叫的傳輸媒介。 HTTPRequest在这里作为进行Ajax呼叫的传输媒介。
  • RailsController表示伺服器端對應至特定URL的程式,在這裡也必須使用REST方式來回應。 RailsController表示伺服器端对应至特定URL的程式,在这里也必须使用REST方式来回应。 也因此如果要求content-type為HTML的時候就傳JST範本,要求JSON的時候就傳資料,要求javascript的時候就傳javascript文件。也因此如果要求content-type为HTML的时候就传JST范本,要求JSON的时候就传资料,要求javascript的时候就传javascript文件。
  • RailsModel代表伺服器端的資料庫,在要求資料的時候,勢必定要連資料庫來取得資料的。 RailsModel代表伺服器端的资料库,在要求资料的时候,势必定要连资料库来取得资料的。 而回傳的時候,就將ruby物件serialize成JSON。而回传的时候,就将ruby物件serialize成JSON。

spa_03.png

  1. Client呼叫URLRequester函式,例如http://servername/controller/action/id。 Client呼叫URLRequester函式,例如http://servername/controller/action/id。
  2. HTTPRequest送出一個要求,並指定Content-Type為HTML。 HTTPRequest送出一个要求,并指定Content-Type为HTML。
  3. RailsController接到指定的URL,並執行controller#action。 RailsController接到指定的URL,并执行controller#action。 我將JST寫在rhtml裡面,JST基本上也是html,不過是範本的標籤換成{}而已。我将JST写在rhtml里面,JST基本上也是html,不过是范本的标签换成{}而已。 因為REST設計方式會因為指定的content-type回傳對應的型態,此時直接將內容的JST文件傳回。因为REST设计方式会因为指定的content-type回传对应的型态,此时直接将内容的JST文件传回。
  4. 在前面我有提及快取的重要性,所以這裡就快取住這個JST,下次要求同樣的內容就可以直接使用而不用重複傳輸。在前面我有提及快取的重要性,所以这里就快取住这个JST,下次要求同样的内容就可以直接使用而不用重复传输。
  5. 對於同一個URL,使用HTTPRequest送出要求,並指定Content-Type為JSON。对于同一个URL,使用HTTPRequest送出要求,并指定Content-Type为JSON。
  6. 因為REST的特性,這次會執行到content-type為JSON時的程式碼,接著就可以照一般方式使用Rails Model讀取資料庫。因为REST的特性,这次会执行到content-type为JSON时的程式码,接着就可以照一般方式使用Rails Model读取资料库。
  7. Model傳回的Ruby物件,當然就要轉換成JSON回傳。 Model传回的Ruby物件,当然就要转换成JSON回传。 而在ruby中相當簡單地便是呼叫to_json就可以轉換了。而在ruby中相当简单地便是呼叫to_json就可以转换了。
  8. 回傳的JSON,併和剛剛快取的JST,使用trimpath javascript template函式庫產生成HTML,並更新至container裡。回传的JSON,并和刚刚快取的JST,使用trimpath javascript template函式库产生成HTML,并更新至container里。
  9. 在這裡我採用行為javascript程式碼與範本分離的方式,所以還是以HTTPRequest再次傳送要求,並將Content-Type指定為Javascript。在这里我采用行为javascript程式码与范本分离的方式,所以还是以HTTPRequest再次传送要求,并将Content-Type指定为Javascript。
  10. 同樣地根據要求的content-type,會回傳javascript文件。同样地根据要求的content-type,会回传javascript文件。
  11. 快取,並呼叫eval執行回傳的javascript。快取,并呼叫eval执行回传的javascript。
  12. 將container顯示在想要放置的畫面區域。将container显示在想要放置的画面区域。

[ 編輯 ] 結論 [ 编辑 ] 结论

許多人都在說Web 2.0可能又是另一次的泡沫化,這個熱潮怎樣開始的,又怎樣消退的,也是相當明顯。许多人都在说Web 2.0可能又是另一次的泡沫化,这个热潮怎样开始的,又怎样消退的,也是相当明显。 網路上對於各種新技術名詞的炒作,將不同應用層次的技術,全部攪和在一起說明或稱做是最終解決方案,也模糊了使用者的眼睛。网路上对于各种新技术名词的炒作,将不同应用层次的技术,全部搅和在一起说明或称做是最终解决方案,也模糊了使用者的眼睛。 那麼,在這個時代,到底還有什麼可以相信,可以學習的?那么,在这个时代,到底还有什么可以相信,可以学习的?

唯一能夠做的就是重新審視這些技術,瞭解因果。唯一能够做的就是重新审视这些技术,了解因果。 就可以知道哪些作法是適合用在自己現在的專案,那些是本質相同的,哪些是跨大其詞的。就可以知道哪些作法是适合用在自己现在的专案,那些是本质相同的,哪些是跨大其词的。 根本的觀念正確,就不需要擔心這些延伸的技術是否會有誤解或誤用。根本的观念正确,就不需要担心这些延伸的技术是否会有误解或误用。

posted @ 2009-11-10 12:09 小马歌 阅读(1246) | 评论 (0)编辑 收藏
 
在Web Service, Ajax, Web 2.0, REST等Web应用与技术话题热潮,带动许多第二代的Web开发技术成长之后,这些话题也渐渐地消退。 不過許多人可能不曾發現,其實這些技術名詞,是在慢慢地顯露一點:Web應用程式逐漸從Server Side轉移到Client Side,也就是瀏覽器身上。不过许多人可能不曾发现,其实这些技术名词,是在慢慢地显露一点:Web应用程式逐渐从Server Side转移到Client Side,也就是浏览器身上。

本篇文章要從以往的Server Side Web應用程式,其開發方式與演進來介紹Single Page Application(SPA) 與現今所有Web技術。本篇文章要从以往的Server Side Web应用程式,其开发方式与演进来介绍Single Page Application(SPA)与现今所有Web技术。

我在Web 2.0過去,現在與未來介紹Ruby On Rails都有提到一些Web技術的演進,比較明顯的趨勢就是從靜態到動態頁面,而設計的方式也更程式化。我在Web 2.0过去,现在与未来介绍Ruby On Rails都有提到一些Web技术的演进,比较明显的趋势就是从静态到动态页面,而设计的方式也更程式化。 而在Web2.0:過去,現在及未來«就是愛程式也有讀者在前言提到,技術並不是將一個名詞安上去就好。而在Web2.0:过去,现在及未来«就是爱程式也有读者在前言提到,技术并不是将一个名词安上去就好。 我相當贊同這句話,因此也在這篇文章中希望來做個總整理,以技術及歷史來看看Web是怎樣成長的。我相当赞同这句话,因此也在这篇文章中希望来做个总整理,以技术及历史来看看Web是怎样成长的。

本篇文章也同步發佈於http://kiwi.csie.chu.edu.tw/blog/archives/156本篇文章也同步发布于http://kiwi.csie.chu.edu.tw/blog/archives/156

目錄目录

[隱藏]

[ 編輯 ] Web應用程式的演進 [ 编辑 ] Web应用程式的演进

[ 編輯 ] 動態網頁 [ 编辑 ] 动态网页

儘管Web並不是一個三言兩語能拿個版本號碼來解釋,但實際上Web技術確實有些明顯的分隔點。尽管Web并不是一个三言两语能拿个版本号码来解释,但实际上Web技术确实有些明显的分隔点。

最早期我們熟知的就是靜態網頁,這應該沒啥問題。最早期我们熟知的就是静态网页,这应该没啥问题。 儘管在2000前,php,asp就開始流行,坊間的書上也都稱之為動態網頁。尽管在2000前,php,asp就开始流行,坊间的书上也都称之为动态网页。 而我在此提及的動態網頁程式 ,實際上卻是從php4釋出的那一年開始。而我在此提及的动态网页程式 ,实际上却是从php4释出的那一年开始。 這邊要讓大家瞭解的分界點,其實是php4開始被許多商業公司所採用,而軟體的形式也更為套裝化,而不再像之前大家認定的「動態」網頁僅僅只是拿來完成一些簡單的區塊來與一般的靜態網頁整合。这边要让大家了解的分界点,其实是php4开始被许多商业公司所采用,而软体的形式也更为套装化,而不再像之前大家认定的「动态」网页仅仅只是拿来完成一些简单的区块来与一般的静态网页整合。

在2004年的時候,Web Framework的產生,創造了Web應用程式另一個新的高峰。在2004年的时候,Web Framework的产生,创造了Web应用程式另一个新的高峰。 而在這個時候也開始有一些Rich Web Client概念的雛形了。而在这个时候也开始有一些Rich Web Client概念的雏形了。 我將Ruby On Rails定為一個分界點是因為,他顛覆了傳統動態網頁還在使用設計方式,而改用MVC。我将Ruby On Rails定为一个分界点是因为,他颠覆了传统动态网页还在使用设计方式,而改用MVC。 但要注意的是Ruby On Rails儘管整合了Ajax與進階Javascript函式庫,但還是沒有改變回傳完整或部分HTML的方式,意思便是HTML的產生始終在Server Side。但要注意的是Ruby On Rails尽管整合了Ajax与进阶Javascript函式库,但还是没有改变回传完整或部分HTML的方式,意思便是HTML的产生始终在Server Side。

[ 編輯 ] Rich Internet Application [ 编辑 ] Rich Internet Application

一直到現在,有相當多的Web應用程式,都還是維持使用URL來切換各種功能與畫面。一直到现在,有相当多的Web应用程式,都还是维持使用URL来切换各种功能与画面。 而這些以「頁面」為主的程式,並不太需要控制DOM,就不常遇到跨瀏覽器問題。而这些以「页面」为主的程式,并不太需要控制DOM,就不常遇到跨浏览器问题。 然而自從Firefox逐漸也在市場佔有一席之地,Javascript的應用普及之後,跨瀏覽器問題也接著發生。然而自从Firefox逐渐也在市场占有一席之地,Javascript的应用普及之后,跨浏览器问题也接着发生。 為了避開各種不同的瀏覽器所帶來的問題,各大企業都獨力發展自己可以嵌入在瀏覽器的應用程式。为了避开各种不同的浏览器所带来的问题,各大企业都独力发展自己可以嵌入在浏览器的应用程式。 早期如Java Applet及微軟的的Active X,算是Rich Internet Application(RIA)的開始,但效能方面還是差強人意。早期如Java Applet及微软的的Active X,算是Rich Internet Application(RIA)的开始,但效能方面还是差强人意。

直到2004年的時候,RIA出現了兩種不同的實做方法:一種是承襲以往需要安裝額外Runtime或是在特定瀏覽器才能執行的方式,稱做Sandbox;另一種是只採用CSS,HTML,並以Javascript控制HTML DOM的Dynamic HTML方式,優點就是只需要瀏覽器就可以執行。直到2004年的时候,RIA出现了两种不同的实做方法:一种是承袭以往需要安装额外Runtime或是在特定浏览器才能执行的方式,称做Sandbox;另一种是只采用CSS,HTML,并以Javascript控制HTML DOM的Dynamic HTML方式,优点就是只需要浏览器就可以执行。 而後者也延伸出利用Offline Database或是Ajax+Web Service來傳送與儲存程式資料,並可以儲存成一個獨立頁面的Web應用程式,稱做Single Page Application(SPA)而后者也延伸出利用Offline Database或是Ajax+Web Service来传送与储存程式资料,并可以储存成一个独立页面的Web应用程式,称做Single Page Application(SPA) SPA最典型的例子,就是Gmail。 SPA最典型的例子,就是Gmail。 Google盡力克服了跨瀏覽器的問題,將Javascript發揮的淋漓盡致,讓大家驚嘆光靠純粹的Web技術竟能做到如此程度。 Google尽力克服了跨浏览器的问题,将Javascript发挥的淋漓尽致,让大家惊叹光靠纯粹的Web技术竟能做到如此程度。

而我將2005定為Sandbox RIA真正開始的年代,也是因為Adobe併購Macromedia,而有了較完整的開發環境與資源,並不是以往單純地嵌入Flash。而我将2005定为Sandbox RIA真正开始的年代,也是因为Adobe并购Macromedia,而有了较完整的开发环境与资源,并不是以往单纯地嵌入Flash。 這個契機也促使微軟改變策略,比起效能較差的Asp.Net,而拿Sliverlight作為Web下一代主力軍。这个契机也促使微软改变策略,比起效能较差的Asp.Net,而拿Sliverlight作为Web下一代主力军。

RIA或SPA都是學習歷程長,語言多又複雜的Web應用程式技術,也因此發展速度相當緩慢,但不可小看的是這些優點: RIA或SPA都是学习历程长,语言多又复杂的Web应用程式技术,也因此发展速度相当缓慢,但不可小看的是这些优点:

  • 相較以往在Server上產生HTML並回傳至瀏覽器,任何畫面皆利用瀏覽器本身或附加的功能來產生。相较以往在Server上产生HTML并回传至浏览器,任何画面皆利用浏览器本身或附加的功能来产生。 形同於借用了Client Side CPU的運算資源,減少Server成本。形同于借用了Client Side CPU的运算资源,减少Server成本。 使用者感受到的互動性與回應速度皆有大幅的提升。使用者感受到的互动性与回应速度皆有大幅的提升。
  • 由於Server並不是回傳複雜龐大的HTML,而是純粹傳輸資料,使用的頻寬也相對變小。由于Server并不是回传复杂庞大的HTML,而是纯粹传输资料,使用的频宽也相对变小。
  • Server Side除了使用傳統XML Web Service,更可以採用REST,讓Client的應用程式可以更快速掌握資料的新增修改刪除(CRUD)。 Server Side除了使用传统XML Web Service,更可以采用REST,让Client的应用程式可以更快速掌握资料的新增修改删除(CRUD)。
  • 能夠快速Mashup其他的Web應用程式資源,又能擁有高速的執行效能。能够快速Mashup其他的Web应用程式资源,又能拥有高速的执行效能。

下表列出了Web技術的演進,要注意到後三種技術集合,其時間是並行的:下表列出了Web技术的演进,要注意到后三种技术集合,其时间是并行的:

靜態網頁 静态网页 動態網頁程式 动态网页程式 Web應用程式 Web应用程式 Rich Internet Application with Sandbox Rich Internet Application with Sandbox Single Page Application Single Page Application
時期 时期 2000以前 2000以前 2000(php4釋出)~2004 2000(php4释出)~2004 2004(Ruby On Rails釋出)以後 2004(Ruby On Rails释出)以后 2005(macromedia被adobe併購)以後 2005(macromedia被adobe并购)以后 2004(Gmail釋出beta)以後 2004(Gmail释出beta)以后
表現層 表现层 CSS CSS CSS,HTML,Javascript CSS,HTML,Javascript CSS,HTML,Javascript CSS,HTML,Javascript Flash, Sliverlight Flash, Sliverlight CSS,HTML(DOM) CSS,HTML(DOM)
邏輯層 逻辑层 Javascript Javascript Template或自行撰寫 Template或自行撰写 Web Framework Web Framework Action Script, C# Action Script, C# Javascript或是撰寫Web Service的語言 Javascript或是撰写Web Service的语言
資料層 资料层 HTML HTML Database(SQL) Database(SQL) Database(ORM) Database(ORM) Database(ORM) Database(ORM) Offline Database, Web Service Offline Database, Web Service
開發方式 开发方式 網頁編輯程式网页编辑程式 整合HTML及Server Side語言的編輯器整合HTML及Server Side语言的编辑器 整合Web Framework的IDE整合Web Framework的IDE 整合Sandbox的IDE整合Sandbox的IDE 整合Server Side與Client Side語言的IDE整合Server Side与Client Side语言的IDE
運算資源 运算资源 所有資料直接透過Web Server送出,除了硬碟讀取,幾乎不需要額外的運算所有资料直接透过Web Server送出,除了硬碟读取,几乎不需要额外的运算 因為使用了Server Side語言來Render表現層,運算多半會消耗在Server因为使用了Server Side语言来Render表现层,运算多半会消耗在Server 因為使用了Server Side語言來Render表現層,運算多半會消耗在Server因为使用了Server Side语言来Render表现层,运算多半会消耗在Server 運算資源平均被分散在Server及Client,但Client需要Sandbox去執行,所以會消耗更多CPU資源运算资源平均被分散在Server及Client,但Client需要Sandbox去执行,所以会消耗更多CPU资源 運算資源平均被分散在Server及Client运算资源平均被分散在Server及Client
資料格式 资料格式 傳送完整的HTML传送完整的HTML 傳送完整的HTML传送完整的HTML 傳送部分或完整的HTML传送部分或完整的HTML 只需第一次傳送HTML及內嵌程式(Flash或Sliverlight),其餘傳送XML只需第一次传送HTML及内嵌程式(Flash或Sliverlight),其余传送XML 只需第一次傳送HTML及Javascript,其餘可傳送XML或JSON只需第一次传送HTML及Javascript,其余可传送XML或JSON
優點 优点 簡單易學简单易学 學習同一種Server Side語言,搭配簡單的HTML,CSS,JS觀念便能夠有成果学习同一种Server Side语言,搭配简单的HTML,CSS,JS观念便能够有成果 整合Ajax或進階Javascript函式庫,REST及MVC。整合Ajax或进阶Javascript函式库,REST及MVC。 使得設計概念更為物件導向化使得设计概念更为物件导向化 使用者介面反應快速,變化多且美觀。使用者介面反应快速,变化多且美观。 兼顧視窗程式的反應速度,且能部分相容傳統HTML應用。兼顾视窗程式的反应速度,且能部分相容传统HTML应用。 完全相容傳統HTML應用,及任何可能的Web應用程式Mashup。完全相容传统HTML应用,及任何可能的Web应用程式Mashup。 可以採用不同的傳輸方式,併和REST及瀏覽器快取來節省頻寬,使得整體反應相當快速。可以采用不同的传输方式,并和REST及浏览器快取来节省频宽,使得整体反应相当快速。
缺點 缺点 無法讓使用者儲存任何應用程式資料,任何資料必須藉由人工設計无法让使用者储存任何应用程式资料,任何资料必须藉由人工设计 對於龐大的應用程式,便得花上大量的Server成本。对于庞大的应用程式,便得花上大量的Server成本。 程式反應速度受限於伺服器負載,需要叢集架構來彌補。程式反应速度受限于伺服器负载,需要丛集架构来弥补。 設計方式更為簡單快速,但相對於傳統的動態網頁程式付出更大的Server成本。设计方式更为简单快速,但相对于传统的动态网页程式付出更大的Server成本。 除了CSS,HTML,JS以外還需學習一兩種不同的語言才能進行開發。除了CSS,HTML,JS以外还需学习一两种不同的语言才能进行开发。 RIA通常程式資料較大,在開始使用前必須等候一段下載時間。 RIA通常程式资料较大,在开始使用前必须等候一段下载时间。 除了CSS,HTML,JS以外還需學習一種撰寫Web Service的語言。除了CSS,HTML,JS以外还需学习一种撰写Web Service的语言。 需要相當熟悉DOM及CSS,也需考慮瀏覽器的差異,開發起來相對地困難許多。需要相当熟悉DOM及CSS,也需考虑浏览器的差异,开发起来相对地困难许多。

表現層的演進可以得知,不管是RIA利用sandbox或者是SPA利用DHTML作為表現層,相較起來傳統以文字HTML拼湊出畫面的作法已經無法符合使用者的需求。表现层的演进可以得知,不管是RIA利用sandbox或者是SPA利用DHTML作为表现层,相较起来传统以文字HTML拼凑出画面的作法已经无法符合使用者的需求。 更動態,更彈性的作法才能讓使用者獲得操作感。更动态,更弹性的作法才能让使用者获得操作感。

而在邏輯層上,演進到了SPA則是變得較為複雜。而在逻辑层上,演进到了SPA则是变得较为复杂。 如果是使用Web Service作為Server Side,除了必須撰寫該語言外,也還是得撰寫Javascript來控制畫面的呈現。如果是使用Web Service作为Server Side,除了必须撰写该语言外,也还是得撰写Javascript来控制画面的呈现。 而這也影響到開發方式,以往的編輯器多半著重一種主要的語言上,但現在的Web IDE多半都可以完整的處理所有瀏覽器的語言,及多種Server Side語言。而这也影响到开发方式,以往的编辑器多半着重一种主要的语言上,但现在的Web IDE多半都可以完整的处理所有浏览器的语言,及多种Server Side语言。 例如Aptana IDE就是最好的例子。例如Aptana IDE就是最好的例子。

資料層的演進就比較簡單,直到RIA的時期,還是相當依賴傳統的Database。资料层的演进就比较简单,直到RIA的时期,还是相当依赖传统的Database。 但SPA的時期,就可以採用Offline Database,這裡指的就是Google Gears但SPA的时期,就可以采用Offline Database,这里指的就是Google Gears 但如果要使用傳統的Online Database,全部都尚未有Javascript的Client,就必須透過Web Service來轉換資料。但如果要使用传统的Online Database,全部都尚未有Javascript的Client,就必须透过Web Service来转换资料。 而在SQL到ORM的演進上,雖然使用物件導向的作法減緩執行速度,但相對降低開發難度,帶來更大的價值。而在SQL到ORM的演进上,虽然使用物件导向的作法减缓执行速度,但相对降低开发难度,带来更大的价值。

[ 編輯 ] 小結 [ 编辑 ] 小结

本篇說明了Web技術的歷史,來帶出現今的Web技術。本篇说明了Web技术的历史,来带出现今的Web技术。 下一篇要介紹的是Single Page Application與它在Ruby On Rails上的應用。下一篇要介绍的是Single Page Application与它在Ruby On Rails上的应用。

posted @ 2009-11-10 12:08 小马歌 阅读(381) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 62 63 64 65 66 67 68 69 70 下一页 Last