Quantcast
Channel: 陈成的博客
Viewing all 32 articles
Browse latest View live

(续上篇)还是不推荐用Fiddler模拟低网速

$
0
0

本来是直接在上篇文章里更新的,想想还是把他拎出来,可以让更多的人注意到。

大家都知道Fiddler是通过设置浏览器代理来实现的,而设置代理后浏览器会认为所有的连接都指向Proxy,即都是来自一个域。

如果你有针对assets和img设置不同的域名,会因为浏览器连接数的限制,使得开不开Fiddler出来的瀑布图差别很大。

见下图,测试用Cuzillion构建了一个页面,上面是没开Fiddler的,下面是开了Fiddler的。

此外,还用淘宝新首页也测了下:without-fiddler vs. with-fiddler

所以最后还是推荐大家用Network Delay Simulator,因为既然想到要模拟低网速肯定是希望得到相对较精确的用户体验。

===

Update 2010/1/12:原来《Even Faster WebSites》P208里已经有对于Fiddler的相关描述。


优化资源文件的载入方式

$
0
0

假如一个页面有4个CSS,1个JS,2张图片(CSS和JS在domain1,图片在domain2)。

工具准备:IE6/7,HttpWatch,不要开Fiddler

我们通常会按以下顺序加载:css, css, css, css, js, img, img
测试页面,瀑布图如下

但是如果我们调整下顺序:css, css, css, js, css, img, img
测试页面,瀑布图如下

可以看得,载入时间由4段变成了3段(每段2秒)。

这是利用了“link方式引入css的异步载入特性”,让最后第四个CSS和图片同步载入,让第三个CSS和JS同步载入。

注意:
IE如果安装了使用Async Pluggable Procotol的插件(如debugger bar),会导致link方式载入的CSS文件也同步下载

参考:
http://www.stevesouders.com/blog/2009/04/09/dont-use-import/

淘宝新首页开发实践PPT分享

新首页开发PPT详解 —— HTML规范

$
0
0

PPT写得过于简短,此文对PPT里的第一部分详细说明下,可以对照着看。

一、Doctype

A DOCTYPE is a mostly useless, but required, header. —— 《HTML5 Spec》

Doctype最大的作用是让浏览器用来选择渲染模式,或者说是用来触发标准模式。具体点说,就是设置正确的Doctype进入(准)标准模式,设置了错误的或者没设置Doctype则进入怪癖模式。(附:Doctype和渲染模式对照表)

理论上,标准模式和准标准模式的区别仅在于非IE6/7浏览器对于单元格里(原文说法可能有误,经测试所有图片均受影响)图片布局的不同处理。同事渔隐做了细致地测试并给出了解决方案。
Update 20100203:发现iframe的布局也受到影响,解决方法和图片一致。

还有个次要的作用是校验HTML时自动判断选择哪种规范。

另外,对于Doctype可能还存在的一些误解:

  1. 认为设置了xhtml的Doctype,使用自闭合标签,通过xhtml验证,文档就成xhtml了,浏览器会以xhtml的引擎来解析。
    事实上,浏览器还是会以HTML引擎来解析文档。目前网上通过xhtml验证(页面下方贴xhtml验证通过图标)的其实都不是真正的XHTML,而是“HTML-Compatible XHTML”。因为XHTML刚出来时,浏览器都不支持他,然后W3C为了推广XHTML语法就搞了这么个东西。 (附:给HTML文档设置XHTML Doctype是有害的)
  2. 认为strict的doctype即进入了“严格解析模式”,比如认为严格模式下标签一定要全部小写,该自闭合的元素一定要自闭合,否则页面会出错。
    事实上,doctype没有这个功能。

说到兼容问题,大家可能会奇怪IE6/7居然也支持HTML5 Doctype,难道是因为他的“预见性”。其实不是的,IE是只要doctype符合

<!doctype html xxxxxxxxxxx>

这种格式,都进入标准模式。个人猜测可能是W3C为了推广HTML5,利用了IE的这一点。

二、Charset的Meta标签

<meta http-equiv="Content-Type" content="text/html; charset=gbk" /> 

<meta charset="gbk" />

以上两种写法(均为兼容问题)的区别在于前者声明了mime-type为text/html。

所以主要问题就是这个mime-type声明能不能省去。据说,页面是text/html还是application/xhtml+xml还是其他什么的唯一决定权在于服务端的mime-type设置,所以页面上是否设置了mime-type完全没有意义。玉伯有另一种说法是mime-type有优先级,会先从取服务端,再取页面,因目前服务端基本都有设置,所以省去。(待测试考证)

三、CSS和JavaScript的引入方式

<link rel="stylesheet" href="" type="text/css" media="all" />
<style type="text/css"></style>
<script type="text/javascript" src=""></script>

这几个加删除线的属性都是默认值,故省去,详见HTML5 Spec

======

