布局笔记(加密,未发布)

我们知道,页面渲染的一般过程为 JS > CSS > 计算样式 > 布局 > 绘制 > 渲染层合并:

其中,Layout(重排)和 Paint(重绘) 是整个环节中最为耗时的两环,所以我们应尽量避免使用触发这两个环节的 CSS 属性。

布局

我们可以把网页理解成是由一个个盒子排列组合而成的,那么盒子之间又是怎么排列布局的呢?

网页常见的布局方式大概有五种:普通文档流布局、浮动布局(Float)、绝对布局(Absolute)、弹性布局(Flex)、网格布局(Grid)。

普通文档流布局

默认的布局方式,由块级元素(display: block)和行内元素(display: inline)等组成,元素之间按照从左到右,从上到下的顺序排列。

浮动布局

相对于普通文档流布局,浮动布局会脱离普通文档流,分为左右浮动,一般会在普通文档流布局的上面进行界面的布局,如果想避免浮动布局遮盖普通布局的情况,可以考虑使用清除浮动。

绝对布局

元素使用 position: absolute 属性进行绝对布局,使用绝对布局的元素会脱离文档流,其定位是参考祖先元素中 position 为非 static 值的第一个元素。

弹性布局

也称 Flex 布局,是一个完整的模块,而不是一个单一属性,其中有的属性是设置在父元素上,有些则是设置在子元素上。如果我们说传统的布局是建立在块级元素和行内元素的文本流上,那么 Flex 布局就是建立在 flex-flow 的轴方向上的。

网格布局

是用于制定行与列的二维 CSS 布局方法,可以将页面分割成数个主要的区域,或者用来定义组件内部元素间的大小、位置和图层之间的关系。

以上是常用的五种网页布局方法,在实际项目中,我们应该根据场景选择适当的方法。…

语义化

HTML 的标签虽然不多,但在编写的过程中,也会时不时犹豫应该使用 div 还是 p 标签,是使用 span 还是 i 标签?不管使用哪个标签,大体上都能实现想要的效果。

HTML 语义化就是根据具体的内容,选择合适的标签进行代码的编写,这样既能便于开发者阅读和维护,也能让搜索引擎的爬虫更好地识别。简单的说,就是可以让机器更容易读懂网页内容。

如果用语义化的标签编写网页结构,可以写成如下结构:…

Element Class与Id

编写 CSS 是前端开发中,比较愉快的一步。在此过程中,你可以一步步见证代码神奇的力量。编写可用的 CSS 比较容易,但是要维护它却不简单。

CSS 是一种定义样式结构,被用于描述网页上信息的排版方式的语言。由于其声明属性的方式不具备编程语言流程性控制的特点,而且自身「层叠」的特性,难以写出低耦合度的代码。不好好组织,容易造成不同地方的 class 相互影响,引起样式冲突等问题。所以 CSS 命名是样式代码组织中最重要的一环。

BEM

在各类 CSS 命名规范中,BEM 命名规范被更多人所接受。

BEM 是一种基于组件的命名方法,它的基本思想是将用户界面划分成独立的模块,即使是复杂的用户界面,也能让开发过程变得简单、快速。并且可以在一定程度上提高代码的可复用性,而不用纯粹的复制粘贴。

BEM 的意思就模块(Block)、元素(Element)、修饰符(Modifier),使用这种命名方式可以让 CSS 的类名变得有实际意义且能自我解释,具有更高的开发友好性。

Block - 模块,名字的单词之间用 `-` 符号连接
Element - 元素,模块中的子元素,用 `__` 符号连接
Modifier - 修饰符,表示父元素或子元素的其他形态,用 `--` 符号连接

在没用 BEM 之前,我们可能会这样组织 CSS 类名:

<!-- S Search Bar 模块 -->
<div class="search-bar">
<input class="input">
<!-- / input 输入框 -->
<button class="btn">
<!-- / button 搜索按钮 -->
</div>
<!-- E Search Bar 模块 -->

上述写法虽然也给 class 赋予了一定的语义,但容易产生样式冲突的情况。

用 BEM 命名重写之后:

<!-- S Search Bar 模块 -->
<div class="search-bar">
<input class="search-form__input"/>
<!-- / input 输入框子元素 -->
<button class="search-form__button"></button>
<!-- / button 搜索按钮子元素 -->
</div>
<!-- E Search Bar 模块 -->

这样命名的好处是,模块语义化了,便于后期的维护,而且减少了 CSS 样式的层层嵌套,提升了网页的渲染效率。

