05-基础篇:样式计算
juejin_logo copyCreated with Sketch.

前言

作为一名开发者,在看到设计图就应本能地在脑海中组织好大概的CSS代码用于排版与布局网页。虽然样式看上去很简单,但在编写样式时肯定会遇到以下情况。

  • 样式覆盖导致排版错位甚至乱套
  • 布局时不知使用什么长度单位声明属性
  • 引入第三方UI框架导致自定义样式失效
  • 同一节点同时出现多种类型样式时不知如何优雅解决

这些都是开发项目时的常见问题,我相信很多同学在遇到样式覆盖的问题时一言不合就使用!important暴力解决,这样真的好吗?

优先级别

在编写CSS时,使用不同选择器选中相同节点且为相同样式声明不同值,就会发生样式覆盖。发生样式覆盖时,应用哪个样式就由选择器的优先级别决定,因此样式覆盖的根本原因是未处理好规则间的优先级别。虽然使用!important能解决问题,但不能什么情况都由!important暴力解决。

为何样式有优先级别之分?当创建的样式越来越复杂,一个节点的样式将会受到越来越多的影响,该影响可能来自周围节点也可能来自自身。通过相关规范分配给规则一个权重,那样式可根据权重计算,呈现网页最终效果。

从以下方面了解优先级别,相信能更好把握优先级别的场景,通过优先级别解决样式覆盖的问题。

特性

优先级别具备以下特性,在熟练编写CSS后就能很易发现这些特性的存在。

  • 就近原则:后出现的样式其优先级别比先出现的样式更高
  • 继承样式:优先级别最低
  • !important样式:优先级别最高,若冲突则重新计算
  • 引入权重内联样式 > 内嵌样式 > 外部样式 > 导入样式

就近原则继承样式!important样式都很易理解,那引入方式该如何理解?那看看它们是如何表现的,需从文件层面理解。

内联样式

内联样式指在HTML标签中使用style声明样式,是最简单粗暴的样式声明方式。通常不推荐这样声明样式,它会导致HTML结构变得不纯净且体积变大,不利于SEO,也不利于后期维护。

<p style="color: #f66;">I am JowayYoung</p>

内嵌样式

内嵌样式指在<head>中使用<style>声明样式。通常在开发环境中使用,像webpack使用style-loader时就是将代码中的样式全部提取出来,以该方式声明网页样式。若多个网页使用公共样式,就要重复声明,网页数量越多就越难维护,当然网页样式较少的情况下还是使用该方式最好。

<head>
	<style>
		p {
			color: #f66;
		}
	</style>
</head>

外部样式

外部样式指在<head>中使用<link>引入样式,是最方便快捷的样式声明方式。通常在生产环境中使用,这样方便地将HTML代码CSS代码完全分离,为HTML语义化结构与表现完全分离提供技术上的支持。这样的处理不仅利于开发也利于维护,同时也是团队协作的最优CSS代码组织方式。

<head>
	<link rel="stylesheet" href="path/to/main.css">
</head>

导入样式

导入样式指在CSS类型文件中通过@import导入其他样式。导入样式其实与外部样式很相似,在HTML初始化时,会被导入到文件中成为文件的一部分。对于多个网页的公有样式,可将它们抽离出来,再通过@import导入样式到这些网页的css文件中。

<head>
	<style>
		@import url("path/to/common.css");
	</style>
</head>
@import url("path/to/common.css");

通过代码再结合就近原则,是不是更易理解?内联样式直接作用在节点中,因此优先级别最高。内嵌样式附属于html文件的解析而解析,因此优先级别跟着内联样式外部样式需加载完毕才能解析,因此优先级别跟着内嵌样式导入样式作用于内联样式外部样式中,因此优先级别跟着外部样式

最终就有了内联样式 > 内嵌样式 > 外部样式 > 导入样式的优先级别排序。

权重

上述都是基于文件层面的理解,那进入文件中所有样式都使用不同选择器声明,那如何区它们的分优先级别?选择器有着明显不可逾越的等级制度,可将其划分为六个权重等级。每个等级间的优先级别差距不可逾越,这些等级又称为权重。

以下从直观权重微观权重两方面表述。简而言之,数字组成的值越大其权重就越大。

直观权重

  • 10000!important
  • 1000内联样式
  • 100ID选择器
  • 10类选择器伪类选择器属性选择器
  • 1标签选择器伪元素选择器
  • 0通配选择器兄弟选择器后代选择器

微观权重

  • 1,0,0,0,0!important
  • 0,1,0,0,0内联样式
  • 0,0,1,0,0ID选择器
  • 0,0,0,1,0类选择器伪类选择器属性选择器
  • 0,0,0,0,1标签选择器伪元素选择器
  • 0,0,0,0,0通配选择器兄弟选择器后代选择器

