一览CSS布局标准

回顾历史,CSS1于1996.12.17发正式版,它是为辅助HTML的展现效果而生的。
1998.5.12,CSS2发正式版。随后发修订版CSS2.1,纠正了CSS2中的一些错误。注意从CSS2起,CSS的作用就不是辅助展现了,变为全全负责展现。
(注:当年CSS2的勘误表有兴趣可以看看)

CSS2.1明确了2个模型,一个是众所周知的盒模型(Box model),CSS1没有盒模型的概念,盒模型的前身在CSS1里叫做面向盒的格式化模型。元素抽象为盒,以盒为对象设计思路清晰多了。CSS3的盒模型丰富了更多属性。盒的产生,以及盒的定位就是CSS2.1定义的第二个模型-可视格式化模型(Visual Formatting Model)。CSS3相关的布局标准实际上也在这个大的框架之下。

CSS布局有明确标准始于CSS2。CSS1里压根没提布局,虽然CSS1里有Float元素的定义,但它的设计不是为页面布局,只是为了实现图文绕排。早期用table布局便顺理成章。CSS2是98年变为推荐标准的,按理此时应该普及新的CSS布局标准,但table布局的使用习惯一直保持到2004年才被Jeffrey Zeldman那本书点醒。05年它的中文版《网站重构》出版,国内掀起重构浪潮。早期网页开发者是受软件教育的(Frontpage, Dreamweaver之类),完全没有标准的概念。

所以要从标准出发学习CSS,而不是仅仅掌握一些工具(包括一些库)。这是有前车之鉴的。

回到布局的话题上。CSS2.1的布局分为3种:
1. 常规流(Normal Flow)
2. 浮动(Float)
3. 绝对定位(Absolute Position)

这3种不能混用。如果代码里看到position:absolute;display:block;这种明显是概念混乱。很多人也错把position:relative跟position:absolute归为一类。position:relative是常规流中的一种,例外是它可以和Float一起使用。关于CSS2.1布局的阐述请看这里:http://kejun.github.io/bootcamp_htmlcss/?5

IE6时代被忽视的常规流布局:
1. Inline-Block。兼容性:http://caniuse.com/#feat=inline-block,触发IE的hasLayout可以实现相似的效果,以至于可以兼容IE6/7,逐渐被广泛应用。
2. CSS Table。兼容性:http://caniuse.com/#feat=css-table,仅仅不兼容IE6/7,在IE6时代被埋没。随着IE6/7的淡出终有出头之日了。

对float的滥用就像当年对table的滥用“。很多人设计布局时不假思索的用Float,明显欠缺对布局技术有更多了解。尤其在低端浏览器日渐淡出,新的布局技术触手可及的当下,是时候学习实践这些新技术了。之前做过一个demo,用6种思路实现同一种两列布局:http://dabblet.com/gist/4094139。看完本文后,可以有更多实现思路。

今天脱囗而出的CSS3只是一种笼统的叫法。有CSS Level 1,CSS Level 2,时至今日还没有CSS Level 3。CSS2.1之上的新标准大部分仍在WD状态,只有CSS Color Level 3和Selectors Level 3进入REC状态。

(注:PD – Public Draft, 公共草案,初步想法。不乏PD之后就废弃的。WD – Working Draft, 工作草案。开始有人修订它了。LC – Last Call, 对外宣布了。CR – Candidate Recommendation, 候选推荐。REC – Recommendation,正式推荐。也就是正式版标准。这些状态只能说明标准的稳定状态。浏览器厂商从自身利益出发,选择性的支持了一些仍处于WD状态的标准。于是出现各种厂商前缀。标准制订跟不上发展,浏览器厂商在竞争中冒进,苦逼的是开发者。)

新的布局标准可以更简单更灵活的实现布局。CSS3目前有5种布局方案:
1. 多栏布局(Multi-Column Layout )
2. 灵活盒布局(Flexible Box Layout)
3. 栅格布局(Grid Layout)
4. CSS3的浮动盒(CSS3 Floating Boxes)
5. Regions

Multi-Column Layout
当前状态:CR
历史:1999.6.23发PD,2009.12.17进入CR状态
兼容性:http://caniuse.com/#feat=multicolumn
Demo: http://dabblet.com/gist/5507829
问题:
1. 目前只能平均分栏,还不支持分别指定栏宽(未来会有)。
2. 浏览器支持的新旧标准不一。
比如,上例中div.intro的内容想保持在一栏中,chrome支持column-break-inside: avoid;这是2005.12.15更新的WD中的标准。由此推测chrome遵循的是05年的WD标准。目前CR标准已改成break-inside。Firefox20仍实现的是2001年的WD标准
3. 多栏布局更适合用于内容流布局,不适合页面布局。