通常在开发中使用 BEM 命名方法,会搭配 CSS 的预处理语言,如 SCSS 等。这可以一定程度上解决手写冗长命名的繁琐。

// 以下是 SCSS 代码
.search-bar {
&__input { ... }
&__button { ... }
}

将 BEM 用于中大型项目之后,我们会发现,当嵌套的层级越多时,类名也会越长,这给编写 HTML 代码带来了一些麻烦,同时也增加了 HTML 的文件大小。

那么问题来了,如何解决 BEM 命名冗长的问题?

姓氏命名法

为了进一步简化 CSS 的命名,推荐的 CSS 命名规范并不严格遵循 BEM 规范,不强制使用两个下划线「_ _ 」来分隔 B 和 E,而 E 和 M 之间也不一定要用两个中划线做分隔「- -」。

如:
简化版的 BEM 好像也没有解决命名冗长的问题呀?

确实,如果按照这种继承的写法,再结合「给小孩取名」的生活场景,会出现下面的情况:

有位同学的名字叫「李小璐」,他的儿子名字叫「李小璐乃亮」,他的孙子叫「李小璐乃亮皮几万」。。。

而事实上,他的孩子只需要保留「李」姓就可以了,名字是可以随便取的。

所以在纠结怎么给一个元素做 CSS 命名的时候,联想一下我们身边的姓名是怎么起的吧。我们在业务中推广使用的「姓氏命名法」也因此而诞生。

如果要关联上 BEM 命名方法,姓氏命名法中的 Block 就是「姓」,Element 就是「名字」,而 Modifier 就表示这个人的某种状态,例如:「范冰冰 - - 很美」。

如何优化?

对于上面 app_market_answer 的案例,我们可以确定模块的姓氏是「app_market_answer」,名字随意取的话,我们可以尝试如下优化:

<div class="app_market_answer">
<div class="app_market_secheader"></div>
<div class="app_market_answer_list">
<div class="app_market_answer_item">
<div class="app_market_answer_itop"></div>
<div class="app_market_answer_imid"></div>
<a href="javascript:;" class="app_market_answer_ibtn">去围观</a>
</div>
</div>
</div>

我们将 app_market_answer_item_top 改成了 app_market_answer_itop ,将 app_market_answer_item_middle 改成了 app_market_answer_imid ,只保留了「姓」。

如何进一步优化?

姓氏可以进一步简化,例如 app_market 可以看成是「复姓」,我们有时候为了书写便利,可以将两个单词的首字母结合在一起形成一个新的「单姓」,如 am 。追求便利的副作用之一是牺牲了代码的可读性。如果你做的项目或页面没有太大的二次维护或者交叉维护的可能性,推荐做此简化。

对于上面 app_market_answer 的案例,我们可以进一步优化成:

<!-- am = app_market -->
<div class="am_answer">
<div class="am_secheader"></div>
<div class="am_answer_list">
<div class="am_answer_item">
<div class="am_answer_itop"></div>
<div class="am_answer_imid"></div>
<a href="javascript:;" class="am_answer_ibtn">去围观</a>
</div>
</div>
</div>

总结
1.ClassName 的命名应该尽量精短、明确,以英文单词命名,且全部字母为小写,避免意义不明的缩写
2.单词之间统一使用下划线 _ 或 - 连接
3.学习 BEM 的思想,参考使用姓氏命名法规范
4.定义样式模块,提高代码的可复用性

响应式页面开发

响应式页面开发的能力可以定义为:

利用一套代码实现页面的布局和排版以适配不同分辨率的设备。

响应式页面开发要求我们解决两大问题:

为不同特性(如横屏还是竖屏等)的浏览器视窗使用不同的样式代码
让页面元素的尺寸能够依据浏览器视窗尺寸变化而平滑变化

viewport meta

在页头 head 标签内添加 viewport meta 标签是实现响应式页面的第一步。

viewport meta 标签源于 Apple 公司,用来定义 iOS Safari 浏览器展示网页内容的可视范围及缩放比率。它虽然没有成为W3C标准,但是被其他绝大多数的移动端浏览器所支持(目前已知 IE Mobile 10 不支持)。W3C 尝试将 viewport meta 标签的功能进行标准化并通过 CSS 的 @viewport 规则来实现同样的功能,但这个标准目前还在草案中,兼容性也没有 viewport meta 标签好。

PageSpeed 准则
Google 网页性能分析工具 PageSpeed Insights 的其中一条准则就是:

网页应在 head 标签内添加 viewport meta 标签,以便优化在移动设备上的展示效果,其推荐的设置为:

<meta name="viewport" content="width=device-width, initial-scale=1">

Media Queries

Media Queries 是为指定特性的浏览器视窗应用指定样式的手段,可以看成是 CSS 样式的过滤器或拦截器,通常情况下它可以通过 「@media 规则」结合「6 个查询参数」来拦截设备的浏览器特性(如显示类型、视窗高度、视窗宽度、横竖屏等),藉此可以为不同的特性应用不同的样式代码(相当于为不同的设备应用了不同的 CSS 样式)。

6 个参数

参数名称参数描述
min-width当视窗宽度大于或等于指定值时,@media 规则下的样式将被应用
max-width当视窗宽度小于或等于指定值时,@media 规则下的样式将被应用
min-height当视窗高度大于或等于指定值时,@media 规则下的样式将被应用
max-height当视窗高度小于或等于指定值时,@media 规则下的样式将被应用
orientation=portrait当视窗高度大于或等于宽度时,@media 规则下的样式将被应用
orientation=landscape当视窗宽度大于高度时,@media 规则下的样式将被应用

2 种用法

方法 1,使用 link 标签,根据指定特性引入特定的外部样式文件

<link rel="stylesheet" media="(max-width: 640px)" href="max-640px.css">

方法 2,直接在 style 标签或 样式文件内使用 @media 规则

@media (max-width: 640px) {
/*当视窗宽度小于或等于 640px 时,这里的样式将生效*/
}

样式断点
Media Queries 所使用的查询参数的临界值又可称为「样式断点」。 在响应式页面开发过程中,对于「样式断点」我们需要掌握 2 个重要的技巧:

依据目标设备的分辨率,制定一套合适的样式断点,并为不同的断点定制必要的 CSS 样式。 移动端优先的页面,可使用 min-width 查询参数从小到大来定义断点。 如果我们页面的响应式设计要涵盖从手机到高清大屏幕,什么样的「样式断点」比较合理呢?

我们可以从业界一些热门可靠的 CSS 框架中寻找参考答案,例如 Bulma,其采用的「样式断点」有 5 个:

断点名称断点描述
mobile移动设备断点,视窗宽度 ≤ 768 px
tablet平板电脑设备断点,视窗宽度 ≥ 769 px
desktop桌面电脑断点,视窗宽度 ≥ 1024 px
widescreen宽屏电脑断点,视窗宽度 ≥ 1216 px
fullhd当高清宽屏电脑断点,视窗宽度 ≥ 1408 px

在实际工作中,「样式断点」的制定需要我们同视觉设计师一起沟通确认,因为视觉设计师可能需要根据不同的断点为页面设计不同的视觉表现。

一个小例子

如果针对 tablet 及以上的设备定制样式,我们就可以这样写了:

@media (min-width: 769px) {
/* tablet 及以上的设备,页面背景色设置为红色 */
body {
background-color: red;
}
}

使用 Viewport 单位及 rem

Media Queries 只解决了「为不同特性的浏览器视窗使用不同的样式代码」的问题,而 Viewport 单位及 rem 的应用,则是为了解决第二个问题:让页面元素的尺寸能够依据浏览器视窗尺寸变化而平滑变化。

关于 Viewport 单位及 rem 单位的基本概念,可通过下面的扩展阅读进行学习

BTW:本文所提及的 Viewport,译为「视窗」,其含义与扩展阅读中相关文章中的「视口」一致。

方法 1 - 仅使用 vw 作为 CSS 长度单位

在仅使用 vw 单位作为唯一 CSS 单位时,我们需遵守:

1.利用 Sass 函数将设计稿元素尺寸的像素单位转换为 vw 单位

// iPhone 6尺寸作为设计稿基准
$vw_base: 375;
@function vw($px) {
@return ($px / $vm_base) * 100vw;
}

2.无论是文本字号大小还是布局高宽、间距、留白等都使用 vw 作为 CSS 单位

