作为一名正在学习前端开发的学生,我曾经也困惑过这样一个问题:为什么写在 <style>
标签里的样式可以控制网页的外观?CSS 是怎么被浏览器理解和应用的?
今天,我想通过这篇博客,以一个学生的视角,结合底层原理和实际案例,来系统地梳理一下 CSS 的基础概念、执行流程以及各类选择器的作用机制。
一、什么是 CSS?
CSS(Cascading Style Sheets,层叠样式表)是用于描述 HTML 或 XML 文档外观的样式语言。简单来说,HTML 负责结构,而 CSS 负责美化和布局。
例如下面这段代码:
css
h1 {
color: red;
text-align: center;
}
它的作用就是让所有 <h1>
标签的文字变成红色,并居中显示。
为什么 HTML 离不开 CSS?
我们可以把 HTML 想象成"骨架",而 CSS 就是"皮肤"和"衣服"。没有 CSS 的 HTML 页面就像一个裸奔的人------虽然功能存在,但视觉体验非常差。因此,可以说:HTML DOM 不能裸奔!
二、CSS 是怎么引入的?
CSS 可以通过三种方式引入到网页中:
1. 行内样式(Inline Styles)
直接写在 HTML 标签的 style
属性中:
html
<p style="color: blue;">这是一段蓝色文字</p>
优点是即时生效,缺点是难以维护,不推荐大规模使用。
2. 内联样式(Internal Styles)
写在 <head>
中的 <style>
标签里:
html
<head>
<style>
h1 {
color: red;
}
</style>
</head>
适用于单个页面的样式设置。
3. 外联样式(External Styles)
通过 <link>
引入外部 CSS 文件:
html
<link rel="stylesheet" href="styles.css">
这是最常见的方式,便于复用和维护。
注意顺序问题: 浏览器会先下载 CSS 文件,然后解析 HTML 构建 DOM 树,最后将两者合并生成 Render Tree(渲染树),最终绘制出可视化的页面。
三、CSS 的优先级机制
当多个 CSS 规则作用于同一个元素时,浏览器如何决定哪个规则起作用?这就涉及到 优先级(Specificity) 的计算。
1. 优先级的计算规则(权重值)
类型 | 权重值 |
---|---|
标签选择器 | 1 |
类选择器 | 10 |
ID 选择器 | 100 |
行内样式 | 1000 |
!important |
最高 |
举个例子:
css
/* 权重为 10 */
.red-text {
color: red;
}
/* 权重为 100 */
#main-title {
color: blue;
}
/* 权重为 1000 */
<h1 style="color: green">标题</h1>
最终颜色是绿色,因为行内样式的优先级最高。
2. 复杂选择器的优先级加法
对于组合选择器,浏览器会分别计算每个部分的权重并相加:
css
.container ul li:nth-child(odd)
.container
是类选择器 → 10 分ul
是标签选择器 → 1 分li
是标签选择器 → 1 分:nth-child(odd)
是伪类选择器 → 10 分
总分:10 + 1 + 1 + 10 = 22 分
注意:伪类选择器(如
:hover
、:nth-child()
)通常按类选择器计算权重。
四、CSS 选择器分类详解
CSS 选择器是用于定位 HTML 元素的工具。它们可以分为以下几大类:
1. 基础选择器
-
标签选择器(Element Selector)
cssp { font-size: 16px; }
选择所有
<p>
标签。 -
类选择器(Class Selector)
css.highlight { background-color: yellow; }
选择 class 为
highlight
的元素。 -
ID 选择器(ID Selector)
css#header { padding: 20px; }
唯一标识某个元素。
-
通配符选择器(Universal Selector)
css* { margin: 0; padding: 0; }
选择所有元素,性能较差,慎用。
2. 组合选择器
-
后代选择器(Descendant Selector)
css.nav a { color: white; }
选择
.nav
下的所有<a>
元素。 -
子选择器(Child Selector)
css.nav > a { color: white; }
只选择
.nav
的直接子元素<a>
,不会选到嵌套更深层的<a>
。 -
相邻兄弟选择器(Adjacent Sibling Selector)
cssh2 + p { font-weight: bold; }
选择紧接在
<h2>
后面的<p>
。 -
通用兄弟选择器(General Sibling Selector)
cssh2 ~ p { font-weight: bold; }
选择所有位于
<h2>
后面的同级<p>
。 -
案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 兄弟选择器 连着 */
h1 ~ p {
color:red;
}
/* 子元素选择器 */
.container > p{
font-weight: bold;
}
/* 后代选择器 */
.container p{
text-decoration:underline;
}
</style>
</head>
<body>
<div class="container">
<h1>标题</h1>
<p>这是第一段文字</p>
<p>这是第二段文字</p>
<a href="">链接</a>
<span>这是一个span元素</span>
<div class="inner">
<p>这是一个内部的段落</p>
</div>
</div>
</body>
</html>
效果:

这里的h1~p会将container下面的所有子类p进行css样式处理,如果换成h1 + p 则只会对第一个p(也就是相联结的p进行css处理)

3. 伪类选择器(Pseudo-class Selectors)
用于表示元素的某种状态或行为:
css
a:hover {
color: orange;
}
input:focus {
border-color: blue;
}
li:nth-child(even) {
background-color: #f0f0f0;
}
常见的伪类包括:
:hover
:focus
:active
:visited
:first-child
,:last-child
:nth-child()
案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 伪类选择不同的状态 */
button:active{
background-color: red;
color: white;
}
p:hover{
background-color: yellow;
}
::selection{
background-color: lightblue;
color: white;
}
input:focus{
border: 2px solid blue;
}
input:checked + label {
color:blue
}
li:nth-child(odd){
background-color: lightgray;
}
li:not(:last-child){
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>伪类选择器</h1>
<button>点击我</button>
<p>鼠标悬浮在这里</p>
<input type="text" placeholder="输入框">
<input type="checkbox" id="option1">
<label for="option1">选项1</label>
<input type="checkbox" id="option2" checked>
<label for="option2">选项2</label>
<ul>
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
</div>
</body>
</html>

一般用于处理一些跟属性行为相关的css样式
4. 伪元素选择器(Pseudo-element Selectors)
用于创建虚拟元素,常用于装饰性内容:
css
p::first-line {
font-weight: bold;
}
div::before {
content: "★";
}
div::after {
content: "☆";
}
常见伪元素包括:
::before
::after
::first-letter
::first-line
::selection
5. 属性选择器(Attribute Selectors)
根据 HTML 属性匹配元素:
css
input[type="text"] {
border: 1px solid gray;
}
a[href^="https://"] {
color: green;
}
常用属性选择器:
[attr=value]
:精确匹配[attr^=value]
:开头匹配[attr$=value]
:结尾匹配[attr*=value]
:包含匹配
五、CSS 在浏览器中的执行流程
现在我们已经了解了 CSS 的基本语法和选择器类型,那么它在浏览器中到底是怎么工作的呢?我们可以将其执行流程简化为以下几个步骤:
1. 解析 HTML,构建 DOM 树
浏览器首先读取 HTML 文件,将其解析为一棵 DOM(Document Object Model)树,表示文档的结构。
2. 解析 CSS,构建 CSSOM(CSS Object Model)
同时,浏览器解析 CSS 文件,构建 CSSOM,它是一个包含所有 CSS 规则的数据结构。
3. 合并 DOM 和 CSSOM,生成 Render Tree
浏览器将 DOM 和 CSSOM 合并,形成 Render Tree(渲染树),它只包含需要显示的节点及其样式信息。
4. 布局(Layout / Reflow)
浏览器计算每个节点在屏幕上的位置和大小。
5. 绘制(Paint)
将每个像素点绘制到屏幕上,形成最终的可视化页面。
6. 合成(Composite)
如果有多个图层(如动画、透明背景等),浏览器会进行图层合成,最终输出给用户。
注意: 如果 CSS 文件过大或选择器过于复杂,会影响 Render Tree 的构建速度,进而影响页面加载性能。
六、总结与思考
通过这次对 CSS 基础知识的梳理,我对浏览器如何处理样式有了更深的理解。CSS 并不是简单的"美化工具",它是前端开发中非常重要的一环,涉及页面渲染、性能优化、交互设计等多个方面。
作为初学者,我觉得掌握以下几点尤为重要:
- 理解 CSS 的优先级机制,避免样式冲突;
- 熟练使用各种选择器,提高代码可读性和效率;
- 掌握浏览器渲染流程,有助于写出更高效的代码;
- 注重性能优化,比如减少复杂选择器的使用。
未来,我也将继续深入学习 CSS 的高级特性,比如 Flexbox、Grid、动画、变量等,不断提升自己的前端技能。
如果你也在学习 CSS,欢迎留言交流,一起进步!
参考资料:
- MDN Web Docs - CSS Selectors
- W3Schools - CSS Syntax
- 《CSS权威指南》
- Chrome DevTools Performance 面板