ltaoo's web

类名与组件化

关于如何写类名,网上已经有很多讨论了:

总结来说,BEM命名法更好,下面就以自己的实际例子来讨论,为什么更好。

先来看两段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ul class="news">
<li class="news__item news__item--hot">news 1</li>
<li class="news__item">news 2</li>
<li class="news__item">news 3</li>
<li class="news__item">news 4</li>
</ul>
<style>
.news__item {
color: #eee;
}
.news__item--hot {
color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ul class="news">
<li class="hot">news 1</li>
<li>news 2</li>
<li>news 3</li>
<li>news 4</li>
</ul>
<style>
.news li {
color: #eee;
}
.news .hot {
color: red;
}
</style>

两段代码的作用都是相同的,即渲染新闻列表,颜色为#eee,并且第一条新闻颜色为red。不过可以看到两者的选择器不同,前者给每一个li标签添加了类名,而后者直接使用标签选择器li

性能

如果搜索关于网页渲染性能的文章,几年前(2011~2013 左右)都会提到选择器的优化。简单来说就是浏览器渲染样式,选择器是从右向左进行查找的,所以:

1
2
3
.news li {
color: #eee;
}

该段代码,会先找到所有的li标签,再筛选出在.news下的li标签,所以性能不如直接给li标签添加类名,使用类选择器。

同时给出了效率列表,从高至低排序:

  • id选择器(#myid)
  • 类选择器(.myclassname)
  • 标签选择器(div,h1,p)
  • 相邻选择器(h1+p)
  • 子选择器(ul > li)
  • 后代选择器(li a)
  • 通配符选择器(*)
  • 属性选择器(a[rel=”external”])
  • 伪类选择器(a:hover,li:nth-child)

来源 - CSS选择器的优化

但是,在现代浏览器下,使用标签选择器或者类选择器之间的差别已经可以忽略了。经过测试存在 10000 个标签时,可能只有 200ms 左右的渲染时间差别,如果考虑给li标签加类名导致增加了html文件字节数,总时间其实真的差不多。

易于维护

不过,从易于维护的角度来考虑,更建议使用类名选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="goods">
<h1>商品标题</h1>
<small>商品子标题</small>
<div class="info"></div>
</div>
<style>
.goods h1 {
color: #eee;
}
.goods small {
font-size: 14px;
}
.goods .info {
margin-top: 10px;
}
</style>

这是一个商品页面,有商品标题、子标题以及商品信息,需要给商品标题设置颜色,给子标题设置文字大小,上面的样式实现了该需求。

之后由另一个人来维护,需要给子标题设置一个颜色,所以要先在html文件内找到子标题对应的节点,而我们的html文件可能是这样的:

1
2
3
4
5
<div class="goods">
<h1></h1>
<small></small>
<div class="info"></div>
</div>

因为数据往往是通过ajax请求后填充到页面中。到底哪个是子标题呢,从语义化的角度,能够猜测出small可能是,那怎么确定呢,可以在浏览器打开页面右键审查元素,或者看看js文件,在填充数据的时候是将子标题填充到了哪个标签中:

1
2
var subTitle = data.subTitle;
$('.news').find('small').text(subTitle);

这两种方式都能够确定到底哪个标签是子标题,但是有没有更好的方式呢?如果直接给类名呢?

1
2
3
4
5
<div class="goods">
<h1 class="title__main"></h1>
<small class="title__sub"></small>
<div class="info"></div>
</div>

只要看了html,就知道要在样式文件中搜索title_sub就能够准确找到对应的样式。

组件化

还有一个理由就是,现在组件化的开发方式,要求我们将页面上的元素拆分为一个一个组件,尽可能通过组件的组合实现页面。

举个例子,一个新闻列表板块,可以分为TitleListItem三个组件:

组件的组合

这样划分是考虑到Title很可能在其他地方也要使用,封装为组件便于复用。

样式,同样可以依照这个思路,所以样式会这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.title {
line-height: 30px;
}
.title__main {
font-size: 20px;
color: #999;
}
.title__sub {
margin-top: 10px;
font-size: 12px;
}
.list {
padding: 10px;
background: #eee;
}
.item {
overflow: hidden;
padding: 4px;
line-height: 16px;
}
.item__title {
display: inline-block;
font-size: 14px;
}
.item__icon {
display: inline-block;
margin-left: 100px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<body>
<div class="news">
<div class="title">
<h1 class="title__main">新闻列表</h1>
<small class="title__sub">看天下新闻</small>
</div>
<ul class="list">
<li class="item">
<h2 class="item__title">新闻列表一</h2>
<i class="item__icon">*</i>
</li>
<li class="item">
<h2 class="item__title">新闻列表二</h2>
<i class="item__icon">*</i>
</li>
<li class="item">
<h2 class="item__title">新闻列表三</h2>
<i class="item__icon">*</i>
</li>
<li class="item">
<h2 class="item__title">新闻列表四</h2>
<i class="item__icon">*</i>
</li>
</ul>
</div>
</body>

组件化的选择器

重点是,在另一个地方同样需要相同样式的Title,只是文字不同,就可以将html结构复制,就能够快速得到相同样式的板块。

1
2
3
4
5
6
7
8
9
10
<div class="goods">
<div class="title">
<h1 class="title__main">商品介绍</h1>
<small class="title__sub">特色商品介绍</small>
</div>
<li class="item">
<h2 class="item__title">商品</h2>
<i class="item__icon">$</i>
</li>
</div>

组件复用

同时可以注意到,.news.goods都是和页面,或者说和业务有关的,这些无法“复用”的组件,就负责最终的布局,一般是设置位置与大小。

如果是react或者Vue,代码可能会是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="news">
<Title
title = "新闻列表"
subTitle = "看天下新闻"
/>
<List>
<Item
title = "新闻列表一"
icon = "*"
/>
<Item
title = "新闻列表二"
icon = "*"
/>
</List>
</div>
<div class="goods">
<Title
title = "商品介绍"
subTitle = "特色商品介绍"
/>
<Item
title = "商品"
icon = "$"
/>
</div>

总结

写代码的过程中,不只是实现功能,更多的要有自己的思考,总结出如何更好更快的写代码,就能够节省更多时间做其他的事情了。