今天看了Optimizing Optimizing HTML后想到的两点:

  1. 能不能省去<link rel="shortcut icon" />?


    经测试,没设置的页面,Chrome/FF/Safari/Opera则会请求favicon.ico,而IE6/7/8不会请求。如果不想放弃IE下的这个icon,还是不要学yahoo省去这个标签。


    Update 20100130:可以省去,会写文章详细地说明下。

  2. 是否省去自闭合元素的斜线?


    一直在纠结这个问题。HTML解析器不认识XML的自闭合语法,会认为 / 是一个属性,然后因为 / 不是个合法的属性而过滤掉。省去?一直都有的,去掉可能会不适应。不省去?怎么看怎么不像HTML。

新首页开发PPT详解 —— Sprites(1)

$
0
0

PPT写得过于简短,此文对PPT里的第二部分详细说明下,可以对照着看。

占位标签

“The biggest problem with CSS sprites is memory usage.” —— vladimir,a leader in the Mozilla community

内存占用高是CSS Sprites的一大缺点,因为通常他会包含非常多的空白。http://www.wthitv.com/这个网站曾经用了张1299 * 15000的Sprites图,虽然压缩后只有26K,但这张图的内存占用达75M+之多。

所以为了更好地应用Sprites,我们要减少Sprites里空白所占的比例。

搜索新首页源码,可以发现共有38处<s></s>,42处<b></b>,他们都是用来占位的。s和b是两个废弃的标签,因为废弃,所以用起来既安全又环保。(经人提醒,b标签并没有被废弃,只是推荐优先选择strong和em)

个人觉得通过利用占位标签,可以更好地组织CSS Sprites,随心所欲地安排进行内部组织,使得Sprite图片尺寸尽可能地小,也使得空白位尽可能地少。

这里权衡下优缺点:

  • 优点:性能(内存占用少)、工作效率(省去精心计算)、成就感(图美)
  • 缺点:DOM数增加

(ps: 此法绝非我原创,此前王爷和小虎就一直在用。)

Image Sprites

以下代码可以帮助理解什么是Image Sprites:

<div style="width:100px;height:100px;overflow:hidden;">
    <img src="" width="400" height="400" style="margin:-50px 0 0 –50px;" />
</div>

(Google搜索结果页的Sprites图)

Image Sprites最经典的例子当然是Google搜索结果页的logo了,logo和CSS Sprites公用一张图。但这种做法也需要权衡,并不是在所有场景都适用:

优点
  1. 省去logo的HTTP请求
  2. 提升Sprites图的在请求序列中的位置
    有种情况我们可能都看到过:页面载入后,先是看到一个框架全好但没有背景图的页面,然后背景图一下子全部展现出来。这是因为通常背景图的请求发生在CSS载入并解析完成且HTML解析器遭遇到对应的节点时。应用此方法后,可以减缓这种情况的发生。因为1)不需要等待CSS载入,2)logo一般在HTML文档顶部,所以请求发起时间会有所提前。
缺点&问题
  1. logo不可复制
    logo作为品牌的象征,有些用户可能会希望把这张图片复制或另存下来做某些用途,但应用这种方法后会使用户得不到他们想要的。
  2. 可替换(alt)文本显示问题
    使用负值定位后,alt文本会跟着img的位置一起变化,这样我们可能在图片未载入时就看不到希望看到的替换文本了。如图:

    (推荐阅读:各浏览器alt和title的显示情况)
  3. IE渐进渲染PNG的bug(只测试IE6,IE7/8待考证)
    我们知道,默认存储的图片在浏览器中是渐进渲染的。但IE在处理PNG的渐进渲染时表现地很怪异,一张13.8K的图片,在载入11K时才渲染这么一点(如下图),然后到13.8K时全部展现,这个问题会导致Logo的展现缓慢。
     
    (感兴趣的朋友可自行用Network Simulator打开这里看效果)

Flash的Fallback Content等

$
0
0

看youa的源码发现的,原来flash可以有fallback content:

<object type="application/x-shockwave-flash" data="...ny2010.swf" width="300" height="100" >
	<param name="movie" value="...ny2010.swf" wmode="transparent">
	<param name="menu" value="false">
	<param name="wmode" value="transparent">
	<a href="..." target="_blank"><img src=".../ny2010.png" alt="..." width="300" height="100"></a>
</object>

测试了Safari 4, Firefox 3, IE6/7/8禁用Flash插件的访问,均无兼容问题。

但是却有另外两个问题,都是IE下的:

  1. Flash文件会被请求两次;
  2. Flash要等全部下载好才播放;(尚未仔细验证)

==

所以最终采各家(见文末rel)所长后用了这种写法:

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="300" height="68">
	<param name="movie" value=".../T1isRtXlNEXXXXXXXX.swf">
	<param name="wmode" value="transparent">
	<!--[if !IE]>-->
	<object type="application/x-shockwave-flash" data=".../T1isRtXlNEXXXXXXXX.swf" width="300" height="68">
	<param name="wmode" value="transparent"> <!-- for firefox -->
	<!--<![endif]-->
		<a href="http://www.taobao.com/" target="_top" style="height:43px;margin-left:56px;">
			淘宝网
			<img width="167" height="110" src=" alt="淘宝网" title="Taobao.com - 阿里巴巴旗下网站">
		</a>
	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>

==

在HTML5 Spec中找到下面两段相关文字:

Some embedded content elements can have fallback content: content that is to be used when the external resource cannot be used (e.g. because it is of an unsupported format). The element definitions state what the fallback is, if any.


Due to the algorithm above, the contents of

object

elements act as fallback content, used only when referenced resources can't be shown (e.g. because it returned a 404 error). This allows multiple

object

elements to be nested inside each other, targeting multiple user agents with different capabilities, with the user agent picking the first one it supports.

但测试证明,没有浏览器在swf文件404时启用fallback content。

==

另外使用时要注意的一点:在浏览器支持flash的情况下(99%+?),除IE家族外,其他浏览器均会发起对替换图片的请求。

==

rel:

  1. http://www.alistapart.com/articles/flashsatay
  2. http://blog.deconcept.com/swfobject/
  3. http://www.xintend.com/Article/EssuefNieCFMDbENScai.aspx
  4. http://www.w3.org/TR/html5/text-level-semantics.html#the-object-element

IE6 Jump Bug

$
0
0

记性太差,写下来备忘。

Bug 演示:
http://f2ee.googlecode.com/svn/trunk/lab/2010/css/ie6-jump-bug.html

触发此bug的dom结构如下:
relative元素(A) > haslayout元素(B) > relative|absolute元素(C)

解决方法:
避免给B加layout,或者让C直接是A的子节点
Update: 给A添加layout可解决此bug (relative元素顺手添加layout可以避免很多bug,多谢ytzong提醒)

Favicon图标小常识

$
0
0

tips:如果根目录下有favicon.ico,可省去:

<link rel="shortcut icon" ...>

==

各浏览器下favicon的请求情况Google上搜过没找到,自己动手整理了下:

  icon文件实际存在 icon文件不存在(404)
空Cache 有Cache且Cache没有过期
Ctrl+F5 F5 地址栏按回车 Ctrl+F5/F5 地址栏按回车
Chrome发送请求不发送请求发送404请求
Firefox发送请求不发送请求如果两次都返回404,第三次开始不再请求
IE8/7发送请求不发送请求如果一次都返回404,第二次开始不再请求
IE6/5x只在加入收藏夹时,会请求icon,且cache过期前不再请求
Safari发送请求/发送请求不发送请求发送404请求不发送请求
Opera发送请求/发送lastModified请求不发送请求发送404请求不发送请求

==

rel:

  1. http://msdn.microsoft.com/en-us/library/ms537656(VS.85).aspx
  2. http://annevankesteren.nl/2010/01/optimizing-html


IE6下百分比宽高设置原理

$
0
0

结构

<div id="A"><div id="B"><div id="C"></div></div></div> 

情景

  1. A 的 position 为 relative 或 absolute,或者 A 为 body (定位元素)
  2. B 的 position 不为 relative 或 absolute (非定位元素)
  3. C 的 position 为 absolute (绝对定位元素)