总体来说直观权重与微观权重只是表达方式不同,但实际意义一样,使用公式可表达为以下形式。

!important > 内联样式 > ID选择器 > 类选择器 = 伪类选择器 = 属性选择器 > 标签选择器 = 伪元素选择器 > 通配选择器 = 兄弟选择器 = 后代选择器

计算

认识了优先级别的特性与权重,可通过它们计算出以下常见场景的样式。

优先级别相同的规则使用最后出现的规则

input {
	padding: 0 10px;
	border: none;
	border-radius: 5px;
	height: 30px;
	font-size: 16px;
	color: #fff;
}
input.input-box {
	background-color: #f66;
}
input:focus {
	background-color: #66f;
}
input[type=text] {
	background-color: #f90;
}

虽然类选择器伪类选择器属性选择器三者的优先级别相同,但最后出现的规则其优先级别最高,所以<input>的背景颜色最终会显示#f90

优先级别无视节点在DOM树中的距离

html h1 {
	color: #f66;
}
body h1 {
	color: #66f;
}

虽然<html>包括着<body>,但根据就近原则,所以<h1>的颜色最终会显示#66f

不同规则作用于相同节点使用优先级别最高的规则

#bruce {
	color: #f66;
}
[id=bruce] {
	color: #66f;
}

虽然两者规则都作用于IDbruce<h1>,但ID选择器的优先级别比属性选择器高,所以<h1>的颜色最终会显示#f66

:not()不参与优先级别的计算

:not()在优先级别计算中不会被看作伪类,但会把:not()中的选择器当作普通选择器计数。简而言之,忽略:not(),其他伪类照常参与优先级别计算。


通过上述示例可得到以下规则。

  • 规则的权值不同时,权值高的规则优先
  • 规则的权值相同时,后定义的规则优先
  • 属性后面追加!important时,规则无条件绝对优先

长度单位

粗糙的干活可能只需px%两个长度单位,随着终端设备分辨率的多样性提高,CSS衍生出越来越多长度单位,灵活结合这些长度单位能为网页的布局方案提供更多可能性。

单位定义类型描述
px像素绝对单位-
pt绝对单位1pt = 1/72in
pc绝对单位1pc = 12pt
mm毫米绝对单位-
cm厘米绝对单位-
in英寸绝对单位1in = 96px = 2.54cm
%百分比相对单位相对父节点尺寸,宽度相应,高度不一定相应
emM的宽度相对单位相对当前节点字体
remM的宽度相对单位相对根结点字体
ch0的宽度相对单位相对当前节点字体
exx的宽度相对单位相对当前节点字体
vw1%视窗宽度相对单位相对视窗
vh1%视窗高度相对单位相对视窗
vminvw/vh最小者相对单位相对视窗
vmaxvw/vh最大者相对单位相对视窗

这么多单位,到底如何区别?首先要明确一点,那就是屏幕分辨率

屏幕分辨率

屏幕分辨率指横纵向中的像素点数,单位是px屏幕分辨率确定计算机屏幕中能显示多少信息,以水平与垂直像素衡量。在屏幕尺寸一样的情况下,屏幕分辨率越低在屏幕中显示的像素越少,单个像素尺寸也较大,屏幕分辨率越高在屏幕中显示的像素越多,单个像素尺寸也较小。

屏幕分辨率就是屏幕中显示的像素个数,分辨率1920×1080意味着水平方向含有1920个像素数,垂直方向含有1080个像素数。在屏幕尺寸一样的情况下,屏幕分辨率越高,显示效果越细腻。这也是为何iPhone经常亮瞎眼睛的原因。

在同一网页中以px作为长度单位时,在不同屏幕分辨率中显示的大小可能不同。在低屏幕分辨率中像素较大,显示的网页元素也偏大偏模糊。实际上所有单位无论是绝对单位还是相对单位,最终都会转化为px在屏幕中显示,因此在设计与开发时都以px为准。

em/rem区别

emrem是移动端布局中特有的长度单位,两者的后缀都一样。rem全称是root em,指相对根节点作为参考的长度单位。

  • em:当前节点字体宽度,准确来说是一个M的宽度
  • rem:默认字体宽度,准确来说是一个M的宽度

两者区别在于:em相对父节点,rem相对根节点em以当前节点字体宽度作为参考,rem以根节点<html>字体宽度作为参考,默认是16px。很多同学错误地以为em是根据父节点作为参考,实际上是当前节点继承了父节点的属性后产生的错觉。

emrem都是很灵活且可扩展的长度单位,由浏览器自行转换为px,具体取决于设计图中的字体大小。