.mod_nav {
background-color: #fff;
&_list {
display: flex;
padding: vw(15) vw(10) vw(10); // 内间距
&_item {
flex: 1;
text-align: center;
font-size: vw(10); // 字体大小
&_logo {
display: block;
margin: 0 auto;
width: vw(40); // 宽度
height: vw(40); // 高度
img {
display: block;
margin: 0 auto;
max-width: 100%;
}
}
&_name {
margin-top: vw(2);
}
}
}
}

  1. 1物理像素线(也就是普通屏幕下 1px ,高清屏幕下 0.5px 的情况)采用 transform 属性 scale 实现
    .mod_grid {
    position: relative;
    &::after {
    // 实现1物理像素的下边框线
    content: '';
    position: absolute;
    z-index: 1;
    pointer-events: none;
    background-color: #ddd;
    height: 1px;
    left: 0;
    right: 0;
    top: 0;
    @media only screen and (-webkit-min-device-pixel-ratio: 2) {
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin: 50% 0%;
    }
    }
    ...
    }

4.对于需要保持高宽比的图,应改用 padding-top 实现

.mod_banner {
position: relative;
// 使用padding-top 实现宽高比为 100:750 的图片区域
padding-top: percentage(100/750);
height: 0;
overflow: hidden;
img {
width: 100%;
height: auto;
position: absolute;
left: 0;
top: 0;
}
}

友情提醒:桌面版 Chrome 支持的字体大小默认不能小于 12PX,可通过 「chrome://settings/ 显示高级设置-网络内容-自定义字体-最小字号(滑到最小)」设置后再到模拟器里体验 DEMO。

方法 2 - vw 搭配 rem,寻找最优解

方法 1 实现的响应式页面虽然看起来适配得很好,但是你会发现由于它是利用 Viewport 单位实现的布局,依赖于视窗大小而自动缩放,无论视窗过大还是过小,它也随着视窗过大或者过小,失去了最大最小宽度的限制,有时候不一定是我们所期待的展示效果。试想一下一个 750px 宽的设计稿在 1920px 的大屏显示器上的糟糕样子。

当然,你可以不在乎移动端页面在 PC 上的展现效果,但如果有低成本却有效的办法来修复这样的小瑕疵,是真切可以为部分用户提升体验的。

我们可以结合 rem 单位来实现页面的布局。rem 弹性布局的核心在于根据视窗大小变化动态改变根元素的字体大小,那么我们可以通过以下步骤来进行优化:

给根元素的字体大小设置随着视窗变化而变化的 vw 单位,这样就可以实现动态改变其大小
其他元素的文本字号大小、布局高宽、间距、留白都使用 rem 单位
限制根元素字体大小的最大最小值,配合 body 加上最大宽度和最小宽度,实现布局宽度的最大最小限制
核心代码实现如下:

// rem 单位换算:定为 75px 只是方便运算,750px-75px、640-64px、1080px-108px,如此类推
$vw_fontsize: 75; // iPhone 6尺寸的根元素大小基准值
@function rem($px) {
     @return ($px / $vw_fontsize ) * 1rem;
}
// 根元素大小使用 vw 单位
$vw_design: 750;
html {
    font-size: ($vw_fontsize / ($vw_design / 2)) * 100vw
    // 同时,通过Media Queries 限制根元素最大最小值
    @media screen and (max-width: 320px) {
        font-size: 64px;
    }
    @media screen and (min-width: 540px) {
        font-size: 108px;
    }
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
    max-width: 540px;
    min-width: 320px;
}

扩展阅读
《利用视口单位实现适配布局》

高效CSS属性

我们知道,页面渲染的一般过程为 JS > CSS > 计算样式 > 布局 > 绘制 > 渲染层合并:

其中,Layout(重排)和 Paint(重绘) 是整个环节中最为耗时的两环,所以我们应尽量避免使用触发这两个环节的 CSS 属性。

例如,最基本的一个注意点是:使用切换类名的方式来触发动画,而不是使用 diaplay: none 属性值,因为它会引起相关元素的重排和重绘。

除此之外,下面几个属性的替换使用也是值得推荐的:

  • translate 属性替换 top/left/right/bottom
  • scale 属性换 width/height
  • opacity 属性替换 display/visibility

这里推荐 CSS Triggers,通过它你可以查阅到各 CSS 属性及其影响的环节,从而避免不小心使用到性能开销较大的属性。

除此之外,对动画渲染的优化还有其他方式,这里抛出 @登平登平 在 《H5 动画 60fps 之路6》一文中的总结,同学们可以前往原文进行进一步的学习。

本文标题:布局笔记(加密,未发布)

文章作者:Seven

发布时间:2018年03月04日 - 17:03

最后更新:2018年06月13日 - 17:06

原始链接:http://www.yuanziwen.cn/2018/03/04/布局笔记/

许可协议: 署名-非商业性使用 转载请保留原文链接及作者。