xml地图|网站地图|网站标签 [设为首页] [加入收藏]
构建单页Web应用,网易邮箱的CSS开发
分类:web前端

网易邮箱的CSS开发(一)

2012/06/03 · CSS · CSS

来源:网易邮箱的博客

网易邮箱是个庞大而且细节繁多的系统,注定了前端开发中样式管理的复杂程度非常高。如果没有一个合理的体系来管理样式,开发和维护的难度是不可想象的。从极速3.5版本开始,我们就一直遵循并不断改善这套规则,现在就来分享一下~

开发思想

在错综复杂的样式面前,CSS显得过于简陋。于是我们不得不人为的加上一些思维方式和规则来帮助我们管理样式。

我们借鉴了“面向对象”中的“封装”和“继承”来开发CSS,使我们能很大程度上重用CSS代码的同时又不至于在修改CSS代码时因为过度重用而难以下手。

每个页面都由很多元素(除非特别指出,本文的“元素”都不是指这样的HTML元素)组成,元素可以大到整个界面框架,也可以小到一个图标。元素相互嵌套、组合,形成了最终的页面。

对于任意元素,我们建立这样一种模型:

1、所有的元素内部都可以嵌套其他元素;

如:“a”里可以嵌套“a的头”、“b”等。

图片 1

2、元素内部的元素中,有的是“私有的”,只能在这个元素内使用,其样式也不会受父元素之外的CSS规则影响,有的是“公有的”,可以出现在任何位置;

如:“a的头”、“a的身体”是“a”的私有元素,“a的身体的某部分”是“a的身体”的私有元素,而“b”是公有元素,被“a”调用,“a”可以修改其内部的“b”的样式,但不能修改其他“b”的样式。

3、元素根据需要可以具备多种状态,可以给这个元素一个“参数”让它呈现不同状态;

如:设一个hasBorder=“true”可以让“a”有边框,设一个size=“big”可以让”a的身体的某部分”变大

4、可以从一类元素派生出另一类元素

如:我们需要和“a”类似的元素,但是需要对其进行扩展,这时我们新建一类“a2”元素,“a2”继承了“a”的所有样式和“私有元素”和“参数”

开发时,元素就像“类”一样被定义在CSS中,元素的“私有元素”、“参数”等也都定义在这个“类”中。

CSS类定义:

XHTML

/* a类 =================================*/ /* a类的定义 */ .a {...} /* a类的私有元素 */ .a的头{...} .a的身体{...} .a的身体的某部分{...} /* a类的参数 */ .a(hasBorder时){...} .a(size为big时){...} HTML中的a类元素的两个“实例”: <div class="a" id="a1"> <div class="a的头"></div> </div> <div class="a" id="a2"> <div class="a的头"></div> <div class="a的身体"></div> <div class="b"></div> </div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* a类
=================================*/
/* a类的定义 */
.a {...}
/* a类的私有元素 */
.a的头{...}
.a的身体{...}
.a的身体的某部分{...}
/* a类的参数 */
.a(hasBorder时){...}
.a(size为big时){...}
 
HTML中的a类元素的两个“实例”:
<div class="a" id="a1">
<div class="a的头"></div>
</div>
<div class="a" id="a2">
<div class="a的头"></div>
<div class="a的身体"></div>
<div class="b"></div>
</div>

命名和编码规则

以上所说的模型并不是CSS与生俱来的,所以需要一些命名和编码规则来加以实现,不过这个并不复杂。

1、我们使用连字符“-”从逻辑上划分class名,使用驼峰式命名区分单词;

如:.a-b-helloWorld

2、有时我们会给class加上一个命名空间前缀,命名空间与class之间也用连字符“-”连接;

如:.WB3-a-b-helloWorld,在极速4中,WB3命名空间下的class名将被压缩成1~3个字母短名称。(注:WB3是网易邮箱前端吊丝们给极速4起的开发代号……三楼水吧WaterBar3,碉堡了-。-……)

3、“私有元素”必须以父元素的class名作为前缀;

如:“a的身体的某部分”的名称是j,而它的父元素“a的身体”的名称是bd,“a”的名称是a,那么最后连起来的class就是.a-bd-j。