问题

  1. C 的百分比宽度受以下影响:
  • B 的 margin, padding
  • A 的 padding
  • C 的百分比高度受以下影响:
    • C 设置filter后,会导致百分比高度失效
  • B 设置 layout 后,C的百分比宽高即以B为参照。
  • 图解:当用户点击链接后

    [mac] 代理切换利器 - ProxySwitcher (v0.1.0) 发布

    $
    0
    0

    上周群里在讨论代理的问题,然后发现现在每次切换代理或更新pac都要这样来一遍:"偏好设置 - 网络 - 高级 - 代理 - 设置好 - 应用”,很是费鼠标,决定利用周末的时间写个代理切换的小程序,将以上重复步骤简化为点一下鼠标。

    图:

    功能介绍:

    1) 在Socks, Pac, Http, None代理之间进行切换

    2) 刷新Pac

    安装:

    下载地址

    下载后解压缩到Application目录后打开即可。

    反馈:

    如果您有任何建议或意见,请发邮件到 sorrycc#gmail.com,谢谢:)

    让前端忍不住喊爽的设计元素

    $
    0
    0


    (此文目标对象为:设计师)

    现状

    1. 视觉效果太复杂,前端一看就傻眼了,实现成本太大;
    2. 界面太简陋,仅采用一些简单元素比如直角、实色填充等实现设计。

    其实可以尽情地使用这些设计元素

    1. 水平或垂直的线性渐变
    2. 箭头及各种箭头的变型
    3. 单像素圆角
    4. 简单阴影
    5. 半透明背景

    以上这些设计元素对于前端实现来说有个相同的特点:即可以通过添加少量标签或完全利用CSS特性完成,实现成本和后期维护成本低扩展性高,且不需要任何图片。图片对前端来说就是个难搞的事情,尤其是现在关注性能的同学还需要进行sprites合并。(真的,我宁愿多加几个tag,也不愿多加一张图片)

    如果您认可优雅降级,还可针对高级浏览器选择以下设计元素

    当然,IE用户是看不到的。

    1. 圆角
    2. 复杂的阴影
    3. 文字阴影

    此外,设计师们还可以为Chrome、Safari浏览器设计一些小动画,比如鼠标移上去icon旋转下之类的。

    只有让设计师更了解前端,才能实现设计效果和前端开发成本的双赢。

    欢迎补充。

    反思我最近的工作效率问题,记下来,引以为戒

    $
    0
    0

    趁玉伯改bug的时间,思考了下这个问题:我的工作效率在图书馆、家里和公司差别非常大,图书馆 >> 公司 > 家里。相关因素罗列如下:

    图书馆:
            [+] 有计划,知道今天要做什么
            [+] 大块时间长
            [+] 就算出会小差,也能迅速拉回来

    公司:
            [-] 经常会被人打扰
            [-] 经常看公司群和邮箱
            [-] 翻墙方便,导致经常挂在 twitter 上和过度地访问墙外网站

    家里:
            [-] 老婆时不时会和我聊几句
            [-] 惦记着儿子
            [-] 有吃的,容易嘴馋
            [-] 看PLU
            [-] 看电影
            [-] 容易困
            [-] 出小差后,不容易拉回来

    iDebug v1.0 发布

    $
    0
    0

    iDebug 就像是一个魔术桶,把页面倒进去,可以出来任意你想要的东西。

    Github:https://github.com/kissyteam/kissy-tools/tree/master/idebug

    T181VUXeBrXXXXXXXX-876-566.png

    他包含以下功能:

    • 抓网页:
          通过点击 Bookmarklet 把网页抓过来,或修改,或存档
    • responder:
          模拟 Fiddler 的 Responder 功能,对 HTML 内容进行替换
    • 查看所有 CSS 和 JS:
          模拟 YSlow 里包含的一个功能,对于不常开 Firefox 的 Chrome 用户来说非常有用
    • JS Beautify:
          格式化压缩后的脚本文件(不支持 Packer 解压缩),格式化 JSON
    • HTML Beautify:
          格式化HTML
    • Demo 模式:
          切换到 Demo 模式,保存后可在本页刷新 Demo

    以下为淘宝特色功能:

    • 还原 Combo:
          把 http://a.tbcdn.cn/??a.js,b.js 替换成 http://a.tbcdn.cn/a.js 和 http://a.tbcdn.cn/b.js
    • 删除 assets 路径上的 -min
          把 http://a.tbcdn.cn/a-min.css 替换成 http://a.tbcdn.cn/a.css
    • 切换 Hosts
          不用修改 Hosts 文件,直接切换 Taobao 环境的日常/预发/线上等环境

    使用方法:

    1. 确认配有 PHP 环境,并支持 SQLite
    2. 上 github 把 iDebug clone 或 下载下来
    3. 修改 idebug.sqlite.sample 为 idebug.sqlite
    4. 访问  idebug/idebug.php
    5. 根据引导,添加页头的 bookmarklet 到收藏夹
    6. --- 准备完毕 ---
    7. 访问要抓取/测试的页面
    8. 点击刚才添加的 bookmarklet
    9. --- 剩下的大家应该都会了 ---

    快捷键:

    Shift + A:查看所有 CSS 和 JS
    Shift + V:查看 Demo
    Shift + ?:查看帮助
    Shift + H:切换 Demo Mode
    Ctrl + Enter:保存

    规则示例:

    a.css
    ----
    b.css
    ====
    c.js
    ----
    d.js
    e.js

    说明PPT:

    见 docs/idebug_20101030.pdf

    调研 ControlJS

    $
    0
    0

    URL:http://stevesouders.com/controljs/

    Steve 提供了一个看似“普适”的方案,实现功能如下:

    1. 异步载入所有脚本
    2. 延迟所有脚本的执行,包括 document.write

    强大功能的背后也有些限制:

    1. 需调整 HTML script 标签的输出
    2. 需确保 Cache 工作正常

    流程图和值得关注的技术点:

    0016>


    1. 0019>

    0022>

      0024>


    [翻译]关于脚本载入器的思考

    $
    0
    0

    原文:http://www.nczonline.net/blog/2010/12/21/thoughts-on-script-loaders/

    ==

    上周,Steve Sounder 发布了 ControlJS 项目,目的是让开发者可以更好地控制 JavaScript 的载入和执行。实现上借鉴了 Stoyan Stefanov 关于预载入但不执行的方法,同时开启平行下载还带来了一些愉快的"副作用"。更多详情见 Steve 的三篇

    第一篇日志的评论中有来自 Kyle Simpson 的批评 (Kyle 是另一个脚本载入器 LABjs 的创建者)。LABjs 目标和 ControlJS 有些不同:开启 JavaScript 文件的平行下载,同时管理执行顺序。为了要做到这点,LABjs 需要知道哪些浏览器支持平行下载,然后为不支持的浏览器提供其他解决方案。

    然而,LABjs 和 ControlJS 都有个问题:他们用了多种浏览器探测技术来决定如何脚本载入的方式。有些人觉得 LABjs 用的浏览器接口(探测)比 ControlJS 的 user-agent 探测安全点,我不同意。浏览器接口(探测)其实是特征探测加假设,天生就有缺陷(更甚)。浏览器接口(探测)不比 user-agent 探测准确,也并非更稳定。我没有说 user-agent 探测是个好东西,但至少他明确地知道要探测什么。我总是选择清晰的而非含糊的,因为这可以让我们减少错误,或者当错误发生时,可以更快地定位。这个讨论有点偏离本文的主题了。

    LABjs 其实已经证明了,“为不同浏览器使用不同的脚本加载技术”不是个好办法。他在面对浏览器的突然更新时不堪一击,所以我不建议使用尝试征服浏览器的脚本载入器。Kyle 曾由于 Firefox 4 每夜版更新后破坏了 LABjs 的实现,面临了严重的问题。这个问题是:Firefox 4 动态插入脚本元素不再保持执行顺序,而这正式 LABjs 的实现所依赖的。这个变更使 Firefox 和 HTML5 规范及其他浏览器保持一致。毫无疑问,面对浏览器的高速发展,ControlJS 也将会面临同样的问题。对应解决方案的维护成本就变得很高。

    真正的问题

    有些人在讨论 LABjs 和 ControlJS 尝试解决的真正问题。事实上,他们面临着三个问题。

    第一,两者都尝试实现 JavaScript 的平行下载。这很有意义,但很多新的浏览器已经自己处理了。虽然可以通过变通的方式让老浏览器也实现 JavaScript 的平行下载,在学术上很有价值,但我不认为值得去做。浏览器正在为我们解决这个问题,脚本载入器不要做了。

    第二,LABjs 专注于保持脚本执行的顺序。我们要先假设:你希望下载多个 JavaScript 文件,且他们之间有依赖关系。我并不推荐这样做,但其他有些人觉得这很重要。ControlJS 并不关心这个。但不管怎样,这是个没有被浏览器合理解决的问题,如果你要用他,就必须用脚本载入器。

    第三,ControlJS 非常关注 JavaScript 下载和执行的分离。为实现这个想法,需要可以下载 JavaScript 文件但不执行他,直到想执行时再执行。这是个很有趣的概念,且在社区里做了很多实现(Steve 在他的博文中提到)。但这需假设你的页面是渐进增强的,比如并不一定要有 JavaScript 。LABjs 没有处理这个问题,浏览器也没有。

    战斗的号角

    尽管 Kyle 和我在很多事情上都有不同的看法,但他对问题 #2 通用解决方案的呼吁我非常认同:我们需要的不是脚本载入器,而是实现所有开发者处理 JavaScript 文件所需要的原生方法。脚本载入器展示了解决性能问题的一些方法,下一步应该由浏览器开发商来实现。Kyle 整理了问题 #2 相关一些问题的测试和提案 (注意:问题 #3 还没有提案)。当时 Kyle 也问了我对此的看法,但我被一些项目绊住,直到现在也没时间去深入研究。

    async=false?

    Kyle 的提案里有关于 script 标签 async 属性的增强,有点奇怪。async 属性是个布尔值属性,意味着只要他出现就开启这个功能,所以下面三行是等价的:

    <script async src="foo.js"></script>
    <script async="true" src="foo.js"></script>
    <script async="false" src="foo.js"></script>

    这和 HTML5 规范表现一致:马上开始下载且在下载完成时立即执行,不考虑原来的顺序。你可以在 JavaScript 中通过设置 script 元素的 async 属性来开启或关闭这个特细功能:

    var script = document.screateElement("script");
    script.async = true;   // enable async per HTML

    Kyle 的提案中还提到,在 JavaScript 中设置 script 元素的 async 属性会触发新的模式。所以下面这行代码的意义就变了:

    var script = document.screateElement("script");
    script.async = false;   // switch into new mode (WebKit nightly, Firefox 4)

    以前,设置 async 为 false 没什么效果。而现在,在支持他的浏览器里设置 async 为 false 会让脚本通过非阻塞的方式下载且保持执行顺序不变。

    当我为 Kyle 推送提案的坚持鼓掌时,也感到了一些不妥。对于我来说,这行代码读起来是 “此脚本非异步”,而不是 “此脚本是异步的,但需要保持执行顺序”。多说一边,我喜欢清晰的而非含糊的,这可以帮助我们避免错误。

    他的 twiki 里还提到另一个备选提案,创建 scriptgroup 元素从逻辑上让脚本文件捆在一起:

    <scriptGroup id="group1" ordered="true">
       <script src="foo.js"></script>
       <script src="bar.js"></script>
       <script>
         somethingInline();
       </script>
     </scriptGroup>

    我非常喜欢这个提案。非常清晰,对他能做什么没有任何疑问,且可以添加事件处理器到 元素,监听全部文件下载完成。虽然引入了一个新元素,但为了思路上的清晰,我想这个开销会被开发者接受的。

    分离下载和执行

    分离 JavaScript 的下载和执行非常必要,但目前还没有一个非常好的方案。不仅仅是针对页面载入时初始脚本,还有页面载入完成后动态地插入的脚本。在我的演讲《Performance on the YAHOO! Homepage》中,我介绍了页面载入后如何缓缓执行 JavaScript ,而不影响用户进行其他操作。预载入 JavaScript 而不执行的的能力正变得越来越重要,而这正是 ControlJS 尝试解决的。

    理想的情况下,我期望能这样写:

    var script = document.createElement("script");
    script.type = "text/cache";
    script.src = "foo.js";
    script.onload = function(){
        //script has been loaded but not executed
    };
    document.body.insertBefore(script, document.body.firstChild);
    
    //at some point later
    script.execute();

    这已经是我所有想要的了,不想先发请求去下载一个文件,然后再发另一个请求并期望他在缓存里 —— 这是非常不可靠的解决方案。我所想要的是下载一个文件,就让他待在缓存里,然后只要调一个方法就可以执行代码。而这正是 ControlJS 的模型。

    最后

    LABjs 和 ControlJS 都在尝试解决 JavaScript 载入的问题,方法略有不同。Kyle 和 Steve 都非常聪明,研究了不同的方案来解决类似的问题。好消息是现在我们有两个脚本载入器,他们展示了开发者尝试在页面里载入脚本的不同方法,并希望浏览器开发商能实现原生的解决方案,这样在不远的将来就再不需要脚本载入器了。

    而短期来说,Kyle 和 Steve 两位,非常抱歉,两者我都不推荐使用。两个类库都展示了脚本载入不同的有趣的方式,但依赖于浏览器探测就意味着需要不停监控,且当浏览器更新时跟进更新。对于大型的 web 应用来说可维护性非常重要,而两个类库看似都增加了额外的维护开销。

    我知道这是最近的热门话题,所以请在评论时保持冷静。

    快乐圣诞节

    $
    0
    0

    周五提早回家陪儿子去东方爱婴过圣诞节,很欢乐。

    过去晚了会,背背兔已经在和小朋友玩了

    抓紧和抱抱熊合个影

    丝带真好玩

    继续玩。。

    刚看到小丑时有点怕,鼓励了下胆子就大了

    在圣诞树上贴祝福语,“祝儿子健康快乐成长”

    圣诞老公公出场

    集体照

    儿子最喜欢的草莓蛋糕来了

    “哇,给我也来一块”

    [翻译]用 Backbone.js, underscore.js 和 jQuery 创建页面应用

    $
    0
    0

    原文:http://andyet.net/blog/2010/oct/29/building-a-single-page-app-with-backbonejs-undersc/

    概览

    &yet 逐渐有了越来越多的富 JS 应用。直到最近,我们才找到合适通用的应用架构。

    并不出奇,我们发现要重复处理很多相同的问题。

    我们在服务端通过 django 来实现 MVC 结构,但客户端代码里却没有清晰的结构。有些大的类库有此功能,但通常会附加上非常多的组件。比如 Sproutcore, YUIGoogle Closure, 以及类似 GWTCappuccino 这样把其他语言编译成 JS 的工具包。

    但对于我们这群希望在 JavaScript 里手动打造 UI 且对需要到实现知根知底的人来说,仍然渴望能有快速轻量的解决方案,而这些工具包则感觉有点过。

    最近,我看到有个叫 Backbone.js 的库,并仔细看了下。结果我被他征服了,接下去会解释为什么用他以及如何用。

    问题

    创建复杂的应用肯定会面临很多挑战,首先是要管理日益庞大的代码。此外,由于 JavaScript 没有正式的类,因此也没有明确的方法来组织整个应用。

    因为这些原因,JS 开发新人创建类似应用通常会经历类似以下过程:

    1. 对 jQuery 等类库很兴奋,尝试在 DOM 中存储数据和应用状态
    2. 需要记录的内容越来越多,开始觉得第一步很 tricky,尝试在 JS model 中存储状态
    3. 开始发现绑定 model change 到 UI,通过 model 的 setter 和 getter 调用函数,会变得比较混乱。不得不在 UI 上重复 model 的修改,而且还不清楚在哪里改,于是写出很多意大利面条式的代码
    4. 开始构建自己的应用框架甚至基础类库
    5. 最后发现,上面的痛苦,早就有人经历过,并解决了,而且开源了,维护得很好,代码质量很好,社区的人都很聪明

    目标

    那么,我们期望应用是什么样?以下是我觉得比较完美的组织:

    1. 所有 state 和 model 放在同一个地方
    2. model 的任何调整自动同步到 UI (不管有几个地方需要同步)
    3. 清晰可维护的代码结构
    4. 尽可能少的 “耦合代码”

    开始 Backbone.js

    Backbone 没有提供组件或应用,甚至没有提供任何视图层的东西。他只提供了一些关键对象来辅助代码的组织,即:Models, Collections 和 Views。最终是为了在客户端创建清晰的 MVC 应用。此外,还有些基础对象以及处理 change 的事件架构。我们来依次看下。

    MODEL 对象

    model 对象可用来设置和获取属性。创建他只需这样:

    var Movie = Backbone.Model.extend({}); 
    

    现在你可以进行实例化,设置和获取属性等操作。

    matrix = new Movie();
    matrix.set({
        title: "The Matrix",
        format: "dvd'
    });
    matrix.get('title');

    还可以在实例化时直接传入属性,如:

    matrix = new Movie({
        title: "The Matrix",
        format: "dvd'
    });

    如果需要在实例化时强制创建特定属性,可提供 initialize() 函数来做初始化检查。基于约定,initialize 函数被调用时会传入你传给构造器的参数。

    var Movie = Backbone.Model.extend({ 
        initialize: function (spec) { 
            if (!spec || !spec.title || !spec.format) { 
                throw "InvalidConstructArgs"; 
            } 
     
            // we may also want to store something else as an attribute 
            // for example a unique ID we can use in the HTML to identify this 
            // item's element. We can use the models 'cid' or 'client id for this'. 
            this.set({ 
                htmlId: 'movie_' + this.cid 
            }) 
        } 
    }); 
    

    还可以定义 validate() 方法。他会在设置属性时被调用,可以用来验证属性。只要 validate() 方法返回任何内容都会阻止属性的设置。

    var Movie = Backbone.Model.extend({ 
        validate: function (attrs) { 
            if (attrs.title) { 
                if (!_.isString(attrs.title) || attrs.title.length === 0 ) { 
                    return "Title must be a string with a length"; 
                } 
            } 
        } 
    }); 
    

    model 中还有其他非常多的好东西。这里我只做概括,并没有代替原生文档的意思。。我们继续。

    COLLECTIONS

    collection 是某一类型 model 的有序集合。他除了把 model 储存在一个 JS 数组里,还提供了一堆实用功能。比如获取 model 时,可通过 comparator() 函数定义规则来保持 model 的有序返回。

    先 collection 要保存什么类型的 model,之后的事情就非常简单了:

    // define our collection 
    var MovieLibrary = Backbone.Collection.extend({ 
        model: Movie, 
        initialize: function () { 
            // somthing 
        } 
    }); 
     
    var library = new MovieLibarary(); 
     
    // you can add stuff by creating the model first 
    var dumb_and_dumber = new Movie({ 
        title: "Dumb and Dumber", 
        format: "dvd" 
    }); 
     
    library.add(dumb_and_dumber); 
     
    // or even by adding the raw attributes 
    library.add({ 
        title: "The Big Lebowski", 
        format: "VHS" 
    }); 
    

    同上,collection 中也还有很多其他好东西,但主要功能是解决有序维护 model 集合的问题。

    VIEWS

    在这里可以操作 DOM ,以及处理一些兼容性问题。这是唯一于依赖 jQuery 的地方。

    通常约定用 view 来向浏览器绘制 model 的改变,可以直接操作 HTML。初始渲染时(首次添加新的model)还需要一些给力的客户端模板引擎。我个人倾向于使用 ICanHaz.jsMustache.js 。(如果对此感兴趣,可以看 ICanHaz.js on github) 此时,view 就可以监听并响应 model 的修改了。

    这里有个 Movie 对应的简单的 view:

    var MovieView = Backbone.View.extend({ 
        initialize: function (args) { 
            _.bindAll(this, 'changeTitle'); 
            this.model.bind('change:title', this.changeTitle); 
        }, 
     
        events: { 
            'click .title': 'handleTitleClick' 
        }, 
     
        render: function () { 
            // "ich" is ICanHaz.js magic 
            this.el = ich.movie(this.model.toJSON()); 
            return this; 
        }, 
     
        changeTitle: function () { 
            this.$('.title').text(this.model.get('title')); 
        }, 
     
        handleTitleClick: function () { 
            alert('you clicked the title: ' + this.model.get('title')); 
        } 
    }); 
    

    view 处理两类事件。首先, events 属性用于连接用户事件和处理器,这个例子里具体是处理模板中包含 title class 元素的点击事件。另外,backbone 还提供很多强大的功能来确保任何对于 model 的修改会自动更新到 html。

    合到一起

    目前为止,我们谈论的是一些片段。现在我们来说下如何把他们汇聚到一起,做一个完整的应用。

    全局 CONTROLLER 对象

    虽然也可以把 controller 放在 AppView 对象里,但我不喜欢把 model 对象存在 view 里。所以我创建了一个全局的 controller 对象来存所有的东西。我创建了一个名字和应用相同的简单的单例对象,继续我们的例子:

    var MovieAppController = { 
        init: function (spec) { 
            // default config 
            this.config = { 
                connect: true 
            }; 
            // extend our default config with passed in object attributes 
            _.extend(this.config, spec); 
            this.model = new MovieAppModel({ 
                nick: this.config.nick, 
                account: this.config.account, 
                jid: this.config.jid, 
                boshUrl: this.config.boshUrl 
            }); 
            this.view = new MovieAppView({model: this.model}); 
            // standalone modules that respond to document events 
            this.sm = new SoundMachine(); 
            return this; 
        }, 
     
        // any other functions here should be events handlers that respond to 
        // document level events. In my case I was using this to respond to incoming XMPP 
        // events. So the logic for knowing what those meant and creating or updating our 
        // models and collections lived here. 
        handlePubSubUpdate: function () {}; 
    }; 
    

    可以看到我们有一个应用 model,包含所有其他 model, collection 和应用 view。

    这个例子中的应用 model 包含了所有的 collection,以及应用 view 可能要用到的属性:

    var MovieAppModel = Backbone.Model.extend({ 
        initialize: function () { 
            // init and store our MovieCollection in our app object 
            this.movies = new MovieCollection(); 
        } 
    }); 
    

    应用 view 可能是这样:

    var MovieAppView = Backbone.View.extend({ 
        initialize: function () { 
            // this.model refers the the model we pass to the view when we 
            // first init our view. So here we listen for changes to the movie collection. 
            this.model.movies.bind('add', this.addMovie); 
            this.model.movies.bind('remove', this.removeMovie); 
        }, 
     
        events: { 
            // any user events (clicks etc) we want to respond to 
        }, 
     
        // grab and populate our main template 
        render: function () { 
            // once again this is using ICanHaz.js, but you can use whatever 
            this.el = ich.app(this.model.toJSON()); 
            // store a reference to our movie list 
            this.movieList = this.$('#movieList'); 
            return this; 
        }, 
     
        addMovie: function (movie) { 
            var view = new MovieView({model: movie}); 
            // here we use our stored reference to the movie list element and 
            // append our rendered movie view. 
            this.movieList.append(view.render().el); 
        }, 
     
        removeMovie: function (movie) { 
            // here we can use the html ID we stored to easily find 
            // and remove the correct element/elements from the view if the 
            // collection tells us it's been removed. 
            this.$('#' + movie.get('htmlId')).remove(); 
        } 
    }); 
    

    现在来看整个应用。我引入了全部依赖的文件,以及 ICanHaz.js 模板引擎。然后在 $(document).ready() 里调用 init 函数并传入一些服务端过来的变量,用于模板替换。最后调用 render() 方法渲染应用 view 到 <body> 元素,比如这样:

    <!DOCTYPE html>
    <html>
        <head>
            <title>Movies App</title>
     
            <!-- libs -->
            <script src="jquery.js"></script>
            <script src="underscore.js"></script>
            <script src="backbone.js"></script>
     
            <!-- client templating -->
            <script src="mustache.js"></script>
            <script src="ICanHaz.js"></script>
     
            <!-- your app -->
            <script src="Movie.js"></script>
            <script src="MovieCollection.js"></script>
            <script src="MovieView.js"></script>
            <script src="MovieAppModel.js"></script>
            <script src="MovieAppView.js"></script>
            <script src="MovieAppController.js"></script>
     
            <!-- ICanHaz templates -->
            <script id="app" type="text/html">
                <h1>Movie App</h1>
                <ul id="movies"></ul>
            </script>
            <script id="movie" type="text/html">
                <li id="{{ htmlId }}"><span class="title">{{ title }}</span> <span>{{ format }}</span></li>
            </script>
     
            <script>
                $(document).ready(function () {
                    // init our app
                    window.app = MovieAppController.init({
                        account: '{{ user.get_profile.account.slug }}',
                        // etc, etc
                    });
                    // draw our main view
                    $('body').append(app.view.render().el);
                });
            </script>
     
        </head>
        <body></body>
    </html>

    都做完了,现在从 collection 中添加或移除 movie 或者改变 model 里的 title,这些修改都会通过 MovieView 中定义的方法反映到 HTML 中,非常神奇。

    通用技巧

    1. 开发环境,所有对象按文件存放
    2. 生产环境,压缩合并所有的 JS 到一个文件
    3. 使用 JSLint
    4. 在 underscore.js 里扩展实用方法,而不是修改原生对象 (详见此 gist)
    5. jQuery 应该只在 view 里实用,这样可以在服务端对 model 和 collection 进行测试
    6. 用普通的 jQuery 事件来注册其他 view,避免强耦合
    7. 尽量保持 model 的简单

    我知道我写了很多,而且有些内容和 backbone.js 上的文档 重复。但要基于 backbone.js 创建应用时,可能会希望有像这样更高层次的概括,所以我写了这篇文章。感谢 DocumentCloud 和 Jeremy Ashkenas 创建并分享了 backbone 。

    如果有任何想法,欢迎通过 twitter (@HenrikJoreteg) 联系我。谢谢!

    ECMAScript 入门

    $
    0
    0

    我其实学了两次 ECMAScript。 第一次直接看的官方 PDF,从第一章开始看,看了没多少就因为太晦涩放下了; 第二次就好多了,先看的闭包,完全理解后,再看 HTML 版的 ECMA 手册,附带看一些讲解的文章,来回反复看,现在总算是明白了大半。

    这里整理了一些资料,希望对大家有所帮助。欢迎补充!

    Resources

    闭包

    解读

    附送一张我的 PPT

    一些心得

    • 规范像字典,有两种用法,1) 查 2) 深入研究,”倒背如流”,..
    • Type 和 Type Convention 是基础,一上去就可以直接看这两块,掌握后再看其他的如 Expression 和 Statement,会容易很多
    • 内部类型 Reference 很重要,也有点难
    • Function (包括执行上下文) 是最大的难点,理解了 Function 就等于掌握了一半规范,可以先从闭包看起

    我的信息流

    Viewing all 32 articles
    Browse latest View live




    Latest Images