Flexible Box Layout
当前状态: CR
历史:2009.7.23发PD, 2011.3.22进入WD状态,2012.9.18进入CR状态
兼容性:http://caniuse.com/#feat=flexbox
Demo: http://dabblet.com/gist/5508104
理解它分为两部分:Flex容器(Flex Container)和Flex项(Flex Item)。
1. 定义Flex容器(display:flex)后, 它的一级子元素(inline box/block box/anonymous box),可沿水平或垂直方向排列(flex-direction, 默认row),可指定靠某一边对齐(flex-wrap),可指定对齐方式(justify-content)。每个Flex项默认是等高的(align-items, 默认stretch)。
2. Flex项不指flex属性时,宽度由实际内容的宽度决定。
3. Flex项可以指定以什么比例填充空白。如:
.list li { flex:1; } 则均匀填充。
.list li:nth-child(1) { flex: 4; background: #f80; }  本例中Flex Item 1的宽为4/8。
4. flex属性是可以动画的。
5. Flex项可以控制顺序(order, 默认0,值高在后面)。
参考:http://d.hatena.ne.jp/teramako/20121021/p1

曾在weibo上出过一道题:“实现一个不定宽高的层,在窗口中水平垂直居中”,用Flex Box可以轻松实现:
html,body { height:100%; }
body { display:flex;justify-content:center;align-items:center; }

(Grid) Template Layout (曾经称为Advanced Layout)
当前状态:WD
历史:2005.12.15发PD, 2007.8.9进入WD状态。最终合并进Grid Layout

Grid Position
历史: 2007.9.5发PD,随后被废弃

Grid Layout
当前状态:WD
历史:2011.4.7发PD,2012.3.22进入WD状态,最新一版是2013.4.2
兼容性:http://caniuse.com/#feat=css-grid
以这种兼容性还不值得学它。1996年有个基于帧布局的草案。当时没往这条路上走,最终绝对定位进入CSS2。Grid Layout正是建在它的基础之上。

CSS3的浮动盒(CSS3 Floating Boxes)
当前状态: WD
历史:2002.10.24就出现在CSS basic box model的草案中。
CSS3的浮动盒太未来了,还没有浏览器支持。比如:
float: right contour; contour关键字,文字可以沿图片不规则的轮廓绕排。
min-height: contain-floats; 新的清浮动方式
float-displace: block; 新的float-displace属性

Regions和Shapes
当前状态:WD
历史: 2011.6.9发表PD, 2011.11.29进入WD状态,最新一版WD是2012.8.23
兼容性:http://caniuse.com/#feat=css-regions
Demo: http://dabblet.com/gist/5509294 Chrome 19+, IE10+
它适用于内容流布局。兼容性有限,可以先简单看看。

Have Fun!

测试touch事件

进入触屏时代意味一切要对触屏友好。首先要搞清touch事件。今天仅仅测试了ios6,其它版本包括android还不清楚差别有多大。看了PPK的touch兼容表(http://www.quirksmode.org/mobile/tableTouch.html),深感刚准备告别ie6,又迎来了一个新的混乱时代,苦逼的前端工程师们永远摆脱不了兼容的魔咒。

做这个测试的目的是为了写这个库: https://github.com/kejun/SwipeEvent

—————————————————————————

测试地址:http://jsbin.com/isuqaf/2/

在iOS上的测试结果:

一:发生在非可点击元素上:
1. 手指点击(tap),坐标不变:
touchstart
touchend
mouseover
mousemove

2. 手指在屏幕上滑动,坐标改变小:
touchstart
touchmove (多次)
touchend
mouseout (若前一次触发过mouseover)
mouseover (有时没有, 坐标改变很小时没有)
mousemove (有时没有)

3. 手指在屏幕上滑动,停留时间长一些:
touchstart
touchmove (多次)
touchend

二:发生在可点击元素上:
1. 链接若绑定touch事件,touch事件里操作dom(内容发生改变),不会触发click事件,链接也不会跳转
2. 包括父级的touch事件若改变页面内容,同样不会触发click事件
3. 不触发click的情况,解发顺序:
touchstart
touchend
mouseover
mousemove

三:window和模块都绑定事件:
1. window绑定scroll事件和touch事件,顺序:
page touchstart
page touchmove (多次)
page touchend
page scroll (手指水平移动,不触发scroll)

2. 滑过模块不会触发模块的touch事件

3. 在模块上滑动页面
touchstart
page touchstart
touchmove, page touchmove (交替触发)
touchend
page touchend
scroll (页面停止滚动时,触发一次,若手指水平移动,不触发scroll)

4. 页面滚动过程中,手指在模块区域内移动
touchstart
page touchstart
touchmove, page touchmove (交替触发)
touchend
page touchend
scroll (触发两次)

5. 页面在开始位置,向下拉并保持,再松开
同3,touchmove, page touchmove交替触发多次

四:阻止模块touchstart事件的默认行为(preventDefault),在模块内移动手指:
1. 页面滚动失效,事件顺序:
touchstart
page touchstart
touchmove, page touchmove (交替触发)
touchend
page touchend
只是没触发scroll

五:callout时会触发touchcancel,如长按链接之类

已知的BUG:

http://code.google.com/p/chromium/issues/detail?id=142187

PPK的touch兼容表:http://www.quirksmode.org/mobile/tableTouch.html
苹果官方文档:https://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html

一次响应性开发实践

响应性开发的概念不做细述,一年前曾做过一次分享”聊聊响应性设计和开发“。
仅仅利用media query适配样式是远远不够的,并没有考虑触屏下的行为和特有的内容组织方式的不同。简单在桌面版基础上叠加mobile版的代码,会带来请求增多、流量、性能、代码冗余等诸多方面问题。有统计说86%的手机站看起来small其实体积比桌面版还大。

我们这次充分发挥“响应”的灵活性,实现one web。

响应式开发就为了实现one web:

 

1. 响应性的模块
原本网站的模块化程度越高越便于做响应性开发。一个page例如是这样组织的:
<%include file=”path/mod1.html” args=”data=data” />
<%include file=”path/mod2.html” args=”data=data” />
如果在手机下访问,模板系统在生成这个页面时,会在path/下找mobile.mod1.html,有则加之,否则加mod1.html。也就是说在同一目录下,会存在多个版本的模块,当前只有2种:mod1.html(桌面版),mobile.mod1.html(mobile版)

响应部分的代码,跟主站代码是放在一起的,这样更便于维护。一个页面模板的结构是这样的:

page1.html:

<%inherit file=”/base.html” />

<%def name=”main”>

桌面版主要内容

<%include file=”path/mod1.html” args=”data=data” />

</%def>

<%def name=”sidebar”>

桌面版边栏内容

</%def>

<%def name=”mobile_main”>

<%block filter=”collect_css”>

mobile版css

</%block>

mobile版主要内容。如果可以复用,直接调${self.main()}

很多情况下内容是不同的,比如去掉不必要的模块。

</%def>

<%def name=”mobile_sidebar”>

mobile版底部内容

</%def>

这意味可以同时开发/维护两个版本。(同样,设计师在设计一个页面,也需要秉承mobile first的原则)
在同一目录、同一文件维护比分布在不同的仓库中要方便的多。

 

 

2. 响应性的css/js
mobile版的变化很大,在样式上并不是桌面版css+mobile版css的关系。这得益于我们之前对静态文件管理系统的改造。传统的css的组织方式是集中式的,集中在几个通用文件中,形如base.css + product.css。而我们现在的方式是base.css + mod_1.css(inline) + mod_2.css(inline) + mod_3.css(inline) … 是按需组合的形式。

这样,加上设备判断后就可以轻易变成:
mobile.base.css + mobile.mod_1.css(inline) + mobile.mod_2.css(inline) + mobile.mod_3.css(inline) …

css/js文件跟模板一样,在同一目录下分别有桌面版和mobile版。根据访问端的情况,自动适配、按需组合。这样可以得到一个更优化的mobile站。

 

 

3. 增强触屏行为和兼容桌面事件
前者是指附加触屏上特有的事件:touchstart/touchmove/touchend以及手势swip/pinch/rotate/shake。这个不是难点。

mobile浏览器和桌面浏览器的事件模型有明显差异,为了完全复用桌面版的各种js组件,首要问题是设法兼容桌面事件(click和mouse事件)。

mobile上的click和mouse事件有几个需要注意的地方:
a. click和mouse事件不会发生在不可点击的元素上,意味绑在document上的事件代理完全失效
b. mouse事件是发生在手指离开屏幕后,且顺序是mouseover > mousemove > mousedown > mouseup
c. click事件最后触发。从手指离开屏幕起,有约300多毫秒延迟,而且有可能不会被触发

见下图:

“If the user taps a clickable element, events arrive in this order: mouseover, mousemove, mousedown, mouseup, and click. The mouseout event occurs only if the user taps on another clickable item. Also, if the contents of the page changes on the mousemove event, no subsequent events in the sequence are sent.”(出处)

android/ios不支持beforeunload事件,对unload事件的支持有些怪异,需要用pageshow/pagehide事件替代。

以上事件的差异都是要尽力消除的。解决思路是利用jQuery的special event机制覆盖掉原本的事件绑定。即:node.click(fn),mobile上转向node.touchend(fn) 。实现的代码:https://gist.github.com/3358036

 

 

4. 优化和用户体验

a.去掉了apple-mobile-web-app-capable声明。单页应用要加上,用响应式开发的加上这句体验反而不好,跳转的链接会弹出窗口打开。

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no"/>
## <meta name="apple-mobile-web-app-capable" content="yes">

b. “If the user taps a nonclickable element, no events are generated.”(引自Apple Dev Center),所以:

body {
cursor:pointer;
}

c. 设定内容中图片的最大宽:

.topic-content img {
max-width:100%;
}

d. 加载提示,感觉像异步加载。

e. application cache本身是为web app设计的,在响应式开发中的应用是不同的。
把大文件在手机端cache起来:

CACHE MANIFEST
# version 0.0.1
CACHE:
${static('/js/jquery.min.js')}
${static('/js/do.js')}
${static('/css/mobile/base.css')}
${image_url('/pics/icon/dou.png')}
${static('/css/ui/dialog.css')}
${static('/js/ui/dialog.js')}
${static('/js/mobile/jquery.mobile.events.js')}

这个文件是动态生成的,好处是当文件更新后,文件名中的签名会跟着变,也就会触发手机端app cache的更新。

判断cache更新:

if (window.applicationCache) {
window.applicationCache.addEventListener('updateready',function(){
window.applicationCache.swapCache();
}, false);
}

为了避免动态页面被cache,在一个隐藏的iframe里指定它。(更改,还需要进一步测试)

f. mobile上的UI库,比桌面版更有必要。

 

 

5. 调试和监测

用了两个工具adobe shadow和自带的tcpdump,对android/ios均适用。

a. adobe shadow 出了一段时间了。原来的问题是它要走adobe的weinre server,慢!终于最新版4已经可以指定本地的weinre server了。怎么启用本地weinre server是另外话题自己去搜吧,我是用jar启动的,作者网站提供各种版本:http://people.apache.org/~pmuellr/weinre/builds/1.x/
>java -jar ~/weinre-jar/weinre.jar –boundHost 10.0.2.48  (ifconfig查具体ip是什么)
weinre server启动参数:

–httpPort [portNumber] 改变HTTP服务器的端口号 8080
–boundHost [hostname | ip address | -all-] 改变主机名。如果使用默认的localhost,将无法从另一台机器访问该服务器 localhost
–verbose [true | false] 记录标准输出行为 false
–debug [true | false] 详细的操作日志输出到标准输出 false
–deathTimeout [seconds] 指定超时 3

安装shadow,同时手机上安装shadow client。

指定本地server:
 手机打开shadow client直接找ip。shadow其实就是对weinre包装了一层,它的inspector其实就是weinre调web inspector调试。

 

b. 用tcpdump监测http请求(参考这里)。步骤:

step 1: 建一个wifi热点
step 2: 用tcpdump命令捕获tcp的traffic,命令:
sudo tcpdump -i en1 -n -s 0 -w group.pcap tcp or port 53
(参考tcpdump用法http://www.tcpdump.org/tcpdump_man.html)
step 3: 手机联上热点,打开网站(先清cache)
step 4: Ctrl+c停掉tcpdump,log保存到指定的group.pcap文件中。pcap(packet capture)
step 5: pcap转成har文件浏览,http://pcapperf.appspot.com 或导入到charles里查看也很方便,分别看下图:


c. 在线工具:mobitest.akamai.com (说是开源了)

 

响应式开发最大的优点是投入产出比高。可以高效的把产品向各种终端设备上部署,而且使用体验也可以做到不错。这只是探索的开始。
用手机访问:http://group.douban.com

意识流

我自己绝对是一个顽固的意识流的人。以往的各种内部或外部的分享,谈的最多的也是前端开发的各种concept。

2009.12 – D2上我说“库”时代过去了,以后不会再有类似jQuery、Dojo、YUI这种大而全的库了。堆积“库”的开发方式将会演变为按需加载更细粒度的模块(http://www.slideshare.net/kejun/yui2yui3)。框架提供的插件机制会把单一功能的库整合到一起。同年1月Mozilla发起一个叫ServerJS的项目,8月改名为CommonJS。CommonJS定义了一整套Javascript模块(module)、包(package)、promises、io等规范,目的用于服务器端、桌面应用的Javascript开发。它只是一套规范,没有具体实现。AMD(Asynchronous Module Definition)最初是CommonJS模块定义方面的一个草案,后来出现意见分歧,另立一个amdjs组单独发展,如今已被广泛接受,CommonJS也就接纳了它。

Javascript模块的理念发展到今天ES Harmony要把它正式加入到标准中,要向先驱Dojo致敬。 YUI3的设计思想也很超前,只不过走的是另一条路。透过这种演变的脉络,要看清它最初的需求是什么?细粒度模块的按需加载和依赖关系管理就这么朴素。YUI3的风格是把模块配置写在外面,AMD是写在模块里面。

辩论风格的好坏意义不大,就像讨论加不加分号一样无趣。关键是否有效解决最初的问题,围绕这个根源,可以有不同的思路。比如我们目前的做法是在build阶段设计一些伪语法导入细粒度的模块。前端用一个loader加载粗粒度的模块。我今年4月曾在杭州的D2沙龙上分享过(http://www.slideshare.net/kejun/ss-12148658)。为解决问题完全可以跨越前后端的界线,而且这样往往效果会更好。

开发如下棋,局局新,要观局、布局。如今国外一个新技术冒出来,国人就追捧。欠缺观局这一环,真正搞清实际开发遇到的问题了吗?独立思考过这些问题吗?还是在开发中试玩各种新技术、照搬别人的经验,还是觉得只有新技术才够fashion……

2009.12 webrebuild北京的一次活动,我分享过“分层语义化模板实践”。也是很“意识流”。同年3月,我看到Y!的女前端工程师Nicole Sullivan在YDN分享的“Object Oriented CSS: for high performance websites and web applications”。可能同受雅虎前端开发意识流的影响,想法非常相似。2年后,Jonathan Snook提出SMACSS( Scalable and Modular Architecture for CSS )的CSS开发思想,并出了一本书(http://smacss.com/book/)。

在前端技术里HTML不像Javascript和CSS那么精彩,其作用最被忽视,但它又是最基础的载体。HTML的难点在结构的设计上,需要稳定、灵活、友好、易理解。其实HTML/CSS完全受意识流控制。如何架构好,源于经验而非知识。现在越来越少人在这方面做认真的追求了。

2010年7月的webrebuild年会上,我分享了前端基础架构(貌似这个词是我攒出来的,我还特地搜过)的想法,也是纯意识流的一次分享。之前前端开发的规范、工具都是毫无关联的个体,开发中各种工具的应用也是毫无目的性的,而且前端开发是脱离整个体系的(像外包)。实际上开发规范是让团队保持统一的代码风格和明示各种性能、安全、易用性原则,但文字本身没有约束力,需要工具保证。因此工具的职能,一方面检验规范,另一方面实现自动化。在这个线索下工具体系就形成了,而且目的性很明确,接下来把它们无缝的挂在开发环境下,成为开发流程的一部分。于是规范、工具、系统就形成了完整的前端基础架构的模型。前端team在这个平台上可以跑的更快。

有人觉得这种分享是浅入浅出,他显然没领会我的意识流里的线索。

11年我有两个针对前端基础知识的分享“前端开发理论热点面对面:从怎么看,到怎么做”和“永不止步的‘重构’“。内容都是前端基础知识。很多人看了很多前端开发方面的书,掌握了很多技术点,但在面临具体问题时仍然不能融会贯通。我们的生活充满大量碎片信息,很多人认为这样可以快速掌握很多知识。伟大的摇滚乐手兼作家Frank Zappa在一首歌里写到:“Information is not knowledge,Knowledge is not wisdom,Wisdom is not truth,Truth is not beauty,Beauty is not love…”。

意识流和技术流就像武侠里的气宗和剑宗。我不过多评价,根据个人特点和兴趣所向。但是在公司项目开发上注重意识流显然更有益处。现在这年头太多浮躁、盲从、缺少独立思考、务实求索之精神。我希望在以后国内技术会议上,能看到高质量的“意识流”分享。也希望更多人不要觉得这东西“水”而排斥它。

js和css的顺序关系

1. head里的顺序如下,考虑会对请求有何影响:

a. 外部js在css前面

<script src="1.js"></script>
<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<link rel="stylesheet" type="text/css" href="2.css?sleep=5s">

b. 外部js在css后面

<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<link rel="stylesheet" type="text/css" href="2.css?sleep=5s">
<script src="1.js"></script>

c. 内部js在css前面

<script>
// do something
</script>
<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<link rel="stylesheet" type="text/css" href="2.css?sleep=5s">

d. 内部js在css后面

<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<link rel="stylesheet" type="text/css" href="2.css?sleep=5s">
<script>
// do something
</script>

e. 内联css在外联css前面

<style>
body { background:red; }
</style>
<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">

f. 内联css在外联css后面

<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<style>
body { background:red; }
</style>

结果:
a,b – head里出现外联js,无论如何放,css文件都不能和body里的请求并行。补充:body里dom渲染取决于head里的js执行完。 (图1)
c – head里的内联js只要在所有外联css前面,css文件可以和body里的请求并行(图2)
d – head里的内联js只要在任一外联css后面,css文件就不能和body里的请求并行。补充:原因也是要等js执行完(图1)
e – firefox/ie下,要等1.css加载完生效。safari/chrome下,则先生效,再加载1.css
f – 等1.css加载完生效

2. 内联js要等它前面的所有外联css文件加载完后执行。之前写过一篇子资源原理的笔记

<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<script>
s_time = +new Date;  // 5s后才执行,所以在这里记录开始时间是不准确的。
</script>

3. 外联js放在页面最后,高级浏览器会自动做优化。如:

<head>
<link rel="stylesheet" type="text/css" href="1.css?sleep=5s">
<link rel="stylesheet" type="text/css" href="2.css?sleep=5s">
</head>
<body>
<img src="1.png">
<img src="2.png">
<img src="3.png">
<script src="1.js"></script>
</body>

firefox/chrome/safari加载的优化处理相似。见图3。

4. 内联长执行时间js,无论放在页面任何位置,都会影响整个页面的渲染。测试文件,如:

<body>
<img src="1.png">
<img src="2.png">
<img src="3.png">
 
<button id="bn">button</button>
<script type="text/javascript">
  document.getElementById('bn').onclick = function() {
    alert(1);
  }
  // 正常渲染时间
  document.body.appendChild(document.createTextNode(+new Date - s_time + 'ms'));
</script>
 
<script>
// 执行5s。重新打开页面(不是刷新),整个页面空白5s。
// 前面的dom结构正常渲染,但不显示,等5s后全部显示出来。
</script>
</body>

在页面初始执行阶段如果有长执行时间的内联js,对性能的影响是非常非常严重的!但是,如果把那段执行5s的js放到外部就不会有上述影响,或者移到domreay/onload后执行也可以。

依据上述结论组织页面中的css和js才会更合理。

感谢玉伯的这篇很好做了解释也指出一些问题: http://lifesinger.wordpress.com/2012/02/03/performance-impact-of-js-css-loading-order/

顺流而下, 把梦做完 …… 2011

2011最后一个changeset调了一些无聊的样式,收官不够华丽。

总结一下自己一年做过的事:

FM神秘实验项目 – 很好玩,因为是技术驱动的,我们尝试了node.js。当然少不了各种html5,css3技术混搭。背后还有牛逼的算法。node.js今年很火,yahoo!新一代应用开发平台就是用node.js。它的优势是处理高并发的微请求,这个项目里复杂的运算和数据存储都调service。到底效果怎么样上线后再看吧。

FM WebApp – 我决对是WebApp的拥趸者。但Android 2.2/2.3内置的Webkit就是渣,跟ie6一样招人恨。还好手机的换代更快,我觉得从iOS 5和Android 4开始WebApp的时代要来了。明年打算换个phone7,我喜欢反苹果美学的东西。

首页改版 - 2011年突然流行起“砖块布局”(YY的叫法)像花瓣之类的网站。但新的豆瓣猜布局将是正经的magazine-style布局,是溶合了前后端技术加设计的高级货。

今年还做了团购(已下线)、二手和部落 – 这些项目技术上平淡无奇,贵在开发方式上。比如做部落的过程高度同步,PRD出来后就着手开发,几天整个流程就跑通了,后面等设计出来后再改样式。这种完美时刻,就像比赛中偶尔出现的美妙配合一样。这里面也积累了一些代码组织上的经验。代码组织的三层:业务逻辑代码、通用功能代码,基本架构(类MVC式分层,model是ajax数据交互、controller注册事件、view是javascript template。注意不是single page app那种MVC)。

推荐改版事件 - 细节跳过(…惹了不小的麻烦)。这件事的经验是不要试图实现一个复杂和看似完美的方案,里面可能隐患重重。有时朴实无华是最合适的。像这种通用程度极高的组件,对复用、性能、响应速度都有很高的要求,用户使用上也极敏感。达芬奇说过”简单是极端的复杂“。

上半年主要做了新版FM。它是最适合实践响应性开发的项目,做的不好。明年接着来。国内的设计师对响应性设计无感。这篇文章反映了国外设计的主流思想,基本都在谈响应性设计:A Year in Web Design: How the Experts Saw 2011

在这些产品开发实践中愈加感触:过细过明确的分工会限制单位创造力的发挥,协调成本也高,从而拖缓产品发展,更甭提创新了。各个角色的人(PM、Designer、Engineer)应当通过扩展技能来弥补之间的代沟。也就是Designer应该有产品意识和工程开发意识、Engineer也应该有基本美感,同时,前后端工程师之间应当技术互补。前端问题并不一定用前端技术解决。典型就是静态文件管理,它对前端开发的帮助很大。没它前端代码重构和拆分基本寸步难行。但目前还是简陋。前端开发需要有一套完整的工具体系,闲来整出一张图,后面会谋求更多人支持。总之这是一件任重道远的事,我也有心理准备。

开发要追求高度的模块化,但是在普遍情况下,模块的依赖关系要简单、偏平。也许在很独立的应用或框架内部模块的依赖关系可能会复杂,但纯属个案。因此,前端文件按需加载的粒度不应太小,而在开发环境中维护的应该是粒度更小的模块。因此,后端的预处理和前端的异步按需加载混用是最合理的方案。我对Do也有了新想法,打算2012年重写一下。

今年参加的三个会和所做的分享:
淘宝的技术嘉年华 (slides)
webrebuild年会 (slides)
tencent北京CDC火山囗 (slides)

承办了一次web标准化交流会。这是目前北京坚持下来时间最长的民间前端技术沙龙。

今年还在知乎上比较认真的回答问题:zhihu.com/people/kejun

8月一个人跑到HK看Red Hot Chili Peppers的现场。所有好的东西都充满某种力量,甚至一行代码也是如此。今年还幸运的看了eels、black rebel motor club、envy的现场。稍稍有点后悔没看Bob Dylan,但我更爱Leonard Cohen,这个老家伙据说2012年出新专辑。顶马的十年和声音碎片的十年专场印象也颇深,对于上了岁数的人来说,有点时间厚度的东西就带感。声音碎片的一句歌词特别好:“顺流而下,把梦做完”。

新年快乐!

近期面试感受

这两天大概面了5、6位应届的同学,最后往往忍不住聊的多了些,有点开复老师的感觉了。归纳下来有几个突出的问题。

1. 在公司实习学不到技术,学到的只是做事的方式
这些做事的方式(或者说技巧)有些是必要的,比如沟通方面。但更多是没有价值的,比如大公司繁琐的流程,各种毫无道理的开发规范,拖沓的做事风格等等。对于技术人员来说技巧性的东西千万不能太重。一个技术团队如果让实习生学不到专业上的东西,可以质疑其专业性了。应届生在学校,在公司如果都接触不到最专业的技术,那么他们又怎么能成长起来呢。一个恶性循环的开始。前端开发岗位的需求这么大,但始终缺少专业人才的一个根本原因吧。

2. 用的不是技术,更多是技巧
由于在学校里没有系统的前端开发课程,导致对html/css/javascript基本概念的理解非常薄弱。大部分人的学习方式是:先看书,然后觉得书和实践离得很远就直接实践,遇到问题就去网上搜,而搜到的基本都是“技巧”性的东西。或者是跟着学校里的“牛人”学,掺着各种好的、坏的经验全盘接受。比如实现一个左图右内容的显示效果,写出html和css(见下图)。这是我的一道笔试题,看起来很简单吧。但是还没有人答出最佳答案。如果去网上看,国内那些大网站们是怎么实现的,就不能怪他们了。

笔试题1

a方案 – 百度新闻首页:

<table>
 <tr>
  <td valign="top" class="imgtd">
   <a ...><img ...></a>
 </td>
 <td valign="top">...</td>
 </tr>
</table>

html结构丑陋,但css简单。

b方案 – 新浪微博首页:

<div class="twit_item MIB_linedot2">
  <div class="twit_item_pic">
    <a href="" target="_blank">
      <img src="" ...>
    </a>
  </div>
  <div class="twit_item_content">
   ....
  </div>
</div>

css实现:

.twit_list dd .twit_item_pic{float:left;width:66px;padding-top:2px;}
.twit_list dd .twit_item_content{float:left;width:316px;color:#666;line-height:18px;}

大部分应试者都是这个答案。用了浮动就必须定宽,结构就失去灵活性,同时必须解决浮动带来的一系列问题。

c方案 – 网易首页:

<div class="imgText-temp-1 dotline clearfix">
	<div class="mod-img main-img">
		<a href=""><img src="" ...></a>
	</div>
	<ul class="mod-list main-list">
	<li>...</li>
	<li>...</li>
	<li>...</li>
	<li>...</li>
	</ul>
</div>

css实现:

.imgText-temp-1 { overflow:hidden;padding-left:132px;margin-bottom:3px; }
.imgText-temp-1 .main-img { position:absolute;_display:inline;margin-left:-132px; }

作者意识到float的问题,他用了一个技巧:在容器左侧挤出一个132px的空白,再让.main-img定位为absolute,再向左移132px。
此方案典型的技巧性太重,不是应用技术解决问题,这是不应该提倡的。

这些写法都是欠缺对css基本概念的理解。如果理解block formatting context(块级格式化上下文)的概念, 就不会这么写了。触发了BFC的块级元素,它的边缘不会和float box重叠。所以正解是:

<div class="item">
  <div class="pic">...</div>
  <div class="content">...</div>
</div>

css实现:

.item .pic { float:left;margin-right:10px; }
.item .content { overflow:hidden;zoom:1; } /* 或用display:table-cell */

demo: http://hikejun.com/demo/css/demo_list.html

笔试有几年经验的人也是鲜有答对的。在新技术风起云涌的今天,太忽略基础的东西了。

3. “搜索”式学习害人不浅
面试中有同学觉得书上写的东西不实用,更喜欢边实践边学。但往往实践中主要解决具体问题,比如要实现上面那个例子,从网上搜到一个不好的例子,自己又不足以区分好坏。然后,就把它当成一个解决问题的模式,如果没人纠正,可能几年下来都这么用。网上的资源非常丰富,要区分“技术”和“技巧”。还是那句话,对技术人员来说技巧性的东西不应该太重。

4. 热衷新技术
古人讲究温故而知新还是很有道理的。学习新技术可以给自我镀金,欠缺基础的东西就会内力不足,这样在应用的过程中就会有问题。

5. 学到“二手货”
国内前端技术社区的分享质量总体还是不高。那些照搬国外,加上自己片面认识的资源就是“二手货”。这些资源在看的时候要慧眼识珠啊。最好还是直接看第一手资料。

6. 光看不用,坐等机会
很多同学表示正在看什么什么,或正准备学什么什么。但就是没动手写过,总是希望在实习公司有实践机会。这样的机会可能永远也没有呢,完善自我的技能,是需要自己给自己创造机会,写一些demo,搞一些个人项目,参加一些技术交流,持续关注该技术的发展……坐等只能浪费时间。

D2技术嘉年华分享:前端基础架构的实践和思考

这个话题是我一直以来思考的问题。其实4年前我在淘宝分享过一个基于ant的静态文件编译系统,但那时候对基础架构的全局还没有一个总体思路,现在看来它没有充分用在开发中,不是它本身做的不够好,而是因为它没有完全植入到整个技术体系中,只是一个单纯的工具,这样它发挥的作用始终是有限的。2年前我才真正意识到前端基础架构的意义,它是把技术更好的吸收、组合、应用出来的基础,也是一个足够专业的前端团队高效运转的基础。

我一直以来在追求前端团队职能价值的最大化。那么首先回顾一下前端职能经历了什么样的转变。我相信这些转变不需要过多解释,大家都能理解。但是又有多少团队真正转变过来了。职能和价值更多还是停留在页面是否高效的实现上。

区别只是人多了,分工过细,人被局限在一条流水线的各个环节上,被当成工具使用,没有发挥余地,也没有成长空间。前端工程师实际上沦为苦逼的‘码农’。所以需要一个更好的体制和环境把前端开发的职能作用充分发挥出来,创造更大的价值。同时,对于想上市或想创造奇迹的互联网公司们需要的也不是一两个技术牛人,而是一个更加专业的团队

首先角色定位必须清晰。前端工程师比设计师更懂得界面和交互的实现原理,所以要能弥补设计师或产品经理在技术理解上的不足,这是中国特色。开发的工程特点上跟后端开发非常接近,所以不能脱离整个技术体系,要尽量减少中间环节,共用开发环境,共用系统。前端开发在设计部门是过去时,现在更适合在技术部门。2是打造专业团队,这种专业性不是一两个牛人带来的,而是体制和长时间的技术积淀带来的。3也是通过深厚的技术基础和敏捷的开发方式带动团队高效运转,而不是通过行政管理手段。这个技术基础就是一个不断完善的向前演变的技术平台。最后是结果,带来技术上的持续创新。

今天要谈论的“前端基础架构”就是这个技术平台中的重要内容。什么是 “前端基础架构”?它应当具有这些特点和作用。要先做技术的布局,才能从大格局出发指导用什么和怎么用技术。细节可以先不够好,但整体要有,要能发挥作用。所有人不是机械的完成项目开发需求,而是创造性的应用技术,并且做好技术回收,有意识的积累下来。3是它有团结的作用,内聚所有人的智慧,调剂技术的活跃度,探索新领域。4是从体制上做到技术的大融合,让前后端开发无缝结合在一起。

总结我之前的经验教训,用这个故事可以很好的说明。不是片面做一个局部或放大局部的作用,比如做好一个工具或一个代码库,它的作用都是有限的。而要从大格局出发。所以,我整理了一张图。

这张图从下向上看,它就像一颗树,不断滋生枝干。基础架构的建设正是如些,也是一个不断生长的过程。其中主要的结点构成前端技术的大格局。最下面的是它的根结点,后面会逐一分解。

去年我在另一个活动上分享过同样的话题,不过那时候只做了2成,一年过去了,现在可以说做到4成。它就像一颗种子,需要所有工程师智慧的培育,才能长成大树。

工具、规范和系统看起来是独立的三大块,但它们之间是相互作用,紧密联系的。工具是为落实规范、保证品质定制的,系统又把工具有效的串联起来,建立一套有秩序的开发环境。mission是为了打造高品质的产品使用体验。工具体系由这些部分组成。代码管理是为了代码的模块化组织、提高重用、化繁为简的预处理,从而达到更高的可维护性和灵活性。品质检验的目的是保证代码符合我们约定的统一的代码Guidelines的要求。测试是为了避免异常,规避风险。上线后的运行监测是为了运营过程中的服务品质的持续性。这块是我们正准备做的。

我们在服务器端对无论是内联还是外联的js和css代码进行模块化的组织。使用这种@import的编译语法,在开发环境中看起来是这样的。上线后会将编译后的文件再部署到CDN

css文件除了支持@import编译语法外,还会支持SCSS语法,并且引入Compass的CSS3 Mixin Library,简化开发,使CSS开发更像语言那样去组织,提高开发效率。除此以外,我们还会进一步引入OOCSS概念,称之为Douban-UI,它是对通用信息元素进行对象化的抽象,建立的一套基本样式库。

库和框架是工具中的重要分支。它的根本目的是为了代码资源的重复利用。从格局出发拥抱开源技术,从项目开发中回收技术,结合自主开发弥补空白。除了建立丰富的通用功能库外,同样会重视应用架构的设计,比如应用框架这个分支包括模板系统、前端MVC框架等。在移动开发方面,因为其性能敏感等特殊性,我们会组成更合理的方案,比如用Zepto代替jQuery。在桌面开发上,有洪强宁(豆瓣的首席构架师)开发的onering,它是基于webkit,支持html5的桌面应用开发框架

规范仍然是最最基础的。规范应当具备可操作性,同时要有配套的检验工具。测试部门帮我们在CI中集成了Douban-JSLint,这是根据我们的规范要求定制后的JSLint,同时,我们也会把它植入到开发环境中,很方便的自检。技术文档的积累也是不能忽略的,它是重要的学习资源,结合培训,帮助新同学尽快溶入到团队中。同时,我们也欢迎后端工程师写前端代码,正如我们也会涉足后端一样。这种技能上的重叠,会让协作更默契。我们也正在努力通过规范和工具降低门槛、扫除障碍

我们有三种开发环境,前后端在同一环境下协作开发,尽量减少中间环节。开发前,参与的前后端工程师一起制订一份开发文档,内容主要是URL和对应模板的规划,Ajax的接囗的说明等等。然后,前端部分直接开发出最终的模板和实现交互,用假数据摸拟输出,不好实现的接囗会利用诸如Charles此类工具映射到本地的数据文件上。前后端开发是非常平行的。在项目管理上,也尽量用工具辅助,避免囗头提需求,而是以Ticket的形式启动后面的开发流程,这个过程是简捷而默契的

开发流程追求的是敏捷高效。为了保证最终产品的品质,该有的环节还是不能缺的。所以好的流程是将工具、系统和各种子系统完美的串起来,自动化程度高,同时又操作性强。

正好昨天有人在知乎上邀请我回答一个问题。可见团队大了,技术应用复杂了,要提升它的职能作用,建设前端基础技术架构,是一件非常有意义的事情。

slides: http://www.slideshare.net/kejun/ss-9413262

前端开发理论热点面对面:从怎么看,到怎么做?

View more presentations from kejun

常言道:温故而知新。当对新技术趋之若鹜时,重温一下前端开发中的一些非常基础的理论,对应该“怎么做”,思路会更加清晰。

本来这是对内部实习生的一次培训。也许很多人会期待听到jQuery, Do, CSS3, HTML5神马的,都没有出现。回归最根本,这是一次反思的过程,只有充分理解这些基础理论,才知道“怎么做”,才能做对。你没觉得现在充满了对各种技术偏激的追求,对国外技术盲目的跟随,却忽视这些技术的根本和由来。

聊聊响应性设计和开发

非常有幸受Franky之邀参加北京腾讯CDC“火山囗”活动。第一次同时面对100多设计师谈一些或许设计师会认为跟自己无关的技术话题,虽然我认为这个话题至少50%是设计相关的,感谢他们耐心的听完了。

响应性设计(Responsive UI),这是一个关于新时期设计策略的话题。幻灯里解释了它,我不想再赘述。

这是一个有意义的话题,主要解决跨设备跨平台布署产品的问题。从PC到各种移动设备,各个终端上的产品形态是在一个统一规划的设计策略下管理的。真正的大手笔啊,想想就令人兴奋。

简要列下我认为响应性设计和开发中的要素(具体要到幻灯里看):
1. 兼顾移动设计
2. 模块化设计和开发
3. 浏览器分级支持
4. 自适应布局
5. 资源响应性优化
6. 性能敏感

应用响应性设计不仅仅是变变布局,而是应该把各种设备的不同特点发挥在产品设计中,这里面包含了各种响应性问题。要做到兼顾各种设备是对设计师极大的挑战。而对于工程师来说,要考虑各种技术方案、fallback支持或是降级处理的综合性方案,最终形成一个响应性UI框架把开发的成本降下来,同样是极大的挑战。

这东西现在看起来是一个大公司不想玩、小公司玩不起的事。要实现这个大“阴谋”,需要设计和技术的通力配合。但一般大公司角色分工过于明确,都是各想各的事,不愿意尝试不确定的事,因此通常用人海战术分别解决问题而不是统一解决问题。于是,会有ipad.xx.xx, touch.xx.xx, wap.xx.xx… 这看起来很蠢。当后期维护成本大到无法承受,或看到别人做到时,我想最终会回到这个路子上。