CSS

/* a类定义 =================================*/ .a{...} .a-bd-j{...} /* 允许在a类中定义a类的私有元素 */ /* b类定义 =================================*/ .b{...} .a-bd-j{...} /* 不允许在a类外定义a类的私有元素 */

1
2
3
4
5
6
7
8
/* a类定义
=================================*/
.a{...}
.a-bd-j{...} /* 允许在a类中定义a类的私有元素 */
/* b类定义
=================================*/
.b{...}
.a-bd-j{...} /* 不允许在a类外定义a类的私有元素 */

 

4、“参数”依然使用class实现,以能表达“是否”或具体取值的方式命名:

如:.hasIcon表达有图标,.hasBorder表达有边框,.sizeBig表达大尺寸的,.sizeSmall表达小尺寸的

***这里是一个理想状态(无IE6)下的命名方案,假设元素a提供以上状态,我们可以这样实现:

CSS

.a.hasIcon{...} .a.sizeBig{...} ...

1
2
3
.a.hasIcon{...}
.a.sizeBig{...}
...

为了IE6,我们不得不用这样的命名来兼容:

CSS

.a-hasIcon{...} .a-sizeBig{...} ...

1
2
3
.a-hasIcon{...}
.a-sizeBig{...}
...

5、从一类派生出另一类元素,命名上没有特别的要求,通过在HTML中的class同时写上基类和子类的class来实现;

 

CSS

/* 基类定义 =================================*/ .superclass{...} .superclass-element{...} /* 子类定义,扩展、重写基类定义 =================================*/ .myClass{...} .myClass-element{...} .myClass .superclass-element{...}

1
2
3
4
5
6
7
8
9
/* 基类定义
=================================*/
.superclass{...}
.superclass-element{...}
/* 子类定义,扩展、重写基类定义
=================================*/
.myClass{...}
.myClass-element{...}
.myClass .superclass-element{...}

 

 

by Sunji

 

赞 收藏 评论

图片 2

CSS line-height 属性的一些技巧

2015/08/18 · CSS · line-height

本文由 伯乐在线 - 吴鹏煜 翻译,JustinWu 校稿。未经许可,禁止转载!
英文出处:css-tricks。欢迎加入翻译组。

CSS中的line-height属性控制着两行文本之间的空白多少,通常是会设置成一个无单位数值(比如line-height:1.4),表明其与font-size属性所构成的比例。line-height在排版上是非常重要的一个属性,太低的话,文字的行与行之间会尴尬地挤在一起,太高的话它们又会尴尬地分的太开,无论哪一种都对阅读不利。不过你也许早就知道这一点了。

这篇文章中我们会着重介绍一些技巧,如果你知道(或者能搞清楚)line-height的确切值,你可以做得更有趣 。

构建单页Web应用

2015/12/27 · 基础技术 · 1 评论 · 单页

原文出处: 徐飞(@民工精髓V)   

让我们先来看几个网站:

coding

teambition

cloud9

注意这几个网站的相同点,那就是在浏览器中,做了原先“应当”在客户端做的事情。它们的界面切换非常流畅,响应很迅速,跟传统的网页明显不一样,它们是什么呢?这就是单页Web应用。

所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。它是AJAX技术的进一步升华,把AJAX的无刷新机制发挥到极致,因此能造就与桌面程序媲美的流畅用户体验。

其实单页应用我们并不陌生,很多人写过ExtJS的项目,用它实现的系统,很天然的就已经是单页的了,也有人用jQuery或者其他框架实现过类似的东西。用各种JS框架,甚至不用框架,都是可以实现单页应用的,它只是一种理念。有些框架适用于开发这种系统,如果使用它们,可以得到很多便利。

为每一行文本指定不同颜色

不幸的是,我们并没有::nth-line这个选择器。我们也不能指望``会靠得住,数不清的原因可能导致文本在某处被断开。

有一个办法,虽然不标准,就是利用元素的背景来充当文字的背景。

CSS

.text { -webkit-background-clip: text; -webkit-text-fill-color: transparent; }

