第2章:CSS 现代能力
从会用到会设计:掌握现代 CSS 的核心能力,不仅能写样式,还能架构样式系统。本章将深入讲解布局、现代特性、架构模式,为构建可维护的样式系统打下基础。
📋 本章内容
🎯 学习目标
- 理解 CSS 层叠、优先级、盒模型等基础概念
- 掌握 Flexbox 和 Grid 布局的核心用法
- 学会使用现代 CSS 特性(变量、容器查询、:has())
- 建立 CSS 架构思维,设计可维护的样式系统
🏗 基石概念
层叠与优先级
CSS 的层叠规则决定了当多个规则应用到同一个元素时,哪个规则会生效:
优先级计算(从高到低):
1. 来源:内联样式 > 作者样式 > 用户样式 > 浏览器默认样式
2. 重要性:!important
3. 指定度:ID > 类 > 元素
4. 顺序:后声明的覆盖先声明的
/* 指定度计算示例 */
#header .nav a { color: blue; } /* 指定度:0,1,0,1 = 101 */
.nav a { color: red; } /* 指定度:0,0,1,1 = 11 */
a { color: green; } /* 指定度:0,0,0,1 = 1 */
/* 结果:第一个规则生效,链接显示蓝色 */
盒模型
理解盒模型是 CSS 布局的基础:
/* 标准盒模型 */
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 5px solid #333;
margin: 10px;
/* 总宽度 = 200 + 40 + 10 + 20 = 270px */
}
/* 推荐:使用 border-box */
* {
box-sizing: border-box;
}
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 5px solid #333;
/* 总宽度 = 200px(包含 padding 和 border) */
}
格式化上下文
格式化上下文决定了元素如何布局和定位:
/* BFC(块级格式化上下文)特性 */
.bfc-container {
overflow: hidden; /* 触发 BFC */
/* 或者使用 */
display: flow-root; /* 更语义化的方式 */
}
/* BFC 解决的问题 */
.clearfix::after {
content: '';
display: table;
clear: both;
}
/* 现代解决方案 */
.clearfix {
display: flow-root;
}
🎨 布局三板斧
Flexbox(轴向分布)
Flexbox 是一维布局方法,适合处理组件内部元素的排列:
基础概念
.flex-container {
display: flex;
flex-direction: row; /* 主轴方向:row | column | row-reverse | column-reverse */
flex-wrap: nowrap; /* 换行:nowrap | wrap | wrap-reverse */
justify-content: flex-start; /* 主轴对齐:flex-start | center | flex-end | space-between | space-around */
align-items: stretch; /* 交叉轴对齐:stretch | flex-start | center | flex-end | baseline */
gap: 12px; /* 子元素间距 */
}
实战示例
<!-- 经典三栏布局 -->
<div class="row">
<div class="sidebar">Sidebar</div>
<div class="main">Main Content</div>
<div class="aside">Aside</div>
</div>
.row {
display: flex;
gap: 12px;
min-height: 100vh;
}
.sidebar {
flex: 0 0 200px; /* flex-grow: 0, flex-shrink: 0, flex-basis: 200px */
background: #f5f5f5;
}
.main {
flex: 1; /* flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
background: #fff;
}
.aside {
flex: 0 0 300px;
background: #f0f0f0;
}
/* 响应式:小屏幕时变为垂直布局 */
@media (max-width: 768px) {
.row {
flex-direction: column;
}
.sidebar, .aside {
flex: none;
}
}
高级技巧
/* 1. 垂直居中 */
.center {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* 2. 等分布局 */
.equal-columns {
display: flex;
}
.equal-columns > * {
flex: 1; /* 所有子元素等分空间 */
}
/* 3. 自适应布局 */
.adaptive {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.adaptive > * {
flex: 1 1 300px; /* 最小宽度 300px,可伸缩 */
}
Grid(二维网格)
Grid 是二维布局方法,适合处理复杂的页面布局:
基础概念
.grid-container {
display: grid;
grid-template-columns: 200px 1fr 200px; /* 列定义 */
grid-template-rows: 60px 1fr 60px; /* 行定义 */
gap: 1rem; /* 网格间距 */
grid-template-areas: /* 区域命名 */
"header header header"
"sidebar main aside"
"footer footer footer";
}
实战示例
<!-- 经典页面布局 -->
<div class="page">
<header>Header</header>
<aside>Sidebar</aside>
<main>Main Content</main>
<aside class="aside">Aside</aside>
<footer>Footer</footer>
</div>
.page {
display: grid;
grid-template:
"header header header" 60px
"sidebar main aside" 1fr
"footer footer footer" 60px
/ 200px 1fr 200px;
height: 100vh;
gap: 1rem;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: main; }
.aside { grid-area: aside; }
footer { grid-area: footer; }
/* 响应式布局 */
@media (max-width: 768px) {
.page {
grid-template:
"header" 60px
"main" 1fr
"sidebar" auto
"aside" auto
"footer" 60px
/ 1fr;
}
}
高级技巧
/* 1. 自适应网格 */
.auto-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
/* 2. 复杂网格布局 */
.complex-grid {
display: grid;
grid-template-columns:
[sidebar-start] 200px
[sidebar-end main-start] 1fr
[main-end aside-start] 200px
[aside-end];
grid-template-rows:
[header-start] 60px
[header-end content-start] 1fr
[content-end footer-start] 60px
[footer-end];
}
/* 3. 子网格(CSS Grid Level 2) */
.subgrid {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1;
}
现代流式技巧
/* 1. minmax() 函数 */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/* 2. clamp() 函数 - 响应式字体 */
.responsive-text {
font-size: clamp(1rem, 2.5vw, 2rem);
}
/* 3. auto-fit vs auto-fill */
.auto-fit {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* 空列会被压缩 */
}
.auto-fill {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* 保持列数,空列保持最小宽度 */
}
🚀 现代 CSS 特性
自定义属性(CSS 变量)
CSS 变量是构建主题系统和设计令牌的基础:
/* 定义变量 */
:root {
/* 颜色系统 */
--color-primary: #42b883;
--color-secondary: #35495e;
--color-success: #4caf50;
--color-warning: #ff9800;
--color-error: #f44336;
/* 间距系统 */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
/* 字体系统 */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
/* 圆角系统 */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
}
/* 使用变量 */
.button {
background-color: var(--color-primary);
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
font-size: var(--font-size-base);
}
/* 动态主题切换 */
:root[data-theme="dark"] {
--color-primary: #64b5f6;
--color-secondary: #e0e0e0;
--color-bg: #121212;
--color-text: #ffffff;
}
/* 局部变量覆盖 */
.card {
--card-padding: var(--space-lg);
padding: var(--card-padding);
}
.card--compact {
--card-padding: var(--space-sm);
}
容器查询
容器查询允许组件根据自身尺寸而不是视口尺寸来调整样式:
/* 定义容器上下文 */
.card {
container-type: inline-size;
/* 或者使用 container: inline-size; */
}
/* 基于容器宽度的样式 */
@container (min-width: 300px) {
.card__content {
display: flex;
gap: 1rem;
}
.card__image {
flex: 0 0 120px;
}
}
@container (min-width: 500px) {
.card__content {
flex-direction: row;
}
.card__title {
font-size: 1.25rem;
}
}
/* 容器查询单位 */
.responsive-text {
font-size: 10cqw; /* 容器宽度的 10% */
padding: 5cqh; /* 容器高度的 5% */
}
:has() 选择器
:has()
选择器允许根据子元素状态来设置父元素样式:
/* 父级感知选择器 */
.field:has(input:focus) {
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(66, 184, 131, 0.2);
}
.field:has(input:invalid) {
border-color: var(--color-error);
}
/* 导航高亮 */
.nav li:has(> a[aria-current="page"]) {
background-color: var(--color-primary);
color: white;
}
/* 卡片交互 */
.card:has(img:hover) {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* 表单验证状态 */
.form-group:has(.error-message) {
margin-bottom: 1.5rem;
}
.form-group:has(.error-message) input {
border-color: var(--color-error);
}
嵌套(CSS 原生支持)
现代浏览器开始支持原生 CSS 嵌套:
/* 原生 CSS 嵌套 */
.card {
padding: 1rem;
border-radius: 0.5rem;
background: white;
/* 嵌套选择器 */
&__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
&-title {
font-size: 1.25rem;
font-weight: 600;
}
}
&__content {
line-height: 1.6;
p {
margin-bottom: 0.5rem;
&:last-child {
margin-bottom: 0;
}
}
}
/* 嵌套媒体查询 */
@media (max-width: 768px) {
padding: 0.75rem;
&__header {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
}
}
🎭 动画与过渡
基础过渡
/* 简单过渡 */
.button {
background-color: var(--color-primary);
transition: background-color 0.3s ease;
}
.button:hover {
background-color: var(--color-secondary);
}
/* 多属性过渡 */
.card {
transform: translateY(0);
opacity: 1;
transition:
transform 0.3s ease,
opacity 0.3s ease,
box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
关键帧动画
/* 弹跳动画 */
@keyframes bounce {
0%, 20%, 53%, 80%, 100% {
transform: translate3d(0, 0, 0);
}
40%, 43% {
transform: translate3d(0, -30px, 0);
}
70% {
transform: translate3d(0, -15px, 0);
}
90% {
transform: translate3d(0, -4px, 0);
}
}
.bounce {
animation: bounce 1s ease-in-out;
}
/* 淡入动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease-out;
}
/* 脉冲动画 */
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
.pulse {
animation: pulse 2s infinite;
}
性能优化
/* 只使用 transform 和 opacity 进行动画 */
.optimized-animation {
will-change: transform, opacity;
transform: translateZ(0); /* 强制硬件加速 */
}
/* 避免触发布局和绘制的属性 */
.good-animation {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.bad-animation {
transition: width 0.3s ease, height 0.3s ease; /* 会触发布局 */
}
🏛 CSS 架构
BEM 命名规范
BEM(Block Element Modifier)是一种 CSS 命名方法论:
/* Block(块) */
.button { }
/* Element(元素) */
.button__text { }
.button__icon { }
/* Modifier(修饰符) */
.button--primary { }
.button--large { }
.button--disabled { }
/* 组合使用 */
.button--primary.button--large { }
<!-- BEM 在 HTML 中的应用 -->
<button class="button button--primary button--large">
<span class="button__icon">→</span>
<span class="button__text">Submit</span>
</button>
ITCSS 分层架构
ITCSS(Inverted Triangle CSS)是一种可扩展的 CSS 架构:
/* 1. Settings - 全局变量 */
@import 'settings/variables';
@import 'settings/colors';
/* 2. Tools - 混入和函数 */
@import 'tools/mixins';
@import 'tools/functions';
/* 3. Generic - 重置和标准化 */
@import 'generic/reset';
@import 'generic/normalize';
/* 4. Elements - 基础元素样式 */
@import 'elements/headings';
@import 'elements/forms';
/* 5. Objects - 布局对象 */
@import 'objects/container';
@import 'objects/grid';
/* 6. Components - UI 组件 */
@import 'components/button';
@import 'components/card';
/* 7. Utilities - 工具类 */
@import 'utilities/spacing';
@import 'utilities/display';
现代 CSS 架构实践
/* 1. 设计令牌层 */
:root {
/* 颜色令牌 */
--color-primary-50: #e8f5e8;
--color-primary-500: #42b883;
--color-primary-900: #1a5f3f;
/* 间距令牌 */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
/* 字体令牌 */
--font-family-sans: system-ui, -apple-system, sans-serif;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
}
/* 2. 组件层 */
.btn {
/* 基础样式 */
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-2) var(--space-4);
border: 1px solid transparent;
border-radius: 0.375rem;
font-family: var(--font-family-sans);
font-size: var(--font-size-base);
font-weight: 500;
text-decoration: none;
cursor: pointer;
transition: all 0.2s ease;
/* 变体 */
&--primary {
background-color: var(--color-primary-500);
color: white;
&:hover {
background-color: var(--color-primary-600);
}
}
&--secondary {
background-color: transparent;
color: var(--color-primary-500);
border-color: var(--color-primary-500);
&:hover {
background-color: var(--color-primary-50);
}
}
/* 尺寸 */
&--sm {
padding: var(--space-1) var(--space-3);
font-size: var(--font-size-sm);
}
&--lg {
padding: var(--space-3) var(--space-6);
font-size: var(--font-size-lg);
}
}
/* 3. 工具层 */
.u-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.u-text-center { text-align: center; }
.u-text-left { text-align: left; }
.u-text-right { text-align: right; }
🧪 实战练习
练习1:响应式卡片组件
创建一个使用现代 CSS 特性的响应式卡片组件:
<!-- Card.vue -->
<template>
<article class="card" :class="cardClass">
<div class="card__image">
<img :src="image" :alt="title" />
</div>
<div class="card__content">
<h3 class="card__title">{{ title }}</h3>
<p class="card__description">{{ description }}</p>
<div class="card__actions">
<button class="btn btn--primary">Read More</button>
</div>
</div>
</article>
</template>
<script setup lang="ts">
interface Props {
title: string
description: string
image: string
variant?: 'default' | 'featured'
}
const props = withDefaults(defineProps<Props>(), {
variant: 'default'
})
const cardClass = computed(() => ({
'card--featured': props.variant === 'featured'
}))
</script>
<style scoped>
.card {
container-type: inline-size;
display: flex;
flex-direction: column;
background: white;
border-radius: var(--radius-lg);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.card__image {
position: relative;
aspect-ratio: 16 / 9;
overflow: hidden;
}
.card__image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.card:has(.card__image:hover) img {
transform: scale(1.05);
}
.card__content {
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-3);
flex: 1;
}
.card__title {
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--color-gray-900);
margin: 0;
}
.card__description {
color: var(--color-gray-600);
line-height: 1.6;
margin: 0;
flex: 1;
}
.card__actions {
margin-top: auto;
}
/* 容器查询 */
@container (min-width: 300px) {
.card {
flex-direction: row;
}
.card__image {
flex: 0 0 200px;
aspect-ratio: 1;
}
}
@container (min-width: 500px) {
.card__content {
padding: var(--space-6);
}
.card__title {
font-size: var(--font-size-xl);
}
}
/* 特色卡片 */
.card--featured {
border: 2px solid var(--color-primary-500);
}
.card--featured .card__title {
color: var(--color-primary-500);
}
</style>
练习2:现代表单组件
创建一个使用现代 CSS 特性的表单组件:
<!-- FormField.vue -->
<template>
<div class="field" :class="fieldClass">
<label v-if="label" :for="id" class="field__label">
{{ label }}
<span v-if="required" class="field__required">*</span>
</label>
<div class="field__input-wrapper">
<input
:id="id"
:type="type"
:value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:required="required"
class="field__input"
@input="handleInput"
@blur="handleBlur"
@focus="handleFocus"
/>
<div v-if="icon" class="field__icon">
<component :is="icon" />
</div>
</div>
<div v-if="error" class="field__error">
{{ error }}
</div>
<div v-if="hint" class="field__hint">
{{ hint }}
</div>
</div>
</template>
<script setup lang="ts">
interface Props {
modelValue: string
type?: 'text' | 'email' | 'password' | 'number'
label?: string
placeholder?: string
hint?: string
error?: string
icon?: any
required?: boolean
disabled?: boolean
}
interface Emits {
(e: 'update:modelValue', value: string): void
(e: 'blur'): void
(e: 'focus'): void
}
const props = withDefaults(defineProps<Props>(), {
type: 'text'
})
const emit = defineEmits<Emits>()
const id = `field-${Math.random().toString(36).substr(2, 9)}`
const fieldClass = computed(() => ({
'field--error': !!props.error,
'field--disabled': props.disabled,
'field--required': props.required
}))
const handleInput = (event: Event) => {
const target = event.target as HTMLInputElement
emit('update:modelValue', target.value)
}
const handleBlur = () => emit('blur')
const handleFocus = () => emit('focus')
</script>
<style scoped>
.field {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.field__label {
font-weight: 500;
color: var(--color-gray-700);
font-size: var(--font-size-sm);
}
.field__required {
color: var(--color-error-500);
}
.field__input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.field__input {
width: 100%;
padding: var(--space-3);
padding-right: var(--space-10);
border: 1px solid var(--color-gray-300);
border-radius: var(--radius-md);
font-size: var(--font-size-base);
transition: all 0.2s ease;
background: white;
}
.field__input:focus {
outline: none;
border-color: var(--color-primary-500);
box-shadow: 0 0 0 3px rgba(66, 184, 131, 0.1);
}
.field__input::placeholder {
color: var(--color-gray-400);
}
.field__icon {
position: absolute;
right: var(--space-3);
color: var(--color-gray-400);
pointer-events: none;
}
.field__error {
color: var(--color-error-500);
font-size: var(--font-size-sm);
}
.field__hint {
color: var(--color-gray-500);
font-size: var(--font-size-sm);
}
/* 状态样式 */
.field--error .field__input {
border-color: var(--color-error-500);
}
.field--error .field__input:focus {
border-color: var(--color-error-500);
box-shadow: 0 0 0 3px rgba(244, 67, 54, 0.1);
}
.field--disabled .field__input {
background-color: var(--color-gray-50);
color: var(--color-gray-500);
cursor: not-allowed;
}
/* 使用 :has() 选择器 */
.field:has(.field__input:focus) .field__label {
color: var(--color-primary-500);
}
.field:has(.field__input:invalid) .field__input {
border-color: var(--color-error-500);
}
</style>
练习3:响应式网格系统
创建一个现代的响应式网格系统:
/* Grid System */
.grid {
display: grid;
gap: var(--space-4);
grid-template-columns: repeat(12, 1fr);
}
/* 响应式断点 */
@media (max-width: 640px) {
.grid {
grid-template-columns: 1fr;
gap: var(--space-3);
}
}
@media (min-width: 641px) and (max-width: 1024px) {
.grid {
grid-template-columns: repeat(6, 1fr);
}
}
/* 网格项 */
.col {
grid-column: span 12;
}
.col-1 { grid-column: span 1; }
.col-2 { grid-column: span 2; }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-9 { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-11 { grid-column: span 11; }
.col-12 { grid-column: span 12; }
/* 响应式列 */
@media (max-width: 640px) {
.col-sm-1 { grid-column: span 1; }
.col-sm-2 { grid-column: span 2; }
.col-sm-3 { grid-column: span 3; }
.col-sm-4 { grid-column: span 4; }
.col-sm-6 { grid-column: span 6; }
.col-sm-12 { grid-column: span 12; }
}
@media (min-width: 641px) and (max-width: 1024px) {
.col-md-1 { grid-column: span 1; }
.col-md-2 { grid-column: span 2; }
.col-md-3 { grid-column: span 3; }
.col-md-4 { grid-column: span 4; }
.col-md-6 { grid-column: span 6; }
.col-md-12 { grid-column: span 12; }
}
@media (min-width: 1025px) {
.col-lg-1 { grid-column: span 1; }
.col-lg-2 { grid-column: span 2; }
.col-lg-3 { grid-column: span 3; }
.col-lg-4 { grid-column: span 4; }
.col-lg-6 { grid-column: span 6; }
.col-lg-8 { grid-column: span 8; }
.col-lg-9 { grid-column: span 9; }
.col-lg-12 { grid-column: span 12; }
}
📚 最佳实践
1. 性能优化
/* 使用 transform 和 opacity 进行动画 */
.good-animation {
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* 避免触发布局的属性 */
.bad-animation {
transition: width 0.3s ease, height 0.3s ease;
}
/* 使用 will-change 提示浏览器 */
.optimized {
will-change: transform;
}
/* 动画结束后移除 will-change */
.optimized.animation-complete {
will-change: auto;
}
2. 可维护性
/* 使用 CSS 变量统一管理 */
:root {
--component-padding: var(--space-4);
--component-radius: var(--radius-md);
--component-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* 组件样式模块化 */
.card {
padding: var(--component-padding);
border-radius: var(--component-radius);
box-shadow: var(--component-shadow);
}
3. 可访问性
/* 焦点样式 */
.focusable:focus {
outline: 2px solid var(--color-primary-500);
outline-offset: 2px;
}
/* 减少动画(用户偏好) */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* 高对比度支持 */
@media (prefers-contrast: high) {
.button {
border: 2px solid currentColor;
}
}
🚨 常见问题
1. Flexbox 布局问题
问题:子元素不按预期排列 解决:检查 flex-direction、justify-content、align-items 设置
2. Grid 布局问题
问题:网格项位置不正确 解决:确认 grid-template-columns 和 grid-column 设置
3. CSS 变量不生效
问题:变量值没有应用 解决:检查变量名拼写和作用域
📖 延伸阅读
下一章预告:我们将深入 Vue3 的核心原理,包括响应式系统的源码解析、模板编译机制、虚拟 DOM 的 diff 算法等,为理解 Vue 的内部工作原理打下坚实基础。