针对移动端,我通常会结合JS根据屏幕宽度与设计图宽度的比例动态声明<html>font-size,以rem为长度单位声明所有节点的几何属性,这样就能做到很多移动设备的网页兼容,兼容出入较大的地方再通过媒体查询做特别处理。

function AutoResponse(width = 750) {
	const target = document.documentElement;
	if (target.clientWidth >= 600) {
		target.style.fontSize = "80px";
	} else {
		target.style.fontSize = `${target.clientWidth / width * 100}px`;
	}
}

AutoResponse();

前提还需在<html>中声明以下代码,阻止用户缩放屏幕。

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
视窗比例单位

CSS3中增加了四个与viewport相关长度单位。随着时间推移,目前很多浏览器对这些长度单位都有较好的兼容,这也是未来最建议在伸缩方案中使用的长度单位。

  • 1vw1%视窗宽度
  • 1vh1%视窗高度
  • 1vmin1%视窗宽度与1%视窗高度中最小者
  • 1vmax1%视窗宽度与1%视窗高度中最大者

视窗宽高在JS中分别相应window.innerWdithwindow.innerHeight。若不考虑低版本浏览器的兼容,完全可用一行代码秒杀所有移动端的伸缩方案。

/* 基于UI width=750px DPR=2的网页 */
html {
	font-size: calc(100vw / 7.5);
}

这是calc()的一个神操作,在第7章会详细讲述calc()怎样玩。这行代码就留给大家思考,为何能这样处理,细心的同学可能发现这段代码可代替上述那段JS代码

留言
Ctrl + Enter
全部评论(25)
焦糖色的橙子的头像
删除
要洗
点赞
1
删除
你滴说中文的干货
1
回复
戈君101730的头像
删除
移动端适配,使用postcss-px-to-viewport 或者postcss-pxtorem 是不是更好呢?省去了个人转化
点赞
回复
夏旭晨的头像
删除
Java后端 @ 冇有人要
外联样式是link引入的样式,内联样式是直接写在标签上的样式,他们怎么会优先级一样,只有内联样式的优先级是1000吧,外联样式算什么?
点赞
回复
WeiYY的头像
删除
前端攻城狮
ex 单位应该是 字符“x”的高度
点赞
1
删除
(作者)
x的宽高一样的
点赞
回复
ClariS的头像
删除
前端摸鱼
“M的宽度”是指啥呢?
点赞
1
删除
(作者)
就是指字母M呀
点赞
回复
七天斋的头像
删除
前端工程师 @ 北京数之行科技有限公司
补充一个 line-height 的em 是相对自身的 非父级
点赞
回复
小艸的头像
删除
视窗宽高在JS中分别对应window.innerWdith 应该是window.innerWidth 吧。
1
2
删除
大概 window.visualViewport.height 和 window.visualViewport.width 更接近
点赞
回复
删除
只不过api比较新
大概 window.visualViewport.height 和 window.visualViewport.width 更接近
点赞
回复
塵影的头像
删除
焚膏继晷,兀兀穷年
权重方面 内联样式 应该是 大于 外联样式 吧。
#root {
background: red;
}
id="root" style="background:blue"
最终显示蓝色
4
回复
德育处主任的头像
删除
🛠️打杂
html {
font-size: calc(100vw / 7.5);
}
类似微信小程序的rpx原理对吧?
1
1
删除
(作者)
是小程序rpx参照这个原理的
1
回复
LeonardoRain的头像
删除
前端
作者笔误了吧:
1vmin表示1%视窗高度和1%视窗高度中最小者
1max表示1%视窗高度和1%视窗高度中最大者
应该改为
vmin:1% vw 和 1% vh 中较小的一个值
vmax:1% vw 和 1% vh 中较大的一个值
1
2
删除
(作者)
已修正了,谢谢提醒
1
回复
删除
表格中的还是没改。。😂
已修正了,谢谢提醒
点赞
回复
pediy的头像
删除
配些图 看起来就不会这么累
点赞
1
删除
(作者)
大部分都有图的,除了这一章
1
回复
小二上酒本尊的头像
删除
html h1 {
color: #09f;
}
body h1 {
color: #9c3;
}
虽然html包含着body,但是依据就近原则,所以h1最终会显示#66f。~~~~~~~~~~~~~~~~~~~~~~~~~
h1最终会显示#66f的值,是不是笔误了
展开
点赞
2
删除
(作者)
笔误了,已修正
点赞
回复
删除
所以这部分的无视距离与就近原则不是冲突吗?
笔误了,已修正
点赞
回复
占用号的头像
删除
1vmin表示1%视窗高度和1%视窗高度中最小者
1max表示1%视窗高度和1%视窗高度中最大者
这里应该写错了
点赞
1
删除
(作者)
抱歉,已修正了,谢谢提醒
点赞
回复