1
2
3
4
.text {
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

也有另一个技巧,你可以用linear-gradient()加上色彩点来控制颜色之间不会互相渐变,让一个颜色结束之后突然开始另一个颜色。我们假设line-height的值为22px,那我们就可以让渐变每逢22px断开一次。

Sass

.text { background-image: linear-gradient( to bottom, #9588DD, #9588DD 22px, #DD88C8 22px, #DD88C8 44px, #D3DD88 44px, #D3DD88 66px, #88B0DD 66px, #88B0DD) }

1
2
3
4
5
6
7
8
9
10
11
12
.text {
  background-image: linear-gradient(
    to bottom,
    #9588DD,
    #9588DD 22px,
    #DD88C8 22px,
    #DD88C8 44px,
    #D3DD88 44px,
    #D3DD88 66px,
    #88B0DD 66px,
    #88B0DD)
}

图片 3

两个技巧结合之后:

图片 4

如果你的浏览器不支持text的background-clip,比如Firefox,那你就会看到位于文字背后的色彩长条,可能你会觉得这样看起来很酷甚至会很喜欢,但或许你宁可回到设置文字颜色的老路上来。如果是后者的话,你可以用@support来设置成:如果浏览器支持,css才生效。

另外,既然你反复的在利用line-height的值,说不定把他变成变量比较好。在这里我用SCSS,不过哪天可以使用真正的CSS变量那就真的太棒了,这样在页面渲染完还可以继续修改,并且看着它生效。

Sass

$lh: 1.4em; body { font-size: 1em; line-height: $lh; } @supports (-webkit-background-clip: text) { p { -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient( to bottom, #9588DD, #9588DD $lh, #DD88C8 $lh, #DD88C8 $lh*2, #D3DD88 $lh*2, #D3DD88 $lh*3, #88B0DD $lh*3, #88B0DD); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$lh: 1.4em;
 
body {
  font-size: 1em;
  line-height: $lh;
}
 
@supports (-webkit-background-clip: text) {
  p {
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-image: linear-gradient(
      to bottom,
      #9588DD,
      #9588DD $lh,
      #DD88C8 $lh,
      #DD88C8 $lh*2,
      #D3DD88 $lh*2,
      #D3DD88 $lh*3,
      #88B0DD $lh*3,
      #88B0DD);
  }
}

最简单的办法是在最上层的元素中应用这些属性,这里有个样例,它的最初几行是重点。

Sass

.text { -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient( to bottom, rgba(white, 0.8), rgba(white, 0.8) $lh, rgba(white, 0.6) $lh, rgba(white, 0.6) $lh*2, rgba(white, 0.4) $lh*2, rgba(white, 0.4) $lh*3, rgba(white, 0.2) $lh*3, rgba(white, 0.2)); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text {
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(
    to bottom,
    rgba(white, 0.8),
    rgba(white, 0.8) $lh,
    rgba(white, 0.6) $lh,
    rgba(white, 0.6) $lh*2,
    rgba(white, 0.4) $lh*2,
    rgba(white, 0.4) $lh*3,
    rgba(white, 0.2) $lh*3,
    rgba(white, 0.2));
}

图片 5

如果我们要操控到最末任意行,这将会更难。这样的话,我们就需要色带从头开始一路往下,直到倒数的几行,幸运的是我们可以用calc()来实现。

Sass

.text { -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient( to bottom, rgba(white, 0.8), rgba(white, 0.8) calc(100% - 66px), rgba(white, 0.6) calc(100% - 66px), rgba(white, 0.6) calc(100% - 44px), rgba(white, 0.4) calc(100% - 44px), rgba(white, 0.4) calc(100% - 22px), rgba(white, 0.2) calc(100% - 22px), rgba(white, 0.2)); background-position: bottom center; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text {
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(
    to bottom,
    rgba(white, 0.8),
    rgba(white, 0.8) calc(100% - 66px),
    rgba(white, 0.6) calc(100% - 66px),
    rgba(white, 0.6) calc(100% - 44px),
    rgba(white, 0.4) calc(100% - 44px),
    rgba(white, 0.4) calc(100% - 22px),
    rgba(white, 0.2) calc(100% - 22px),
    rgba(white, 0.2));
  background-position: bottom center;
}

图片 6

也有其他办法可以实现这种效果,比如叠加一层伪元素渐变,并设置pointer-events:none,以免形成干扰。

开发框架

ExtJS可以称为第一代单页应用框架的典型,它封装了各种UI组件,用户主要使用JavaScript来完成整个前端部分,甚至包括布局。随着功能逐渐增加,ExtJS的体积也逐渐增大,即使用于内部系统的开发,有时候也显得笨重了,更不用说开发以上这类运行在互联网上的系统。

jQuery由于偏重DOM操作,它的插件体系又比较松散,所以比ExtJS这个体系更适合开发在公网运行的单页系统,整个解决方案会相对比较轻量、灵活。

但由于jQuery主要面向上层操作,它对代码的组织是缺乏约束的。如何在代码急剧膨胀的情况下控制每个模块的内聚性,并且适当在模块之间产生数据传递与共享,就成为了一种有挑战的事情。

为了解决单页应用规模增大时候的代码逻辑问题,出现了不少MV*框架,他们的基本思路都是在JS层创建模块分层和通信机制。有的是MVC,有的是MVP,有的是MVVM,而且,它们几乎都在这些模式上产生了变异,以适应前端开发的特点。

这类框架包括Backbone,Knockout,AngularJS,Avalon等。

文字间的线

我们上面介绍了如何控制渐变色彩点,如果用相似的办法,我们可以创建以1px为单位的渐变,并重复直到达到line-height。最简单的办法是使用

repeating-linear-gradient来实现,同时也要保证其他元素乖乖就位(比如padding也是基于line-height)。

Sass

.parent { padding: $lh*2; background: #082838; background-image: repeating-linear-gradient( to bottom, rgba(white, 0) 0, rgba(white, 0) $lh/1em*16px-1, rgba(white, 0.1) $lh/1em*16px-1, rgba(white, 0.1) $lh/1em*16px ); }

1
2
3
4
5
6
7
8
9
10
11
.parent {
  padding: $lh*2;
  background: #082838;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(white, 0)   0,
    rgba(white, 0)   $lh/1em*16px-1,
    rgba(white, 0.1) $lh/1em*16px-1,
    rgba(white, 0.1) $lh/1em*16px
  );
}

为了创造1px的线,我们需要知道line-height以像素为单位的值,然后减1。减1是为了让渐变会准确的以已知的line-height来重复,并留出最后1px作为线。由于我们让body的font-size为1em,也就是16px,所以line-height的单位被设置为em,你可以通过除去1em来移除单位,然后乘以16px再减1来得到我们需要的数值。

图片 7

组件化

这些在前端做分层的框架推动了代码的组件化,所谓组件化,在传统的Web产品中,更多的指UI组件,但其实组件是一个广泛概念,传统Web产品中UI组件占比高的原因是它的厚度不足,随着客户端代码比例的增加,相当一部分的业务逻辑也前端化,由此催生了很多非界面型组件的出现。

分层带来的一个优势是,每层的职责更专一了,由此,可以对其作单元测试的覆盖,以保证其质量。传统UI层测试最头疼的问题是UI层和逻辑混杂在一起,比如往往会在远程请求的回调中更改DOM,当引入分层之后,这些东西都可以分别被测试,然后再通过场景测试来保证整体流程。

Position images one-per-line

还有一件知道line-height你可以做的事就是,让bakcground-size与其匹配,至少在垂直方向上要匹配。然后你就可以让背景垂直重复,最终效果就是:一行一个图片。

Sass

.text background-image: url(image.svg); background-size: $lh $lh; background-repeat: repeat-y; padding-left: $lh*2; }

1
2
3
4
5
6
.text
  background-image: url(image.svg);
  background-size: $lh $lh;
  background-repeat: repeat-y;
  padding-left: $lh*2;
}

See the Pen One line of Text Dif Color by Chris Coyier (@chriscoyier) on CodePen.

1 赞 1 收藏 评论

代码隔离

与开发传统页面型网站相比,实现单页应用的过程中,有一些比较值得特别关注的点。

从单页应用的特点来看,它比页面型网站更加依赖于JavaScript,而由于页面的单页化,各种子功能的JavaScript代码聚集到了同一个作用域,所以代码的隔离、模块化变得很重要。

在单页应用中,页面模板的使用是很普遍的。很多框架内置了特定的模板,也有的框架需要引入第三方的模板。这种模板是界面片段,我们可以把它们类比成JavaScript模块,它们是另一种类型的组件。

模板也一样有隔离的需要。不隔离模板,会造成什么问题呢?模板间的冲突主要存在于id属性上,如果一个模板中包含固定的id,当它被批量渲染的时候,会造成同一个页面的作用域中出现多个相同id的元素,产生不可预测的后果。因此,我们需要在模板中避免使用id,如果有对DOM的访问需求,应当通过其他选择器来完成。如果一个单页应用的组件化程度非常高,很可能整个应用中都没有元素id的使用。

关于作者:吴鹏煜

图片 8

童心、勇气、创意和传奇。(新浪微博:@Nappp) 个人主页 · 我的文章 · 13

图片 9

代码合并与加载策略

人们对于单页系统的加载时间容忍度与Web页面不同,如果说他们愿意为购物页面的加载等待3秒,有可能会愿意为单页应用的首次加载等待5-10秒,但在此之后,各种功能的使用应当都比较流畅,所有子功能页面尽量要在1-2秒时间内切换成功,否则他们就会感觉这个系统很慢。

从这些特点来看,我们可以把更多的公共功能放到首次加载,以减小每次加载的载入量,有一些站点甚至把所有的界面和逻辑全部放到首页加载,每次业务界面切换的时候,只产生数据请求,因此它的响应是非常迅速的,比如青云的控制台就是这么做的。

通常在单页应用中,无需像网站型产品一样,为了防止文件加载阻塞渲染,把js放到html后面加载,因为它的界面基本都是动态生成的。

当切换功能的时候,除了产生数据请求,还需要渲染界面,这个新渲染的界面部件一般是界面模板,它从哪里来呢?来源无非是两种,一种是即时请求,像请求数据那样通过AJAX获取过来,另一种是内置于主界面的某些位置,比如script标签或者不可见的textarea中,后者在切换功能的时候速度有优势,但是加重了主页面的负担。

在传统的页面型网站中,页面之间是互相隔离的,因此,如果在页面间存在可复用的代码,一般是提取成单独的文件,并且可能会需要按照每个页面的需求去进行合并。单页应用中,如果总的代码量不大,可以整体打包一次在首页载入,如果大到一定规模,再作运行时加载,加载的粒度可以搞得比较大,不同的块之间没有重复部分。

路由与状态的管理

我们最开始看到的几个在线应用,有的是对路由作了管理的,有的没有。

管理路由的目的是什么呢?是为了能减少用户的导航成本。比如说我们有一个功能,经历过多次导航菜单的点击,才呈现出来。如果用户想要把这个功能地址分享给别人,他怎么才能做到呢?

传统的页面型产品是不存在这个问题的,因为它就是以页面为单位的,也有的时候,服务端路由处理了这一切。但是在单页应用中,这成为了问题,因为我们只有一个页面,界面上的各种功能区块是动态生成的。所以我们要通过对路由的管理,来实现这样的功能。

具体的做法就是把产品功能划分为若干状态,每个状态映射到相应的路由,然后通过pushState这样的机制,动态解析路由,使之与功能界面匹配。

有了路由之后,我们的单页面产品就可以前进后退,就像是在不同页面之间一样。

其实在Web产品之外,早就有了管理路由的技术方案,Adobe Flex中,就会把比如TabNavigator,甚至下拉框的选中状态对应到url上,因为它也是单“页面”的产品模式,需要面对同样的问题。

当产品状态复杂到一定程度的时候,路由又变得很难应用了,因为状态的管理极其麻烦,比如开始的时候我们演示的c9.io在线IDE,它就没法把状态对应到url上。

本文由澳门新葡亰手机版发布于web前端,转载请注明出处:构建单页Web应用,网易邮箱的CSS开发

上一篇:前端图片选择问题,我们该何去何从 下一篇:没有了
猜你喜欢
热门排行
精彩图文