导读:本文系统讲解 Bootstrap 3.4.1 框架的核心机制与应用实战,涵盖响应式栅格系统、全局样式规范、常用组件、JavaScript 插件及主题定制。每个知识点均配完整可运行示例,并引用 Bootstrap 3 官方文档、响应式设计原理、栅格系统设计模式 等权威资料。掌握 Bootstrap 将让你快速构建响应式、移动优先的现代 Web 应用。
目录
- 零、导读与学习价值
- [0.1 配套示例覆盖清单](#0.1 配套示例覆盖清单)
- [0.2 核心名词速查](#0.2 核心名词速查)
- [0.3 为什么要学本篇](#0.3 为什么要学本篇)
- [0.4 前置知识](#0.4 前置知识)
- [0.5 建议练习路线](#0.5 建议练习路线)
- [0.6 延伸练习与实践方向](#0.6 延伸练习与实践方向)
- [一、Bootstrap 快速入门与基本使用](#一、Bootstrap 快速入门与基本使用)
- 二、响应式栅格系统
- 三、全局样式规范
- [四、Bootstrap 组件库](#四、Bootstrap 组件库)
- [五、JavaScript 插件与交互](#五、JavaScript 插件与交互)
- 六、主题定制与自定义栅格
- 总结
零、导读与学习价值
0.1 配套示例覆盖清单
本文完整覆盖以下六个渐进式实战模块的知识点与实现要点:
| 序号 | 主题 | 核心技能 |
|---|---|---|
| 1 | 基本使用 | 引入 Bootstrap、container、按钮、警告框 |
| 2 | 栅格系统 | .row、.col--、12列布局、列偏移 |
| 3 | 响应式栅格 | xs/sm/md/lg 断点、媒体查询 |
| 4 | 全局样式 | 表格、表单、按钮、辅助类、响应式工具 |
| 5 | 组件 | 导航条、下拉菜单、巨幕、路径导航、分页 |
| 6 | 插件与定制 | 模态框、轮播图、Less 定制、自定义栅格 |
本文每章均配「入门 + 实战」两个可独立运行的完整示例,覆盖从单组件到综合页面的全部用法。
0.2 核心名词速查
| 术语 | 一句话解释 |
|---|---|
| Bootstrap | Twitter 开源的响应式前端框架,提供栅格系统、样式组件、JS 插件 |
| 栅格系统 | 将容器分为 12 列的布局系统,通过 .col-*-* 控制元素占比 |
| 断点(Breakpoint) | 响应式分割点,xs (<768px)、sm (≥768px)、md (≥992px)、lg (≥1200px) |
.container |
Bootstrap 的居中容器,随断点变化宽度 |
| 列偏移 | .col-*-offset-* 向右偏移指定列数 |
| Glyphicons | Bootstrap 内置的字体图标库 |
| 模态框 | Bootstrap 的弹出层组件,支持远程内容加载 |
| 轮播图 | Bootstrap 的图片轮播组件,支持自动播放和触控 |
0.3 为什么要学本篇
- 工程价值:Bootstrap 是全球最受欢迎的前端框架之一,GitHub Stars 超 160k,用于数百万网站。
- 快速开发:提供开箱即用的样式和组件,大幅减少 CSS 编写量,专注于业务逻辑。
- 响应式能力:移动优先(Mobile First)的栅格系统,一套代码适配所有设备。
- 生态成熟:丰富的第三方主题(Bootswatch)、插件、模板社区。
0.4 前置知识
阅读本文前,建议已掌握:
- HTML5 语义化标签(
<nav>、<header>、<section>等) - CSS3 盒模型、Flexbox 布局、媒体查询(
@media) - JavaScript DOM 操作、事件处理、jQuery 基础(Bootstrap 依赖 jQuery)
0.5 建议练习路线
#mermaid-svg-DzYBoOQvr092xUy4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DzYBoOQvr092xUy4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DzYBoOQvr092xUy4 .error-icon{fill:#552222;}#mermaid-svg-DzYBoOQvr092xUy4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DzYBoOQvr092xUy4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DzYBoOQvr092xUy4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DzYBoOQvr092xUy4 .marker.cross{stroke:#333333;}#mermaid-svg-DzYBoOQvr092xUy4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DzYBoOQvr092xUy4 p{margin:0;}#mermaid-svg-DzYBoOQvr092xUy4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DzYBoOQvr092xUy4 .cluster-label text{fill:#333;}#mermaid-svg-DzYBoOQvr092xUy4 .cluster-label span{color:#333;}#mermaid-svg-DzYBoOQvr092xUy4 .cluster-label span p{background-color:transparent;}#mermaid-svg-DzYBoOQvr092xUy4 .label text,#mermaid-svg-DzYBoOQvr092xUy4 span{fill:#333;color:#333;}#mermaid-svg-DzYBoOQvr092xUy4 .node rect,#mermaid-svg-DzYBoOQvr092xUy4 .node circle,#mermaid-svg-DzYBoOQvr092xUy4 .node ellipse,#mermaid-svg-DzYBoOQvr092xUy4 .node polygon,#mermaid-svg-DzYBoOQvr092xUy4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DzYBoOQvr092xUy4 .rough-node .label text,#mermaid-svg-DzYBoOQvr092xUy4 .node .label text,#mermaid-svg-DzYBoOQvr092xUy4 .image-shape .label,#mermaid-svg-DzYBoOQvr092xUy4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DzYBoOQvr092xUy4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DzYBoOQvr092xUy4 .rough-node .label,#mermaid-svg-DzYBoOQvr092xUy4 .node .label,#mermaid-svg-DzYBoOQvr092xUy4 .image-shape .label,#mermaid-svg-DzYBoOQvr092xUy4 .icon-shape .label{text-align:center;}#mermaid-svg-DzYBoOQvr092xUy4 .node.clickable{cursor:pointer;}#mermaid-svg-DzYBoOQvr092xUy4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DzYBoOQvr092xUy4 .arrowheadPath{fill:#333333;}#mermaid-svg-DzYBoOQvr092xUy4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DzYBoOQvr092xUy4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DzYBoOQvr092xUy4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DzYBoOQvr092xUy4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DzYBoOQvr092xUy4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DzYBoOQvr092xUy4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DzYBoOQvr092xUy4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DzYBoOQvr092xUy4 .cluster text{fill:#333;}#mermaid-svg-DzYBoOQvr092xUy4 .cluster span{color:#333;}#mermaid-svg-DzYBoOQvr092xUy4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DzYBoOQvr092xUy4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DzYBoOQvr092xUy4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DzYBoOQvr092xUy4 .icon-shape,#mermaid-svg-DzYBoOQvr092xUy4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DzYBoOQvr092xUy4 .icon-shape p,#mermaid-svg-DzYBoOQvr092xUy4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DzYBoOQvr092xUy4 .icon-shape .label rect,#mermaid-svg-DzYBoOQvr092xUy4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DzYBoOQvr092xUy4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DzYBoOQvr092xUy4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DzYBoOQvr092xUy4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 01 基本使用
02 栅格系统
03 响应式栅格
04 全局样式
05 组件
06 插件与定制
【代码注释】学习路线图:从 Bootstrap 基本使用开始,逐步掌握栅格系统、全局样式、组件库、JavaScript 插件,最后实现主题定制。每步都基于前一步知识,形成完整的 Bootstrap 技能体系。建议按顺序实践配套示例,边学边用。
| 阶段 | 对应章节 | 本步验收标准 |
|---|---|---|
| 快速上手 | 第一章 | 能引入 Bootstrap 并使用按钮、警告框 |
| 布局核心 | 第二章 | 掌握 12 列栅格和断点响应 |
| 样式组件 | 第三、四章 | 能使用表格、表单、导航条等组件 |
| 交互定制 | 第五、六章 | 能使用模态框、轮播图,会用 Less 定制 |
0.6 延伸练习与实践方向
学完本文后,建议用下面四个延伸练习把知识点串成完整能力:
| 练习项 | 对应能力 | 建议做法 |
|---|---|---|
| 综合页面搭建 | 综合布局 + 组件 + 插件 | 搭一个含导航条、栅格、轮播、表单的完整页面,再整体替换品牌色 |
| 阅读官方文档 | API 查表能力 | v3.bootcss.com 按组件查 class |
| 定制与主题 | customize + Bootswatch | 用在线定制工具生成 CSS,或切换 Darkly 等主题 |
| 手写 12 列栅格 | 理解 .row / .col-* 原理 |
见第六章,用 Flex 或 float 复现 12 等分 |
【代码注释】延伸练习重在动手而非背 API------核心目标是「能改品牌色、能换主题、理解栅格是 12 等分」。手写栅格可用 Flex 版(display:flex; flex-wrap:wrap)或 float 版(贴近 Bootstrap 3 源码思路)。
常见笔误提醒: 初学者常把列类名误写成 com-md-8,正确类名为 col-md-8 (col 不是 com)。写错后栅格完全不生效,DevTools 中看不到列宽百分比。
一、Bootstrap 快速入门与基本使用
名词解释
- CDN:内容分发网络,Bootstrap 提供 CDN 链接用于快速引入
- 预编译版:编译后的 CSS 和 JS 文件,生产环境使用
- 源码版:包含 Less 源文件和文档,开发环境使用
- 视口元标签 :
<meta name="viewport" content="width=device-width, initial-scale=1">确保移动端正确渲染
概念与底层原理
Bootstrap 3.4.1 是一个基于 移动优先 理念的前端框架,其核心架构包括:
1. 文件结构
text
bootstrap/
├── dist/
│ ├── css/
│ │ ├── bootstrap.css # 完整版 CSS(开发用)
│ │ └── bootstrap.min.css # 压缩版 CSS(生产用)
│ ├── js/
│ │ ├── bootstrap.js # 完整版 JS(开发用)
│ │ └── bootstrap.min.js # 压缩版 JS(生产用)
│ └── fonts/ # Glyphicons 字体图标
└── less/ # Less 源文件
【代码注释】Bootstrap 3.4.1 文件结构:dist/ 包含编译后的 CSS 和 JS,bootstrap.css 约 140KB(未压缩),bootstrap.min.css 约 120KB(压缩后)。fonts/ 包含 Glyphicons 字体图标文件。less/ 源文件用于定制主题。市面应用:开发环境用完整版(便于调试),生产环境用压缩版(减少加载时间)。
根据 Bootstrap 官方文档,预编译版提供即用型 CSS 和 JS,而源码版允许通过 Less 定制主题。
2. 引入方式
html
<!-- 方式1:本地引入 -->
<link rel="stylesheet" href="./dist/css/bootstrap.min.css">
<script src="./dist/jquery/jquery-1.12.3.min.js"></script>
<script src="./dist/js/bootstrap.min.js"></script>
<!-- 方式2:CDN 引入 -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
【代码注释】Bootstrap 引入两种方式:本地引入(./dist/ 相对路径)适合离线开发,CDN 引入(bootcdn.net)利用缓存加速。关键是引入顺序 :viewport 元标签 → CSS → jQuery → Bootstrap JS,jQuery 必须在 Bootstrap JS 之前。市面应用:本地开发用本地文件,生产环境用 CDN + 本地回退。
注意:Bootstrap 的 JavaScript 插件依赖 jQuery ,必须在 Bootstrap JS 之前引入。根据 jQuery 兼容性表,Bootstrap 3 支持 jQuery 1.9.1 - 3.x 版本。
3. 核心设计原则
- 移动优先:从最小屏幕设计,逐步增强到大屏幕
- 语义化:使用 HTML5 语义标签,降低 CSS 选择器权重
- 组件化:样式和结构分离,通过类名组合实现组件
深入:Bootstrap CSS 的分层架构与低权重设计
Bootstrap 的 CSS 不是一锅样式,而是有清晰的分层,自底向上叠加:
- Normalize.css :Bootstrap 3 内置了 Normalize.css,它只抹平浏览器默认样式的差异 (如各浏览器对
<button>、<sub>的默认值不一致),而非把所有样式清零------这与传统 CSS Reset 不同。 - scaffolding(基架) :设定全局
box-sizing: border-box、<html>字号 10px、<body>字号 14px 等基线。 - 基础排版与元素:标题、段落、列表、表格、表单、按钮等 HTML 元素的统一外观。
- 组件层:导航条、下拉菜单、巨幕等由多个 class 组合成的复合组件。
- 工具类层 :
.text-center、.pull-left、.hidden-xs等单一职责的辅助类。
一个贯穿全框架的设计是几乎只用 class 选择器 (如 .btn、.col-md-6),极少用 ID 或标签选择器。原因在 CSS 优先级:class 选择器权重低,你自己写的 .my-btn 或更具体的选择器很容易覆盖它。这就是「为什么 Bootstrap 容易定制」的底层答案------它故意把权重压到最低。
深入:Data API------用 HTML 属性声明行为
注意入门示例里没有一行自己写的 JavaScript,按钮、警告框却能交互。这归功于 Bootstrap 的 Data API :bootstrap.js 在页面加载时,用 jQuery 在 document 上事件委托 监听点击等事件,凡是带 data-toggle="..." 的元素被点击,插件就自动执行对应行为。开发者只需在 HTML 上写声明式属性(data-toggle、data-target、data-dismiss),无需手动 $(...).modal()。这种「行为与结构就近声明」的设计,也是后来 Vue 指令(v-on)等思路的早期形态。
深入:.container 与 container-fluid 的区别
两个容器类常被混用:.container 有一个随断点跳变的固定 max-width (xs 时 100%,sm 750px,md 970px,lg 1170px),所以在大屏上内容两侧留白、保持可读宽度;.container-fluid 则永远 100% 宽 ,随视口连续伸缩。规则:需要内容居中、有最大宽度时用 .container;需要铺满(如导航条、页脚、全宽 Banner)时用 .container-fluid。
mermaid 图:Bootstrap 渲染流程
#mermaid-svg-qgexJDdDaPuvk2mv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qgexJDdDaPuvk2mv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qgexJDdDaPuvk2mv .error-icon{fill:#552222;}#mermaid-svg-qgexJDdDaPuvk2mv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qgexJDdDaPuvk2mv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qgexJDdDaPuvk2mv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qgexJDdDaPuvk2mv .marker.cross{stroke:#333333;}#mermaid-svg-qgexJDdDaPuvk2mv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qgexJDdDaPuvk2mv p{margin:0;}#mermaid-svg-qgexJDdDaPuvk2mv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qgexJDdDaPuvk2mv .cluster-label text{fill:#333;}#mermaid-svg-qgexJDdDaPuvk2mv .cluster-label span{color:#333;}#mermaid-svg-qgexJDdDaPuvk2mv .cluster-label span p{background-color:transparent;}#mermaid-svg-qgexJDdDaPuvk2mv .label text,#mermaid-svg-qgexJDdDaPuvk2mv span{fill:#333;color:#333;}#mermaid-svg-qgexJDdDaPuvk2mv .node rect,#mermaid-svg-qgexJDdDaPuvk2mv .node circle,#mermaid-svg-qgexJDdDaPuvk2mv .node ellipse,#mermaid-svg-qgexJDdDaPuvk2mv .node polygon,#mermaid-svg-qgexJDdDaPuvk2mv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qgexJDdDaPuvk2mv .rough-node .label text,#mermaid-svg-qgexJDdDaPuvk2mv .node .label text,#mermaid-svg-qgexJDdDaPuvk2mv .image-shape .label,#mermaid-svg-qgexJDdDaPuvk2mv .icon-shape .label{text-anchor:middle;}#mermaid-svg-qgexJDdDaPuvk2mv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qgexJDdDaPuvk2mv .rough-node .label,#mermaid-svg-qgexJDdDaPuvk2mv .node .label,#mermaid-svg-qgexJDdDaPuvk2mv .image-shape .label,#mermaid-svg-qgexJDdDaPuvk2mv .icon-shape .label{text-align:center;}#mermaid-svg-qgexJDdDaPuvk2mv .node.clickable{cursor:pointer;}#mermaid-svg-qgexJDdDaPuvk2mv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qgexJDdDaPuvk2mv .arrowheadPath{fill:#333333;}#mermaid-svg-qgexJDdDaPuvk2mv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qgexJDdDaPuvk2mv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qgexJDdDaPuvk2mv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qgexJDdDaPuvk2mv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qgexJDdDaPuvk2mv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qgexJDdDaPuvk2mv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qgexJDdDaPuvk2mv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qgexJDdDaPuvk2mv .cluster text{fill:#333;}#mermaid-svg-qgexJDdDaPuvk2mv .cluster span{color:#333;}#mermaid-svg-qgexJDdDaPuvk2mv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qgexJDdDaPuvk2mv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qgexJDdDaPuvk2mv rect.text{fill:none;stroke-width:0;}#mermaid-svg-qgexJDdDaPuvk2mv .icon-shape,#mermaid-svg-qgexJDdDaPuvk2mv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qgexJDdDaPuvk2mv .icon-shape p,#mermaid-svg-qgexJDdDaPuvk2mv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qgexJDdDaPuvk2mv .icon-shape .label rect,#mermaid-svg-qgexJDdDaPuvk2mv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qgexJDdDaPuvk2mv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qgexJDdDaPuvk2mv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qgexJDdDaPuvk2mv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTML 文件
加载 bootstrap.css
加载 jQuery.js
加载 bootstrap.js
应用全局样式重置
初始化 JS 插件
渲染组件样式
绑定交互事件
浏览器渲染最终页面
【代码注释】流程图展示 Bootstrap 加载与渲染流程:CSS 在 <head> 中优先加载,JS 在 <body> 底部延迟加载。container 类居中内容,.row 创建栅格行,.col-*-* 定义列宽。理解此流程有助于排查样式不生效问题(jQuery 未加载、引入顺序错误)。
可运行示例(入门):Bootstrap 基本结构
将下面内容保存为 demo-bootstrap-basic.html,放在本文所在目录后双击打开:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 关键:视口元标签,确保移动端正确缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 基本使用</title>
<!-- 引入 Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<!-- .container 居中容器,随断点变化宽度 -->
<div class="container">
<!-- .page-header 页面标题组件 -->
<div class="page-header">
<h1>Bootstrap <small>基本使用示例</small></h1>
</div>
<!-- 按钮组 -->
<div class="btn-toolbar" role="toolbar">
<button class="btn btn-default">默认按钮</button>
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-success">成功按钮</button>
<button class="btn btn-info">信息按钮</button>
<button class="btn btn-warning">警告按钮</button>
<button class="btn btn-danger">危险按钮</button>
<button class="btn btn-link">链接按钮</button>
</div>
<hr>
<!-- 警告框 -->
<div class="alert alert-success" role="alert">
<strong>成功!</strong> 这是一条成功消息。
</div>
<div class="alert alert-info" role="alert">
<strong>提示!</strong> 这是一条信息消息。
</div>
<div class="alert alert-warning" role="alert">
<strong>警告!</strong> 这是一条警告消息。
</div>
<div class="alert alert-danger" role="alert">
<strong>错误!</strong> 这是一条错误消息。
</div>
</div>
<!-- jQuery (Bootstrap 的 JS 插件依赖 jQuery) -->
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<!-- Bootstrap JS -->
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】<meta name="viewport"> 是移动端渲染的关键,width=device-width 让视口宽度等于设备宽度,initial-scale=1 设置初始缩放比例为 1。.container 在不同断点下宽度为 100%(xs)、750px(sm)、970px(md)、1170px(lg)。市面应用:企业官网、后台管理系统、着陆页。
可运行示例(实战):响应式登录页面
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>用户登录</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
padding: 20px 0;
}
.login-box {
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
padding: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<!-- 响应式布局:xs 占满 12 列,md 及以上占 6 列并居中 -->
<div class="col-xs-12 col-md-6 col-md-offset-3">
<div class="login-box">
<div class="text-center">
<h2>用户登录</h2>
<p class="text-muted">欢迎使用 Bootstrap 登录系统</p>
</div>
<hr>
<form>
<!-- .form-group 表单组,提供底部间距 -->
<div class="form-group">
<label for="username">用户名</label>
<!-- .form-control 表单控件,宽度 100% + 样式优化 -->
<input type="text" class="form-control" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" placeholder="请输入密码">
</div>
<div class="checkbox">
<label>
<input type="checkbox"> 记住我
</label>
</div>
<button type="submit" class="btn btn-primary btn-block">登录</button>
</form>
<hr>
<div class="text-center">
<p class="text-muted">还没有账号? <a href="#">立即注册</a></p>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示响应式登录页:.col-xs-12 在移动端占满容器,.col-md-6 在桌面端占 50%,.col-md-offset-3 向右偏移 3 列(25%)实现水平居中。.form-group 提供表单元素间距,.form-control 赋予输入框 100% 宽度和统一样式。市面应用:各类网站登录/注册页面、管理后台登录。
【实战要点】
- 经典应用场景:企业官网、后台管理系统(如 GitLab 使用 Bootstrap)、电商网站后台、文档站
- 常见坑 :忘记引入
viewport元标签导致移动端显示异常;jQuery 版本不兼容(需 1.9.1+);本地引入时路径错误(dist/目录结构) - 性能与最佳实践 :生产环境使用压缩版(
.min.css/.min.js);用 CDN 加速静态资源加载;按需引入组件(bootstrap.css完整版约 140KB);考虑自定义构建剔除未使用的样式
【本章小结】
| 要点 | 说明 |
|---|---|
| 引入 | viewport 元标签、CSS、jQuery、Bootstrap JS |
| 容器 | .container 居中,.container-fluid 全宽 |
| 按钮 | .btn + 语义类(.btn-default/.btn-primary 等) |
| 警告框 | .alert + 语义类(.alert-success/.alert-info 等) |
记忆口诀:"viewport 别忘,jQuery 先行,容器居中,类名语义"
【面试考点】
Q1:Bootstrap 3 与 Bootstrap 4/5 的主要区别?
A:Bootstrap 3 支持 IE8,依赖 jQuery 1.x,使用 Less;Bootstrap 4/5 不支持 IE11 以下,支持 jQuery 3.x/无 jQuery,使用 Sass,新增栅格层(.col-xl-*)、Flexbox 工具类、卡片组件等。追问「迁移建议」时答:新项目直接用 Bootstrap 5,老项目考虑渐进升级或继续用 Bootstrap 3。
Q2:为什么 Bootstrap 的 JS 插件依赖 jQuery?
A:历史原因:Bootstrap 3 开发时(2013-2016)jQuery 是 DOM 操作的主流库。Bootstrap 插件大量使用 jQuery 的选择器、事件系统、动画方法。Bootstrap 5 通过原生 JS 重写了所有插件,不再依赖 jQuery,但 Bootstrap 3 必须引入 jQuery。
Q3:viewport 元标签的作用是什么?
A:<meta name="viewport" content="width=device-width, initial-scale=1"> 告诉浏览器:「视口宽度等于设备宽度,初始缩放比例为 1」。这确保移动端按 1:1 渲染,用户可以缩放但不会自动缩放到很小的尺寸。缺少此标签时,移动浏览器会模拟桌面端视口(通常 980px),导致页面显示极小。根据 Viewport Meta Tag 规范,这是响应式设计的基础。
二、响应式栅格系统
名词解释
- 栅格系统(Grid System):将容器分为 12 列的布局系统
- 行(
.row) :创建列的水平组,提供负边距抵消容器的padding - 列(
.col-*-*):水平排列的元素,通过类名指定占 12 列中的份数 - 断点(Breakpoint):响应式分割点,xs/sm/md/lg
- 列偏移(
.offset-*):向右偏移指定列数 - 列推拉(
.push-*/.pull-*):改变列的视觉顺序(不影响 DOM 顺序)
概念与底层原理
Bootstrap 栅格系统是移动优先的响应式布局解决方案,其核心特性包括:
1. 12 列布局模型
html
<div class="row">
<div class="col-md-4">占 4 列(1/3)</div>
<div class="col-md-8">占 8 列(2/3)</div>
</div>
【代码注释】列必须放在 .row 内;col-md-4 表示在 md 及以上 视口占 4/12 宽,xs 端若未写 col-xs-* 可能堆叠为 100% 宽。一行合计 12 份,超出自动换行。
根据 Bootstrap 3 栅格文档,栅格系统通过 max-width 媒体查询实现:xs 断点默认生效(无媒体查询),sm/md/lg 断点分别有 min-width 媒体查询。每行列数之和应为 12,超过 12 会换行。
2. 响应式断点
| 断点 | 视口宽度 | .container 宽度 |
列类名 | 媒体查询 |
|---|---|---|---|---|
| xs | < 768px | 100% | .col-xs-* |
无(默认) |
| sm | ≥ 768px | 750px | .col-sm-* |
@media (min-width: 768px) |
| md | ≥ 992px | 970px | .col-md-* |
@media (min-width: 992px) |
| lg | ≥ 1200px | 1170px | .col-lg-* |
@media (min-width: 1200px) |
断点参考 Bootstrap 断点设计,基于常见设备尺寸:手机(xs)、平板竖屏(sm)、平板横屏/小笔记本(md)、桌面(lg)。
3. 列偏移、推拉与嵌套
html
<!-- 列偏移:向右偏移 4 列 -->
<div class="col-md-4 col-md-offset-4">居中内容</div>
<!-- 列推拉:视觉改变顺序 -->
<div class="col-md-4 col-md-push-8">视觉在右,实际在左</div>
<div class="col-md-8 col-md-pull-4">视觉在左,实际在右</div>
<!-- 栅格嵌套:行内再分行 -->
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-6">嵌套列</div>
</div>
</div>
</div>
【代码注释】offset 用 margin-left 右移;push/pull 改视觉顺序不改 DOM,适合「大图在右、文字在左」的 SEO 结构。嵌套时内层再包 .row,列宽百分比相对内层 row 计算。
列偏移原理:.col-md-offset-4 通过 margin-left: 33.3333%(4/12)实现。列推拉通过 position: relative + left/right 实现。嵌套时,内层 .row 的负边距抵消外层列的 padding,内容仍然对齐。
4. 清除浮动
.row 通过 ::after 伪元素清除浮动(.clearfix() 混合),确保容器包裹住浮动列。Flexbox 布局普及后,清除浮动重要性降低,但 Bootstrap 3 基于 float,仍需要此机制。
深入:一列的宽度到底由什么撑出来
.col-md-4 能占「12 列中的 4 列」,靠的是三件套协同:
float: left:让列脱离常规流、横向排列,一行放不下自动换行------这是 Bootstrap 3 在没有 Flexbox 时的排列手段。width: percentage(4/12):宽度是百分比(约 33.33%),相对父级.row计算,所以容器变宽列也跟着变宽。box-sizing: border-box+ 左右padding: 15px:每列左右各有 15px 内边距(即 gutter 的一半),相邻两列之间就形成 30px 的「列间距」。
第 3 点里的 box-sizing: border-box 是整个栅格能成立的隐藏前提 :默认的 content-box 下,width: 33.33% 再加 padding: 15px 会让列实际占宽超过 33.33%,三列必然换行错位。border-box 让 padding 算进 width 之内,百分比才精确。Bootstrap 3 在 * 选择器上全局设了 border-box,正是为此。
深入:移动优先的级联------为什么 col-xs-12 col-md-6 能同时写
一个列上同时挂多个断点 class 并不冲突,靠的是媒体查询的级联顺序:
.col-xs-*没有媒体查询包裹,任何屏幕都生效,是「基础值」。.col-sm-*/.col-md-*/.col-lg-*分别被@media (min-width: 768/992/1200px)包裹,屏幕够宽时才生效。- Bootstrap 源码里这些规则按 xs→sm→md→lg 的顺序书写,权重相同时后出现的覆盖先出现的。
于是 col-xs-12 col-md-6 的含义是:默认(含手机)占满 12 列,一旦视口 ≥992px,col-md-6 的规则生效并覆盖,变成占 6 列。这就是「移动优先」在代码层面的真正含义------基础样式面向小屏,用 min-width 逐级增强 ,而不是用 max-width 逐级削减。
深入:push/pull 与 offset 的实现差异
三者都改变列的水平位置,但机制不同:offset 用 margin-left 把列真实地推开 (占据空间);push/pull 用 position: relative + left/right 做视觉偏移 (不占空间、不影响其他列),所以能实现「DOM 里文字在前、视觉上图片在前」这种对 SEO 与无障碍友好的布局。Bootstrap 4/5 改用 Flexbox 栅格后,push/pull 被更直观的 order 属性取代。
mermaid 图:栅格系统结构
#mermaid-svg-SH4kfEudX6fOPhwQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SH4kfEudX6fOPhwQ .error-icon{fill:#552222;}#mermaid-svg-SH4kfEudX6fOPhwQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SH4kfEudX6fOPhwQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SH4kfEudX6fOPhwQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SH4kfEudX6fOPhwQ .marker.cross{stroke:#333333;}#mermaid-svg-SH4kfEudX6fOPhwQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SH4kfEudX6fOPhwQ p{margin:0;}#mermaid-svg-SH4kfEudX6fOPhwQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster-label text{fill:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster-label span{color:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster-label span p{background-color:transparent;}#mermaid-svg-SH4kfEudX6fOPhwQ .label text,#mermaid-svg-SH4kfEudX6fOPhwQ span{fill:#333;color:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ .node rect,#mermaid-svg-SH4kfEudX6fOPhwQ .node circle,#mermaid-svg-SH4kfEudX6fOPhwQ .node ellipse,#mermaid-svg-SH4kfEudX6fOPhwQ .node polygon,#mermaid-svg-SH4kfEudX6fOPhwQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SH4kfEudX6fOPhwQ .rough-node .label text,#mermaid-svg-SH4kfEudX6fOPhwQ .node .label text,#mermaid-svg-SH4kfEudX6fOPhwQ .image-shape .label,#mermaid-svg-SH4kfEudX6fOPhwQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-SH4kfEudX6fOPhwQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SH4kfEudX6fOPhwQ .rough-node .label,#mermaid-svg-SH4kfEudX6fOPhwQ .node .label,#mermaid-svg-SH4kfEudX6fOPhwQ .image-shape .label,#mermaid-svg-SH4kfEudX6fOPhwQ .icon-shape .label{text-align:center;}#mermaid-svg-SH4kfEudX6fOPhwQ .node.clickable{cursor:pointer;}#mermaid-svg-SH4kfEudX6fOPhwQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SH4kfEudX6fOPhwQ .arrowheadPath{fill:#333333;}#mermaid-svg-SH4kfEudX6fOPhwQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SH4kfEudX6fOPhwQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SH4kfEudX6fOPhwQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SH4kfEudX6fOPhwQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SH4kfEudX6fOPhwQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SH4kfEudX6fOPhwQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster text{fill:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ .cluster span{color:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SH4kfEudX6fOPhwQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SH4kfEudX6fOPhwQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-SH4kfEudX6fOPhwQ .icon-shape,#mermaid-svg-SH4kfEudX6fOPhwQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SH4kfEudX6fOPhwQ .icon-shape p,#mermaid-svg-SH4kfEudX6fOPhwQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SH4kfEudX6fOPhwQ .icon-shape .label rect,#mermaid-svg-SH4kfEudX6fOPhwQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SH4kfEudX6fOPhwQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SH4kfEudX6fOPhwQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SH4kfEudX6fOPhwQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} < 768px
≥ 768px
≥ 992px
≥ 1200px
.container 居中容器
padding 左右 15px
.row 行
margin 左右 -15px
抵消 container padding
.col-*-* 列
float: left
padding 左右 15px
width: 百分比
媒体查询
视口宽度
应用 .col-xs-*
应用 .col-sm-*
应用 .col-md-*
应用 .col-lg-*
【代码注释】流程图展示栅格系统工作原理:.container 的 padding 与 .row 的负 margin 抵消,.row 内的列通过 float 排列,width 百分比计算相对 .row 宽度。媒体查询根据视口宽度选择对应的列类名。理解此机制有助于排查布局错乱问题(嵌套时 .row 宽度受限)。
可运行示例(入门):栅格基础布局
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>栅格系统基础</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
.box {
background: #e1251b;
color: #fff;
padding: 20px;
text-align: center;
border: 1px solid #c81623;
margin-bottom: 10px;
}
.box-sm { background: #f90; border-color: #e0a800; }
.box-md { background: #52c41a; border-color: #389e0d; }
.box-lg { background: #1890ff; border-color: #096dd9; }
</style>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>Bootstrap 栅格系统</h1>
</div>
<!-- 示例1:等宽列(每列占 4 份,3 列 = 12) -->
<h3>示例1:等宽列(3 列均分)</h3>
<div class="row">
<div class="col-md-4">
<div class="box">.col-md-4</div>
</div>
<div class="col-md-4">
<div class="box box-sm">.col-md-4</div>
</div>
<div class="col-md-4">
<div class="box box-md">.col-md-4</div>
</div>
</div>
<!-- 示例2:不等宽列(4 + 8 = 12) -->
<h3>示例2:不等宽列(1:2 比例)</h3>
<div class="row">
<div class="col-md-4">
<div class="box">.col-md-4 (1/3)</div>
</div>
<div class="col-md-8">
<div class="box box-sm">.col-md-8 (2/3)</div>
</div>
</div>
<!-- 示例3:列偏移 -->
<h3>示例3:列偏移居中</h3>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="box box-lg">.col-md-4 .col-md-offset-4</div>
</div>
</div>
<!-- 示例4:列嵌套 -->
<h3>示例4:列嵌套</h3>
<div class="row">
<div class="col-md-6">
<div class="box">
外层 .col-md-6
<div class="row" style="margin-top: 10px;">
<div class="col-md-6">
<div class="box box-sm" style="margin-bottom: 10px;">内层 .col-md-6</div>
</div>
<div class="col-md-6">
<div class="box box-md">内层 .col-md-6</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-lg">外层 .col-md-6</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】.col-md-4 宽度为 4/12(约 33.33%),.col-md-8 为 8/12(约 66.67%)。.col-md-offset-4 通过 margin-left: 33.33% 向右偏移,实现居中(4 + 偏移4 + 4 = 12)。嵌套时内层 .row 宽度相对外层列,内容对齐。市面应用:商品列表(4:8 分栏)、侧边栏布局(8:4 分栏)、仪表盘。
可运行示例(实战):响应式产品展示页
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>响应式产品展示</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
body { padding: 20px 0; }
.product-card {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
text-align: center;
transition: box-shadow 0.3s;
}
.product-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.product-img {
width: 100%;
height: 200px;
background: #f5f5f5;
border-radius: 4px;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
.product-price {
font-size: 20px;
color: #e1251b;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>产品展示 <small>响应式栅格布局</small></h1>
</div>
<!-- 移动端 1 列,平板 2 列,桌面 3 列 -->
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 1</h3>
<p>这是产品 1 的描述信息...</p>
<p class="product-price">¥199</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 2</h3>
<p>这是产品 2 的描述信息...</p>
<p class="product-price">¥299</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 3</h3>
<p>这是产品 3 的描述信息...</p>
<p class="product-price">¥399</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 4</h3>
<p>这是产品 4 的描述信息...</p>
<p class="product-price">¥499</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 5</h3>
<p>这是产品 5 的描述信息...</p>
<p class="product-price">¥599</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="product-card">
<div class="product-img">产品图片</div>
<h3>产品 6</h3>
<p>这是产品 6 的描述信息...</p>
<p class="product-price">¥699</p>
<button class="btn btn-primary btn-block">购买</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示移动优先的响应式布局:.col-xs-12 在移动端占满一行,.col-sm-6 在平板及以上占 50%(2 列),.col-md-4 在桌面及以上占 33.33%(3 列)。随着视口变大,列数自动从 1 → 2 → 3 增加。市面应用:电商商品列表、博客卡片墙、产品展示页。
【实战要点】
- 经典应用场景:响应式导航栏、商品网格、仪表盘布局、博客文章列表
- 常见坑 :列数超过 12 导致换行布局错乱;嵌套时内层
.row未清除浮动;列偏移后总宽度超过 12;断点选择不当(如只在lg设置布局,md以下显示异常) - 性能与最佳实践 :按需加载断点(不需要
lg就不引入.col-lg-*);用col-*-push-*/col-*-pull-*改变视觉顺序而非 DOM 顺序(SEO 友好);嵌套层级不宜超过 3 层;考虑用 Flexbox 替代复杂的栅格嵌套
【本章小结】
| 特性 | 类名 | 说明 |
|---|---|---|
| 容器 | .container |
居中容器,断点自适应宽度 |
| 行 | .row |
创建列的水平组,负边距抵消 padding |
| 列 | .col-*-* |
指定占 12 列的份数 |
| 偏移 | .col-*-offset-* |
向右偏移指定列数 |
| 推拉 | .col-*-push-* / .col-*-pull-* |
改变视觉顺序 |
记忆口诀:"容器居中,行负边距,列十二份,断点响应"
【面试考点】
Q1:Bootstrap 栅格系统如何实现响应式?
A:通过媒体查询和类名前缀实现。.col-xs-* 默认生效(无媒体查询),.col-sm-* 在 @media (min-width: 768px) 生效,.col-md-* 在 @media (min-width: 992px) 生效,.col-lg-* 在 @media (min-width: 1200px) 生效。多个类名可同时存在(如 col-xs-12 col-md-6),在不同断点下取不同的宽度值。
Q2:为什么 .row 有负 margin?
A:.row 的 margin-left/right: -15px 抵消 .container 的 padding-left/right: 15px,让 .row 内的列从容器边缘开始布局(内容仍有 15px padding)。这是 Bootstrap 设计的「负边距 + 正 padding」模式,确保嵌套栅格时内容对齐。理解此机制有助于排查内容溢出问题(外层容器没有 padding 时负边距会溢出)。
Q3:如何实现一行 5 列均分(非整数倍 12)?
A:方案1:用 .col-xs-2(10 列,剩余 2 列)+ .col-xs-offset-* 调整;方案2:自定义列宽(.col-xs-5 { width: 20%; float: left; });方案3:用 Flexbox(<div class="row" style="display: flex;"> <div style="flex: 1;">)。Bootstrap 3 推荐方案1 或自定义 CSS,Bootstrap 4/5 新增 flex-*-row 类简化此场景。
Q4:栅格完全不生效时先查什么?
A:类名是否为 col-*-*(勿写成 com-md);列是否在 .row 内;是否引入 bootstrap.css;自定义样式是否覆盖 width/float。DevTools 中选中列元素应能看到百分比宽度。
三、全局样式规范
名词解释
- 全局样式重置:Bootstrap 对 HTML 元素的默认样式进行统一,抹平浏览器差异
- 排版:字体、字号、行高、段落间距等文字相关样式
- 表格样式 :
.table系列类提供的表格装饰 - 表单样式 :
.form-control等表单控件的统一样式 - 辅助类:文字颜色、背景色、浮动、定位等工具类
概念与底层原理
Bootstrap 通过** Normalize.css**(抹平浏览器差异)+ 自定义样式(组件样式)实现全局样式规范。
1. 排版系统
css
/* Bootstrap 默认字体栈 */
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.42857143; /* 20px / 14px */
【代码注释】Bootstrap 3 在 <html> 设 font-size: 10px 便于用 rem 算标题;正文 14px、行高约 1.43 保证可读性。覆盖字体时改 body 或定制 Less 变量 @font-family-base。
根据 Bootstrap 排版文档,使用 rem 单位设置字号(<html> 字号 10px),<body> 字号 14px。<small> 为父元素字号的 85%,<mark> 高亮背景为 #fcf8e3。
2. 表格样式
| 类名 | 作用 |
|---|---|
.table |
基础表格样式(水平线、内边距) |
.table-striped |
斑马纹(奇偶行不同背景) |
.table-bordered |
边框表格 |
.table-hover |
鼠标悬停高亮 |
.table-condensed |
紧凑表格(减小内边距) |
.active / .success / .info / .warning / .danger |
行或单元格状态色(灰/绿/蓝/黄/红) |
斑马纹通过 tbody tr:nth-child(odd) 实现,悬停通过 tbody tr:hover 实现。根据 CSS 选择器规范,:nth-child() 从 1 开始计数。
3. 表单样式
html
<!-- 基本表单 -->
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" class="form-control" id="email" placeholder="请输入邮箱">
</div>
<!-- 水平表单 -->
<form class="form-horizontal">
<div class="form-group">
<label class="col-md-2 control-label">邮箱</label>
<div class="col-md-10">
<input type="email" class="form-control" placeholder="请输入邮箱">
</div>
</div>
</form>
【代码注释】.form-group 包一组 label + 控件;.form-horizontal 下 .form-group 行为类似 .row,label 用 col-*-* control-label,输入区再套列。按钮可加 .btn-block 占满一行宽度。
.form-control 赋予表单控件 display: block; width: 100%; 样式,确保统一宽度。.form-horizontal 让 .form-group 表现得像 .row,内部可用栅格系统分列。
4. 辅助类
html
<!-- 文字颜色 -->
<p class="text-muted">灰色文本</p>
<p class="text-primary">主色文本</p>
<p class="text-success">成功文本</p>
<p class="text-info">信息文本</p>
<p class="text-warning">警告文本</p>
<p class="text-danger">危险文本</p>
<!-- 背景颜色 -->
<p class="bg-primary">主色背景</p>
<p class="bg-success">成功背景</p>
<p class="bg-info">信息背景</p>
<p class="bg-warning">警告背景</p>
<p class="bg-danger">危险背景</p>
<!-- 响应式工具 -->
<div class="visible-xs-block">只在超小屏显示</div>
<div class="hidden-xs">在超小屏隐藏</div>
【代码注释】visible-*-* / hidden-*-* 通过媒体查询切换 display,用于「手机显示下载 App、桌面隐藏」等。Bootstrap 4+ 改用 d-none d-md-block 等工具类,概念相同。
文字/背景颜色类通过 !important 提升优先级,确保覆盖已有样式。响应式工具类通过 @media 查询的 display: none / display: block 实现显示/隐藏。
深入:Normalize.css 与传统 Reset 的本质区别
全局样式的第一步是「抹平浏览器差异」,有两种流派:
- 传统 CSS Reset (如 Eric Meyer 的 reset):把所有元素的
margin、padding、border、font-size等统统清零 ,再由开发者从零重建。代价是连<h1>、<strong>、<ul>这些有用的语义化默认样式也被抹掉。 - Normalize.css (Bootstrap 3 采用):只针对各浏览器表现不一致的地方做修正 ,保留合理的默认值。例如它统一各浏览器对
<button>字体继承的处理,但不会把<h1>的字号清零。
Bootstrap 3 选 Normalize,因此你在 Bootstrap 页面里写 <strong> 仍然是粗体、<h2> 仍然比 <h3> 大------它修正差异、而非铲平一切。
深入:辅助类为什么敢用 !important
.text-danger、.bg-warning 这类辅助类的源码里带 !important。一般教程都说「少用 !important」,但工具类是个正当例外 :工具类的语义是「我就是要这个效果,别被组件样式盖住」。比如 .text-center 加在某个组件内部元素上,必须强过组件自身的 text-align。现代工具类框架(Tailwind 的 ! 前缀、Bootstrap 5 的工具类)沿用同一思路------单一职责的原子类用 !important 锁定结果,是可控的 ;真正要避免的是在普通组件样式里滥用 !important。
深入:响应式工具类与 .sr-only
.visible-xs-block / .hidden-md 等响应式工具类,底层只是把 display 包进对应断点的媒体查询里切换。还有一个常被忽略的无障碍工具类 .sr-only(screen-reader only):它把元素移出视觉范围但保留在 DOM 中,专供屏幕阅读器朗读------导航条汉堡按钮里的 <span class="sr-only">Toggle navigation</span> 正是它,视觉用户看不到、视障用户能听到。
可运行示例(入门):全局样式综合演示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>全局样式演示</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="page-header">
<h1>Bootstrap 全局样式</h1>
</div>
<!-- 排版 -->
<h2>排版示例</h2>
<p>这是普通段落,<strong>加粗</strong>,<em>斜体</em>,<mark>高亮</mark>,<del>删除线</del>,<u>下划线</u>,<small>小号文本</small>,<cite>引用</cite>。</p>
<blockquote>
<p>这是引用块,用于引用他人的话语或文章段落。</p>
<footer>出自 <cite>某人</cite></footer>
</blockquote>
<hr>
<!-- 表格 -->
<h2>表格示例</h2>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>邮箱</th>
<th>角色</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
<td>zhangsan@example.com</td>
<td><span class="label label-primary">管理员</span></td>
</tr>
<tr>
<td>2</td>
<td>李四</td>
<td>lisi@example.com</td>
<td><span class="label label-success">用户</span></td>
</tr>
<tr>
<td>3</td>
<td>王五</td>
<td>wangwu@example.com</td>
<td><span class="label label-default">访客</span></td>
</tr>
</tbody>
</table>
<hr>
<!-- 表单 -->
<h2>表单示例</h2>
<form>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" placeholder="请输入密码">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" class="form-control" id="email" placeholder="请输入邮箱">
</div>
<div class="form-group">
<label for="role">角色</label>
<select class="form-control" id="role">
<option>管理员</option>
<option>用户</option>
<option>访客</option>
</select>
</div>
<div class="form-group">
<label>兴趣</label>
<label class="checkbox-inline">
<input type="checkbox" value=""> 编程
</label>
<label class="checkbox-inline">
<input type="checkbox" value=""> 设计
</label>
<label class="checkbox-inline">
<input type="checkbox" value=""> 产品
</label>
</div>
<button type="submit" class="btn btn-primary">提交</button>
<button type="reset" class="btn btn-default">重置</button>
</form>
<hr>
<!-- 辅助类 -->
<h2>辅助类示例</h2>
<p class="text-muted">这是灰色文本(text-muted)</p>
<p class="text-primary">这是主色文本(text-primary)</p>
<p class="text-success">这是成功文本(text-success)</p>
<p class="text-info">这是信息文本(text-info)</p>
<p class="text-warning">这是警告文本(text-warning)</p>
<p class="text-danger">这是危险文本(text-danger)</p>
<div class="well">
<p>.well 类创建简单的嵌入效果</p>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示 Bootstrap 全局样式的核心用法:.table 系列类组合使用(table-striped + table-bordered + table-hover),.form-control 统一表单控件样式,.label 标签类用于角色标记,.well 创建嵌入效果。市面应用:后台管理系统的表格、表单页面、状态标记。
可运行示例(实战):后台用户管理页
将下面内容保存为 demo-admin-users.html 双击打开,可见一个把表格、水平表单、状态行、面板、输入框组、进度条组合到一起的后台页面:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>后台用户管理</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<div class="container" style="padding: 20px 0;">
<div class="page-header">
<h1>用户管理 <small>全局样式实战</small></h1>
</div>
<div class="row">
<!-- 左侧:用户数据表格 -->
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-heading">用户列表</div>
<table class="table table-striped table-hover" style="margin-bottom: 0;">
<thead>
<tr>
<th>#</th><th>姓名</th><th>角色</th><th>状态</th>
</tr>
</thead>
<tbody>
<!-- 状态行:用 .success/.warning/.danger 着色整行 -->
<tr class="success">
<td>1</td><td>张三</td>
<td><span class="label label-primary">管理员</span></td>
<td>正常</td>
</tr>
<tr class="warning">
<td>2</td><td>李四</td>
<td><span class="label label-success">编辑</span></td>
<td>待审核</td>
</tr>
<tr class="danger">
<td>3</td><td>王五</td>
<td><span class="label label-default">访客</span></td>
<td>已禁用</td>
</tr>
</tbody>
</table>
</div>
<!-- 面板 + 进度条:展示存储用量 -->
<div class="panel panel-info">
<div class="panel-heading">存储用量</div>
<div class="panel-body">
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar progress-bar-info" style="width: 64%;">64%</div>
</div>
</div>
</div>
</div>
<!-- 右侧:新增用户的水平表单 -->
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">新增用户</div>
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-xs-4 control-label">姓名</label>
<div class="col-xs-8">
<input type="text" class="form-control" placeholder="请输入姓名">
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">邮箱</label>
<div class="col-xs-8">
<!-- 输入框组:在控件前后拼接附加内容 -->
<div class="input-group">
<span class="input-group-addon">@</span>
<input type="text" class="form-control" placeholder="用户名">
</div>
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">角色</label>
<div class="col-xs-8">
<select class="form-control">
<option>管理员</option>
<option>编辑</option>
<option>访客</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-4 col-xs-8">
<button type="submit" class="btn btn-primary btn-block">保存</button>
</div>
</div>
</form>
<p class="text-muted"><small>提示:保存后用户默认为「待审核」状态。</small></p>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】这是一个贴近真实后台的综合页:.panel 系列(.panel-heading + .panel-body)把内容分块装进卡片;表格行用 .success/.warning/.danger 状态类整行着色,比单元格着色更适合「行级状态」;.form-horizontal 让每个 .form-group 表现得像栅格行,.control-label 与输入区用 .col-xs-* 分列,.col-xs-offset-4 让提交按钮与上方输入框对齐;.input-group + .input-group-addon 在输入框左侧拼接 @ 符号,是邮箱、金额、单位输入的标准做法;.progress + .progress-bar 用 width 百分比表达用量。市面应用:各类后台管理系统的用户/订单/权限管理页几乎都是这套「面板 + 表格 + 水平表单」组合。
【实战要点】
- 经典应用场景:后台管理系统表格、用户注册表单、文章排版、状态标记(成功/警告/错误)
- 常见坑 :
.table-condensed减小内边距但内容过多仍会溢出;.form-control在<input type="file">上样式不一致;文字/背景颜色类被覆盖时需!important - 性能与最佳实践 :表格大量数据时考虑分页或虚拟滚动;表单控件使用
autocomplete="off"禁用浏览器自动填充;自定义主题时覆盖@btn-primary-bg等变量;响应式工具类用于移动端隐藏不必要元素
【本章小结】
| 类别 | 代表类名 | 用途 |
|---|---|---|
| 表格 | .table + 修饰类 |
数据展示 |
| 表单 | .form-control、.form-group |
数据输入 |
| 按钮 | .btn + 语义类 |
操作触发 |
| 辅助 | .text-*、.bg-* |
状态标记 |
记忆口诀:"表格 striped bordered hover,表单 form-control,按钮 btn 语义,辅助 text bg"
【面试考点】
Q1:.form-horizontal 的原理是什么?
A:.form-horizontal 给 .form-group 添加 .row 的样式(通过 Less 混合),使 .form-group 表现得像栅格行,内部可用 .col-*-* 分列。.form-group 的子元素(<label>、.form-control)通过 .control-label、.form-control 等类名添加浮动、宽度等样式。实现原理是栅格系统 + 浮动布局的组合应用。
Q2:Bootstrap 表格的斑马纹如何实现?
A:.table-striped 通过 tbody tr:nth-child(odd) 选择奇数行,添加 background-color: #f9f9f9 实现。根据 CSS 选择器规范,:nth-child() 从 1 开始计数,odd 等价于 2n+1。偶数行背景保持默认(#fff)。鼠标悬停高亮通过 tbody tr:hover 实现,优先级高于斑马纹。
四、Bootstrap 组件库
名词解释
- 组件:Bootstrap 预制的 UI 组件,如导航条、下拉菜单、巨幕、分页等
- 下拉菜单:通过 JS 插件实现的交互式菜单,需添加特定 class 和 data 属性
- 导航条:网站顶部导航组件,支持响应式折叠
- 巨幕:大标题展示组件,通常用于着陆页头部
- 路径导航:面包屑导航,显示当前页面在网站中的位置
概念与底层原理
Bootstrap 组件通过CSS 类名 和JavaScript 插件协同工作:
1. 下拉菜单
html
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
下拉菜单
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a href="#">操作 1</a></li>
<li><a href="#">操作 2</a></li>
<li class="divider"></li>
<li><a href="#">操作 3</a></li>
</ul>
</div>
【代码注释】data-toggle="dropdown" 触发 jQuery 插件;按钮与 ul.dropdown-menu 需在同一 .dropdown 容器内。导航条内下拉要把 .dropdown 放在 li 里,并加 dropdown-toggle。
根据 Bootstrap 下拉菜单文档,.dropdown-toggle 的 data-toggle="dropdown" 属性触发 JS 插件,点击时给 .dropdown 容器加 .open 类,从而显示 .dropdown-menu。.caret 创建小三角箭头。需要说明的是:Bootstrap 3 的下拉菜单完全靠 CSS 绝对定位 (.dropdown-menu 设 position: absolute),不依赖任何定位库;Popper.js 是 Bootstrap 4 起才引入的定位方案,BS3 没有用到。
2. 导航条
html
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Messages</a></li>
</ul>
</div>
</div>
</nav>
【代码注释】小屏 .navbar-toggle + data-target 指向 #navbar-collapse;collapse 插件控制展开。navbar-default / navbar-inverse 控制配色;固定顶栏加 navbar-fixed-top 并给 body 补 padding-top。
导航条核心是.navbar-toggle(移动端汉堡菜单)+ .navbar-collapse(可折叠区域)。data-toggle="collapse" 触发折叠插件,data-target="#navbar-collapse" 指定折叠目标。响应式通过媒体查询实现:移动端显示汉堡按钮,桌面端展开导航。
3. 巨幕
html
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>这是一个巨幕组件示例...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
【代码注释】巨幕适合着陆页首屏;可放在 .container 内或全宽使用。Bootstrap 还提供路径导航 .breadcrumb、缩略图 .thumbnail、分页 .pagination 等纯 CSS 组件,结构在官方文档中均可直接复制使用。
.jumbotron 通过 padding、background-color、border-radius 创建大标题展示区。在 Bootstrap 3 里,把 .jumbotron 放在 .container 之外 (或包进 .container-fluid)即可去掉圆角、撑满整个视口宽度------这是 BS3 实现全宽巨幕的标准做法。注意 .jumbotron-fluid 这个类是 Bootstrap 4 才新增的,Bootstrap 3 中并不存在。
深入:组件的三层构成------结构、样式、行为
Bootstrap 组件可以拆成三层,看清这点就能判断「一个组件要不要引 JS」:
- 结构 :一段约定好的 HTML(如
.modal的.modal-dialog > .modal-content > .modal-header/...)。 - 样式:一组 CSS class 决定外观。
- 行为:可选的 JavaScript 插件,负责交互。
据此,组件分两类:纯 CSS 组件 ------路径导航 .breadcrumb、巨幕 .jumbotron、分页 .pagination、标签 .label、缩略图 .thumbnail,只要写对 class 就生效,不需要 JS;需 JS 的组件 ------下拉菜单、标签页切换、导航条折叠,必须引入 jQuery 与 bootstrap.js。排查「组件没反应」时,先判断它属于哪一类。
深入:用 class 表达 UI 状态
Bootstrap 组件的交互状态几乎都用一个 class 表达:下拉菜单展开是 .dropdown 上加 .open,标签页选中是 .active,折叠展开是 .in,模态框可见也是 .in。JavaScript 插件做的事极其克制------只负责增删这个状态 class,至于「展开长什么样」「淡入动画怎么走」全部交给 CSS。这种「JS 管状态、CSS 管表现」的分工,正是后来 Vue/React「数据驱动 class、样式与逻辑解耦」的雏形。
深入:role 与 aria-* 不是摆设
组件 HTML 里大量出现 role="tablist"、aria-haspopup、aria-expanded、aria-controls。它们对视觉用户毫无影响,却是无障碍(Accessibility)的关键:屏幕阅读器靠 role 知道「这是一组标签页」,靠 aria-expanded="true/false" 播报「下拉菜单已展开/已收起」。Bootstrap 的 JS 插件在切换状态时会同步更新这些 aria 属性。做企业级、政府类项目时,这些属性往往是验收硬指标,不能随手删掉。
mermaid 图:组件交互机制
#mermaid-svg-Io8PcC2TG6lYzlQK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Io8PcC2TG6lYzlQK .error-icon{fill:#552222;}#mermaid-svg-Io8PcC2TG6lYzlQK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Io8PcC2TG6lYzlQK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Io8PcC2TG6lYzlQK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Io8PcC2TG6lYzlQK .marker.cross{stroke:#333333;}#mermaid-svg-Io8PcC2TG6lYzlQK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Io8PcC2TG6lYzlQK p{margin:0;}#mermaid-svg-Io8PcC2TG6lYzlQK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster-label text{fill:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster-label span{color:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster-label span p{background-color:transparent;}#mermaid-svg-Io8PcC2TG6lYzlQK .label text,#mermaid-svg-Io8PcC2TG6lYzlQK span{fill:#333;color:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK .node rect,#mermaid-svg-Io8PcC2TG6lYzlQK .node circle,#mermaid-svg-Io8PcC2TG6lYzlQK .node ellipse,#mermaid-svg-Io8PcC2TG6lYzlQK .node polygon,#mermaid-svg-Io8PcC2TG6lYzlQK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Io8PcC2TG6lYzlQK .rough-node .label text,#mermaid-svg-Io8PcC2TG6lYzlQK .node .label text,#mermaid-svg-Io8PcC2TG6lYzlQK .image-shape .label,#mermaid-svg-Io8PcC2TG6lYzlQK .icon-shape .label{text-anchor:middle;}#mermaid-svg-Io8PcC2TG6lYzlQK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Io8PcC2TG6lYzlQK .rough-node .label,#mermaid-svg-Io8PcC2TG6lYzlQK .node .label,#mermaid-svg-Io8PcC2TG6lYzlQK .image-shape .label,#mermaid-svg-Io8PcC2TG6lYzlQK .icon-shape .label{text-align:center;}#mermaid-svg-Io8PcC2TG6lYzlQK .node.clickable{cursor:pointer;}#mermaid-svg-Io8PcC2TG6lYzlQK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Io8PcC2TG6lYzlQK .arrowheadPath{fill:#333333;}#mermaid-svg-Io8PcC2TG6lYzlQK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Io8PcC2TG6lYzlQK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Io8PcC2TG6lYzlQK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Io8PcC2TG6lYzlQK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Io8PcC2TG6lYzlQK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Io8PcC2TG6lYzlQK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster text{fill:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK .cluster span{color:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Io8PcC2TG6lYzlQK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Io8PcC2TG6lYzlQK rect.text{fill:none;stroke-width:0;}#mermaid-svg-Io8PcC2TG6lYzlQK .icon-shape,#mermaid-svg-Io8PcC2TG6lYzlQK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Io8PcC2TG6lYzlQK .icon-shape p,#mermaid-svg-Io8PcC2TG6lYzlQK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Io8PcC2TG6lYzlQK .icon-shape .label rect,#mermaid-svg-Io8PcC2TG6lYzlQK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Io8PcC2TG6lYzlQK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Io8PcC2TG6lYzlQK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Io8PcC2TG6lYzlQK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dropdown
collapse
modal
tab
用户点击
data-toggle 属性
事件类型?
切换 .dropdown-menu 显示
切换 .collapse 折叠状态
显示 .modal 模态框
切换 .tab-pane 面板
JavaScript 插件处理
添加/移除 .in 类
CSS transition 动画
【代码注释】流程图展示 Bootstrap 组件交互机制:用户点击触发 data-toggle 属性,JavaScript 插件根据属性值(dropdown/collapse/modal/tab)执行相应操作,通过添加/移除 .in 类切换显示状态,CSS transition 提供过渡动画。理解此机制有助于排查组件不工作问题(jQuery 未加载、JS 文件缺失、属性错误)。
可运行示例(入门):常用组件演示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 组件演示</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<!-- 导航条 -->
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Logo</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
</div>
</nav>
<div class="container" style="margin-top: 20px;">
<!-- 巨幕 -->
<div class="jumbotron">
<h1>欢迎来到组件演示页面</h1>
<p>这是 Bootstrap 巨幕组件,通常用于着陆页的头部展示...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">了解更多</a></p>
</div>
<!-- 路径导航 -->
<ol class="breadcrumb">
<li><a href="#">首页</a></li>
<li><a href="#">组件</a></li>
<li class="active">组件演示</li>
</ol>
<!-- 分页 -->
<nav>
<ul class="pagination">
<li><a href="#" aria-label="Previous"><span aria-hidden="true"><<</span></a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#" aria-label="Next"><span aria-hidden="true">>></span></a></li>
</ul>
</nav>
<!-- 标签页 -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#home" aria-controls="home" role="tab" data-toggle="tab">首页</a>
</li>
<li role="presentation">
<a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">个人资料</a>
</li>
<li role="presentation">
<a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">消息</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="home">
<h3>首页内容</h3>
<p>这是首页的内容...</p>
</div>
<div role="tabpanel" class="tab-pane" id="profile">
<h3>个人资料</h3>
<p>这是个人资料的内容...</p>
</div>
<div role="tabpanel" class="tab-pane" id="messages">
<h3>消息内容</h3>
<p>这是消息的内容...</p>
</div>
</div>
<!-- 缩略图 -->
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='200' fill='%23999'/%3E%3C/svg%3E" alt="...">
</a>
</div>
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='200' fill='%23666'/%3E%3C/svg%3E" alt="...">
</a>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示 Bootstrap 核心组件:.navbar 导航条(支持移动端折叠),.jumbotron 巨幕(大标题展示),.breadcrumb 路径导航(面包屑),.pagination 分页,.nav-tabs 标签页(需 JS 插件),.thumbnail 缩略图(带阴影的图片容器)。所有组件通过类名 + data-* 属性实现交互。市面应用:企业官网着陆页、文档站导航、电商分类页。
可运行示例(实战):博客文章列表页
将下面内容保存为 demo-blog-list.html 双击打开,可见一个把导航条下拉菜单、媒体对象、面板、列表组、分页拼成的完整博客页:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>技术博客</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
body { padding-top: 70px; }
.post-thumb {
width: 120px; height: 90px; background: #e7e7e7;
display: flex; align-items: center; justify-content: center;
color: #999; border-radius: 4px;
}
</style>
</head>
<body>
<!-- 固定顶栏导航条 + 导航条内下拉菜单 -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#blog-nav">
<span class="sr-only">切换导航</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">技术博客</a>
</div>
<div class="collapse navbar-collapse" id="blog-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页</a></li>
<!-- 导航条内的下拉菜单:.dropdown 放进 li -->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
分类 <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">前端</a></li>
<li><a href="#">后端</a></li>
<li class="divider"></li>
<li><a href="#">全部分类</a></li>
</ul>
</li>
<li><a href="#">关于</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<!-- 左侧:文章列表,用媒体对象排版 -->
<div class="col-md-8">
<h2>最新文章</h2>
<!-- 媒体对象:缩略图 + 文字横向排版 -->
<div class="media">
<div class="media-left">
<div class="post-thumb">封面</div>
</div>
<div class="media-body">
<h4 class="media-heading">深入理解 Bootstrap 栅格系统</h4>
<p class="text-muted">2024-05-01 · 前端</p>
<p>从 12 列模型到响应式断点,拆解栅格背后的浮动与百分比原理......</p>
</div>
</div>
<hr>
<div class="media">
<div class="media-left">
<div class="post-thumb">封面</div>
</div>
<div class="media-body">
<h4 class="media-heading">Bootstrap 组件交互机制详解</h4>
<p class="text-muted">2024-04-20 · 前端</p>
<p>Data API、状态 class 与 jQuery 插件如何协同......</p>
</div>
</div>
<!-- 分页 -->
<nav>
<ul class="pagination">
<li class="disabled"><a href="#"><<</a></li>
<li class="active"><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">>></a></li>
</ul>
</nav>
</div>
<!-- 右侧:侧边栏,用面板 + 列表组 -->
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">热门文章</div>
<!-- 列表组:竖向排列的可点击条目 -->
<ul class="list-group">
<li class="list-group-item">
CSS 盒模型 <span class="badge">128</span>
</li>
<li class="list-group-item">
Flexbox 实战 <span class="badge">96</span>
</li>
<li class="list-group-item">
JavaScript 闭包 <span class="badge">87</span>
</li>
</ul>
</div>
<div class="panel panel-default">
<div class="panel-heading">订阅</div>
<div class="panel-body">
<p>输入邮箱获取更新通知。</p>
<div class="input-group">
<input type="text" class="form-control" placeholder="邮箱">
<span class="input-group-btn">
<button class="btn btn-primary" type="button">订阅</button>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】这个页面把多个组件组合成真实场景:.navbar-inverse 是深色导航条,.navbar-fixed-top 让它固定在视口顶部------固定后必须给 body 补 padding-top(这里 70px),否则页面内容会被导航条压住;导航条内的下拉菜单要把 .dropdown 放进 <li> 里。.media / .media-left / .media-body 是媒体对象 ,专门用于「图片 + 文字横向排版」的列表项(评论、文章列表、消息流)。.list-group + .list-group-item 是竖向条目列表,配 .badge 显示计数。.input-group + .input-group-btn 把按钮拼到输入框右侧。分页里 .disabled 让「上一页」不可点、.active 高亮当前页。市面应用:博客、资讯站、文档站的列表页与侧边栏几乎都是这套组合。
【实战要点】
- 经典应用场景:企业官网导航、后台管理侧边栏、着陆页巨幕、博客分页、商品缩略图
- 常见坑 :
.navbar-toggle未正确折叠(data-targetID 不匹配);标签页未切换(.nav-tabs+.tab-content未对应);.dropdown下拉菜单在移动端不工作(需pull-right类) - 性能与最佳实践 :按需引入组件(
bootstrap.js包含所有插件);移动端考虑简化导航条(折叠内容);巨幕背景图片优化(响应式srcset);分页大量页码时考虑省略号中间页码
【本章小结】
| 组件 | 类名 | 用途 |
|---|---|---|
| 导航条 | .navbar |
顶部/侧边导航 |
| 下拉菜单 | .dropdown |
交互式菜单 |
| 巨幕 | .jumbotron |
大标题展示 |
| 标签页 | .nav-tabs + .tab-content |
内容切换 |
| 分页 | .pagination |
翻页导航 |
记忆口诀:"navbar 导航,dropdown 菜单,jumbotron 巨幕,tabs 标签,pagination 分页"
【面试考点】
Q1:Bootstrap 导航条在移动端如何工作?
A:移动端(视口 < 768px)时,.navbar-collapse 隐藏导航链接,显示 .navbar-toggle 汉堡按钮。用户点击按钮时,JavaScript 插件切换 .navbar-collapse.in 类,展开/折叠导航菜单。此机制通过 @media (max-width: 767px) 媒体查询 + collapse.js 插件实现。桌面端始终展开导航,移动端默认折叠。
Q2:标签页组件的数据属性如何工作?
A:标签页通过 data-toggle="tab" 属性触发 tab.js 插件。点击链接时,插件根据 href="#home" 或 aria-controls="home" 找到对应 .tab-pane,添加 .active 类显示内容,同时隐藏其他面板。.nav-tabs 和 .tab-content 通过 ID 或 aria-controls 关联。理解此机制有助于排查标签页不切换问题(ID 不匹配、.active 类冲突)。
五、JavaScript 插件与交互
名词解释
- 模态框(Modal):弹出层对话框,支持远程内容加载
- 轮播图(Carousel):图片轮播组件,支持自动播放和触控
- 折叠(Collapse):可折叠/展开的内容区域
- 滚动监听(Scrollspy):根据滚动位置自动更新导航状态
- 工具提示(Tooltip):鼠标悬停时显示提示信息
概念与底层原理
Bootstrap 的 JavaScript 插件通过jQuery + Data API (data-* 属性)实现交互功能:
1. 模态框
html
<!-- 触发按钮 -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
Launch demo modal
</button>
<!-- 模态框 HTML -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body...</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
【代码注释】data-target="#myModal" 与模态框 id 必须一致;data-dismiss="modal" 关闭。模态框 HTML 通常放在 body 末尾,避免被父级 overflow:hidden 裁切。需已引入 jQuery 与 bootstrap.js。
根据 Bootstrap 模态框文档,.modal 默认隐藏(display: none),触发时添加 .in 类显示。.modal-dialog 居中显示,.modal-content 定义内容区。.fade 类添加 CSS 过渡动画。data-dismiss="modal" 关闭模态框。
2. 轮播图
html
<div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
<li data-target="#carousel-example-generic" data-slide-to="1"></li>
<li data-target="#carousel-example-generic" data-slide-to="2"></li>
</ol>
<div class="carousel-inner" role="listbox">
<div class="item active">
<img src="..." alt="First slide">
</div>
<div class="item">
<img src="..." alt="Second slide">
</div>
<div class="item">
<img src="..." alt="Third slide">
</div>
</div>
<a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
</a>
<a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
</a>
</div>
【代码注释】首张幻灯片 .item 必须带 active;指示器 data-slide-to 与项索引对应。data-ride="carousel" 页面加载后自动轮播;图片路径错误时只见空白项。Glyphicons 箭头依赖字体文件或 CDN。
轮播图通过 .carousel-inner 内的 .item 定义幻灯片,.active 标记当前显示项。.carousel-control 左右箭头切换幻灯片,.carousel-indicators 底部指示条。data-ride="carousel" 自动启动轮播。根据 Bootstrap 轮播文档,默认 5 秒切换一次,可配置 data-interval。
3. 折叠
html
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample">
Toggle
</button>
<div class="collapse" id="collapseExample">
<div class="well">
This is collapsible content...
</div>
</div>
【代码注释】与导航条折叠共用 collapse 插件;data-target 可用 id 选择器。手风琴多个面板需包在 .panel-group 内(见文档),本节为最小示例。
.collapse 默认折叠(display: none),触发时添加 .in 类展开。data-toggle="collapse" 绑定折叠插件。折叠高度通过 scrollHeight 动态计算,支持过渡动画。
深入:Bootstrap 插件是怎样的 jQuery 插件
Bootstrap 3 的每个 JS 插件都遵循 jQuery 插件模式 :把方法挂到 $.fn 上。bootstrap.js 里大致是 $.fn.modal = function (option) { ... },于是任何 jQuery 对象都能 .modal()。这带来两种用法:
- 声明式 :HTML 上写
data-toggle="modal" data-target="#m",由 Data API 自动调用------入门示例用的就是这种。 - 命令式 :自己写 JS
$('#m').modal('show')/.modal('hide')/.modal('toggle'),用于「表单校验通过后再弹窗」这类需要时机控制的场景。
两者底层是同一个 $.fn.modal,只是触发方式不同。
深入:插件的自定义事件与命名空间
每个插件在关键时刻派发带命名空间的自定义事件 ,以模态框为例:show.bs.modal(开始显示)、shown.bs.modal(显示动画完成)、hide.bs.modal(开始隐藏)、hidden.bs.modal(隐藏完成)。规律是:进行时(show / hide)可以被 preventDefault() 拦截,完成时(shown / hidden,带 -n 的过去分词)只作通知。
js
$('#myModal').on('show.bs.modal', function (e) {
if (!isLoggedIn) e.preventDefault(); // 未登录则阻止弹窗
});
$('#myModal').on('hidden.bs.modal', function () {
$(this).find('form')[0].reset(); // 关闭后重置表单
});
【代码注释】show.bs.modal 在模态框真正显示前触发,e.preventDefault() 能把它拦下------适合做「未登录拦截」「数据未就绪不弹」。hidden.bs.modal 在关闭动画结束后触发,是清理表单、销毁实例的最佳时机。.bs.modal 是事件命名空间,方便 .off('.bs.modal') 批量解绑而不误伤其他监听。市面应用:电商「加入购物车前校验库存」、后台「关闭弹窗即清空草稿」都依赖这组事件。
深入:模态框的遮罩与滚动锁定
模态框打开时,插件做了两件容易被忽略的事:① 动态插入一个 .modal-backdrop 全屏遮罩,半透明、置于模态框之下、页面之上,点击它默认关闭弹窗(data-backdrop="static" 可禁用);② 给 <body> 加 .modal-open 类锁住页面滚动,避免「弹窗内滚到底后继续滚动背景页」的串滚问题。理解这两点,就能解释「为什么模态框 HTML 要放在 <body> 末尾」------若放进某个 overflow:hidden 或带 transform 的容器里,遮罩层级与定位都会出错。
mermaid 图:模态框交互流程
Bootstrap JS Background 模态框 触发按钮 用户 Bootstrap JS Background 模态框 触发按钮 用户 #mermaid-svg-imLG2uAIe08tAk1o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-imLG2uAIe08tAk1o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-imLG2uAIe08tAk1o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-imLG2uAIe08tAk1o .error-icon{fill:#552222;}#mermaid-svg-imLG2uAIe08tAk1o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-imLG2uAIe08tAk1o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-imLG2uAIe08tAk1o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-imLG2uAIe08tAk1o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-imLG2uAIe08tAk1o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-imLG2uAIe08tAk1o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-imLG2uAIe08tAk1o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-imLG2uAIe08tAk1o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-imLG2uAIe08tAk1o .marker.cross{stroke:#333333;}#mermaid-svg-imLG2uAIe08tAk1o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-imLG2uAIe08tAk1o p{margin:0;}#mermaid-svg-imLG2uAIe08tAk1o .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-imLG2uAIe08tAk1o text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-imLG2uAIe08tAk1o .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-imLG2uAIe08tAk1o .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-imLG2uAIe08tAk1o .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-imLG2uAIe08tAk1o .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-imLG2uAIe08tAk1o #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-imLG2uAIe08tAk1o .sequenceNumber{fill:white;}#mermaid-svg-imLG2uAIe08tAk1o #sequencenumber{fill:#333;}#mermaid-svg-imLG2uAIe08tAk1o #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-imLG2uAIe08tAk1o .messageText{fill:#333;stroke:none;}#mermaid-svg-imLG2uAIe08tAk1o .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-imLG2uAIe08tAk1o .labelText,#mermaid-svg-imLG2uAIe08tAk1o .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-imLG2uAIe08tAk1o .loopText,#mermaid-svg-imLG2uAIe08tAk1o .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-imLG2uAIe08tAk1o .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-imLG2uAIe08tAk1o .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-imLG2uAIe08tAk1o .noteText,#mermaid-svg-imLG2uAIe08tAk1o .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-imLG2uAIe08tAk1o .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-imLG2uAIe08tAk1o .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-imLG2uAIe08tAk1o .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-imLG2uAIe08tAk1o .actorPopupMenu{position:absolute;}#mermaid-svg-imLG2uAIe08tAk1o .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-imLG2uAIe08tAk1o .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-imLG2uAIe08tAk1o .actor-man circle,#mermaid-svg-imLG2uAIe08tAk1o line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-imLG2uAIe08tAk1o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 点击按钮 data-toggle="modal" 添加 .modal-backdrop 添加 .in 类 CSS transition 淡入 显示内容 点击关闭/背景 data-dismiss="modal" 移除 .in 类 移除 .modal-backdrop CSS transition 淡出
【代码注释】时序图展示模态框打开/关闭流程:点击触发 → JS 插件添加背景遮罩和 .in 类 → CSS 过渡动画显示内容 → 关闭时反向操作。.modal-backdrop 阻止页面交互,强制用户操作模态框。理解此流程有助于排查模态框不关闭问题(data-dismiss 缺失、jQuery 版本冲突)。
可运行示例(入门):插件综合演示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 插件演示</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
.carousel-inner > .item > img {
width: 100%;
height: 400px;
object-fit: cover;
}
.well {
margin: 20px 0;
}
</style>
</head>
<body>
<!-- 模态框触发按钮 -->
<div class="container" style="margin: 20px auto;">
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
打开模态框
</button>
</div>
<!-- 模态框 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">模态框标题</h4>
</div>
<div class="modal-body">
<p>这是模态框的内容区域...</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
<div class="container">
<!-- 轮播图 -->
<div id="carousel-example-generic" class="carousel slide" data-ride="carousel" style="margin: 40px 0;">
<ol class="carousel-indicators">
<li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
<li data-target="#carousel-example-generic" data-slide-to="1"></li>
<li data-target="#carousel-example-generic" data-slide-to="2"></li>
</ol>
<div class="carousel-inner" role="listbox">
<div class="item active">
<div class="well" style="height: 400px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<h2>幻灯片 1</h2>
</div>
</div>
<div class="item">
<div class="well" style="height: 400px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white;">
<h2>幻灯片 2</h2>
</div>
</div>
<div class="item">
<div class="well" style="height: 400px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white;">
<h2>幻灯片 3</h2>
</div>
</div>
</div>
<a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
</a>
<a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
</a>
</div>
<!-- 折叠 -->
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample">
切换折叠
</button>
<div class="collapse" id="collapseExample">
<div class="well">
<p>这是可折叠的内容区域...</p>
<p>点击上面的按钮可以展开/折叠此区域。</p>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示 Bootstrap 核心插件:.modal 模态框(弹出对话框),.carousel 轮播图(自动切换幻灯片),.collapse 折叠(展开/折叠内容)。所有插件通过 data-* 属性触发,无需手动编写 JavaScript。市面应用:产品详情弹窗、首页轮播图、FAQ 折叠面板、登录注册模态框。
可运行示例(实战):常见问题(FAQ)页
将下面内容保存为 demo-faq.html 双击打开,可见一个用手风琴折叠、工具提示、命令式模态框组合的 FAQ 页:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>常见问题</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
</head>
<body>
<div class="container" style="padding: 30px 0; max-width: 760px;">
<h1>常见问题
<!-- 工具提示:需 JS 手动初始化 -->
<span class="glyphicon glyphicon-question-sign text-muted"
data-toggle="tooltip" data-placement="right"
title="点击每个问题展开答案" style="font-size: 16px;"></span>
</h1>
<!-- 手风琴:.panel-group 内多个 .panel,同一时刻只展开一个 -->
<div class="panel-group" id="faqAccordion" role="tablist">
<div class="panel panel-default">
<div class="panel-heading" role="tab">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#faqAccordion" href="#faq1">
如何引入 Bootstrap?
</a>
</h4>
</div>
<div id="faq1" class="panel-collapse collapse in" role="tabpanel">
<div class="panel-body">
在 `<head>` 引入 `bootstrap.min.css`,在 `<body>` 末尾按
jQuery → bootstrap.js 的顺序引入脚本即可。
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#faqAccordion" href="#faq2">
栅格为什么不生效?
</a>
</h4>
</div>
<div id="faq2" class="panel-collapse collapse" role="tabpanel">
<div class="panel-body">
多半是类名写错(`col` 写成 `com`),或列没有放在 `.row` 内。
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#faqAccordion" href="#faq3">
还有疑问怎么办?
</a>
</h4>
</div>
<div id="faq3" class="panel-collapse collapse" role="tabpanel">
<div class="panel-body">
<button class="btn btn-primary" id="contactBtn">联系我们</button>
</div>
</div>
</div>
</div>
</div>
<!-- 反馈模态框:由 JS 命令式打开 -->
<div class="modal fade" id="contactModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">联系我们</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label>你的问题</label>
<textarea class="form-control" rows="3" placeholder="请描述你的问题"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">提交</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script>
// 工具提示必须手动初始化(出于性能考虑,Bootstrap 不自动启用)
$('[data-toggle="tooltip"]').tooltip();
// 命令式打开模态框:点击按钮时主动调用 .modal('show')
$('#contactBtn').on('click', function () {
$('#contactModal').modal('show');
});
// 利用插件自定义事件:关闭后清空文本域
$('#contactModal').on('hidden.bs.modal', function () {
$(this).find('textarea').val('');
});
</script>
</body>
</html>
【代码注释】这个页面集中演示「需要写 JS」的插件用法。手风琴 :.panel-group 包多个 .panel,每个折叠面板的触发链接加 data-parent="#faqAccordion"------正是这个属性让「展开一个就自动收起其他」,首个面板加 .in 表示默认展开。工具提示 :data-toggle="tooltip" 只是声明,Bootstrap 出于性能考虑不自动初始化 tooltip/popover,必须手动 $('[data-toggle="tooltip"]').tooltip() 启用------这是与 modal/collapse 最大的不同,也是高频踩坑点。命令式模态框 :「联系我们」按钮不写 data-toggle,而是在 JS 里 $('#contactModal').modal('show') 主动弹出,适合「先做别的、再决定弹窗」的时机控制。最后用上一节讲的 hidden.bs.modal 事件,在关闭后清空文本域。市面应用:帮助中心 FAQ、产品功能说明、带反馈入口的文档页。
【实战要点】
- 经典应用场景:产品详情弹窗(模态框)、首页 Banner(轮播图)、常见问题解答(折叠)、工具提示(Tooltip)、图片灯箱
- 常见坑 :模态框不关闭(jQuery 版本冲突、
data-dismiss缺失);轮播图不自动播放(data-ride="carousel"缺失);折叠不展开(ID不匹配、高度计算错误);工具提示需手动初始化($('[data-toggle="tooltip"]').tooltip()) - 性能与最佳实践 :按需引入插件(
bootstrap.js包含所有插件);轮播图图片懒加载(data-src);模态框大量 DOM 考虑虚拟滚动;工具提示/弹出框使用container: 'body'避免被裁剪;移动端禁用自动播放(data-interval="false")
【本章小结】
| 插件 | 类名 | 用途 |
|---|---|---|
| 模态框 | .modal + data-toggle="modal" |
弹出对话框 |
| 轮播图 | .carousel + data-ride="carousel" |
图片轮播 |
| 折叠 | .collapse + data-toggle="collapse" |
展开/折叠 |
| 滚动监听 | data-spy="scroll" |
滚动高亮导航 |
| 工具提示 | data-toggle="tooltip" |
悬停提示 |
记忆口诀:"modal 弹窗,carousel 轮播,collapse 折叠,tooltip 提示"
【面试考点】
Q1:Bootstrap 模态框如何实现点击背景关闭?
A:.modal 的 data-backdrop="static" 禁止点击背景关闭(默认 true 允许)。背景是 .modal-backdrop 元素,点击时触发 modal 插件的 hide 方法。禁止时背景仍然显示但无法交互。追问「如何实现 ESC 键关闭」时答:通过键盘事件监听 keydown.esc 并调用 $('#myModal').modal('hide')。
Q2:轮播图的自动播放机制是什么?
A:轮播图通过 setInterval 实现自动播放,默认间隔 5 秒(data-interval="5000")。data-ride="carousel" 属性在页面加载时自动启动轮播。鼠标悬停时暂停(通过 mouseenter/mouseleave 事件),鼠标离开后恢复。轮播图支持触摸滑动(需引入 swiper 等第三方库增强)。根据 Bootstrap 轮播文档,可通过 .carousel('pause')/.carousel('cycle') API 控制播放。
六、主题定制与自定义栅格
名词解释
- 主题定制:通过 Bootstrap 官方定制工具修改默认样式变量
- Less 变量 :Bootstrap 源码中定义的 Less 变量,如
@brand-primary、@font-size-base - 自定义栅格:基于 Bootstrap 栅格系统原理,自定义列数和断点
- Bootswatch:Bootstrap 第三方主题网站,提供丰富的免费主题
概念与底层原理
1. Bootstrap 定制方式
根据 Bootstrap 定制文档,有三种定制方式:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 官方定制工具 | 在线配置,直接下载 | 定制性受限 | 快速调整品牌色、字体 |
| Less 源码编译 | 完全自定义 | 需要 Less 环境 | 深度定制、移除未使用样式 |
| CSS 覆盖 | 简单直接 | 优先级问题 | 小幅度调整 |
2. 官方定制工具
访问 Bootstrap 3 定制页面,可调整:
- 颜色:品牌色、成功色、信息色、警告色、危险色
- 排版:字体、字号、行高、标题大小
- 组件:按钮、表格、表单、导航条等组件样式
- 栅格系统:列数(默认 12)、容器宽度、断点
- 实用工具:间距、圆角、阴影等
下载的 bootstrap.css 只包含选中的组件,移除了未使用的样式,减少文件大小。
3. Less 源码定制
less
// variables.less
@brand-primary: #e1251b;
@font-size-base: 14px;
// 自定义栅格
@grid-columns: 12;
@grid-gutter-width: 30px;
@grid-float-breakpoint: 768px;
【代码注释】修改 @brand-primary 等变量后需用 Less 编译整个 Bootstrap;只改业务 CSS 时用覆盖更快。@grid-columns 默认为 12,改成 24 可更细粒度分栏但类名也要自建。
编译源码后生成 bootstrap.css,包含所有自定义设置。
手写栅格核心(Flex 版):
css
.row { display: flex; flex-wrap: wrap; }
[class*="col-"] { box-sizing: border-box; padding: 0 10px; }
.col-3 { width: 25%; } /* 12 列体系中 3 列 = 25% */
.col-4 { width: 33.33%; }
.col-8 { width: 66.66%; }
【代码注释】用百分比宽度 + flex-wrap 实现换行,理解 Bootstrap 3 的 float 栅格后,可用 Flex 或 Grid 复现同样「12 等分」思想;响应式版则在各断点用 @media (min-width: ...) 写不同的 width。根据 Bootstrap Less 文档,核心变量包括 @body-bg、@text-color、@link-color、@font-family-sans-serif 等。
4. Bootswatch 主题
Bootswatch 提供 20+ 免费主题,包括:
- Cerulean(天蓝)
- Cosmo(扁平化)
- Cyborg(科技感)
- Darkly(深色模式)
- Flatly(扁平化)
- Journal(复古)
- Lumen(明亮)
- Readable(高可读性)
- Simplex(简洁)
- Slate(深灰)
- Spacelab(实验室)
- Superhero(超级英雄)
- United(联合)
每个主题都包含完整的 Bootstrap 组件重写,替换为该主题的配色和样式。
深入:Bootstrap 源码就是一棵 Less 文件树
打开 Bootstrap 3 的 less/ 目录,入口是 bootstrap.less,它本身几乎不写样式,只用 @import 把几十个文件串起来:
less
@import "variables.less"; // 所有可配置变量
@import "mixins.less"; // 所有混合
@import "normalize.less"; // 浏览器差异修正
@import "scaffolding.less"; // 全局基架
@import "grid.less"; // 栅格系统
@import "buttons.less"; // 按钮
// ...... 其余组件逐个 import
【代码注释】这种「入口文件只做 @import 编排」的组织方式,让框架可裁剪------官方定制工具的本质,就是让你勾选要哪些 @import、改哪些变量,再编译。variables.less 必须第一个导入,因为后面所有文件都要用它定义的变量(如 @brand-primary);mixins.less 紧随其后,供组件文件调用。
深入:栅格类是 Less「生成」的,不是手写的
.col-md-1 到 .col-md-12 共上百个类,没有一个是手敲的------它们由 grid.less 里的混合循环生成 。核心是变量 @grid-columns: 12 和一个递归混合 .make-grid-columns():Less 从 1 数到 @grid-columns,每一轮拼出一个 .col-xx-n 选择器并算好 width: percentage(n / @grid-columns)。
这解释了本章开头提到的能力:把 @grid-columns 改成 24 再编译,框架会自动产出 .col-md-1 到 .col-md-24 ,每列宽度变成 n/24。栅格的「12」不是写死的魔法数字,而是一个可配置变量------这正是「自定义栅格」最快的实现路径。
深入:Less 变量 vs CSS 自定义属性------换肤的两种时机
Bootstrap 3 的主题能力建立在 Less 变量 上,它是编译期 的:改 @brand-primary 必须重新编译出新的 .css,浏览器运行时无法再改。这对「一次性定制品牌色」足够,但做不到「用户点按钮即时换肤」。运行期换肤要靠 CSS 自定义属性 (--theme-color,可被 JavaScript 实时修改)------这也是 Bootstrap 5 大量改用 CSS 变量的原因。一句话区分:Less 变量回答「这个项目长什么样」,CSS 变量回答「这个用户此刻想要什么样」。
可运行示例(入门):主题定制演示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>自定义主题示例</title>
<!-- 使用 Bootswatch Darkly 主题 -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/bootswatch/3.4.0/darkly/bootstrap.min.css">
<style>
/* 自定义品牌色覆盖 */
.btn-primary {
background-color: #e1251b;
border-color: #c81623;
}
.btn-primary:hover {
background-color: #c81623;
border-color: #a8141d;
}
/* 自定义链接颜色 */
a {
color: #e1251b;
}
a:hover {
color: #c81623;
}
/* 自定义表单控件焦点颜色 */
.form-control:focus {
border-color: #e1251b;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(225, 37, 27, .5);
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">自定义主题</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
</div>
</nav>
<div class="container" style="margin: 20px auto;">
<div class="jumbotron">
<h1>自定义主题示例</h1>
<p>本示例使用 Bootswatch Darkly 主题 + 自定义 CSS 覆盖品牌色。</p>
<p>
<button type="button" class="btn btn-primary btn-lg">大按钮</button>
<button type="button" class="btn btn-default btn-lg">默认按钮</button>
</p>
</div>
<form>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" placeholder="请输入密码">
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
<script src="https://cdn.bootcdn.net/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
【代码注释】本示例展示主题定制两种方式:使用 Bootswatch Darkly 主题(深色模式),以及通过 CSS 覆盖自定义品牌色(#e1251b 京东红)。CSS 覆盖通过提高选择器优先级(.btn-primary 比 Bootstrap 默认样式更具体)实现。市面应用:品牌官网定制品牌色、管理后台深色模式、产品 Landing Page 定制。
可运行示例(实战):手写一套响应式 12 列栅格
下面这个页面不引入 Bootstrap ,从零手写一套响应式 12 列栅格------把前面讲的「float/flex + 百分比 + box-sizing + 媒体查询」原理完整复现。保存为 demo-custom-grid.html 双击即可运行:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>手写响应式栅格</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font: 14px/1.5 "Microsoft YaHei", sans-serif; padding: 20px; }
/* 容器:居中 + 最大宽度按断点跳变 */
.container { margin: 0 auto; padding: 0 15px; }
@media (min-width: 768px) { .container { width: 750px; } }
@media (min-width: 992px) { .container { width: 970px; } }
@media (min-width: 1200px) { .container { width: 1170px; } }
/* 行:flex 容器 + 负边距抵消列的 padding */
.row { display: flex; flex-wrap: wrap; margin: 0 -10px; }
/* 列:统一加左右 padding 作为列间距(gutter),border-box 让 padding 不撑大 width */
[class*="col-"] { padding: 0 10px; }
/* 12 列基础宽度------任意屏幕都生效(相当于 Bootstrap 的 col-xs-*) */
.col-1 { width: 8.3333%; } .col-2 { width: 16.6666%; }
.col-3 { width: 25%; } .col-4 { width: 33.3333%; }
.col-6 { width: 50%; } .col-8 { width: 66.6666%; }
.col-12 { width: 100%; }
/* 响应式:≥768px 时才启用 col-md-* 覆盖基础值(移动优先:min-width 逐级增强) */
@media (min-width: 768px) {
.col-md-3 { width: 25%; }
.col-md-4 { width: 33.3333%; }
.col-md-8 { width: 66.6666%; }
}
.demo { background: #e1251b; color: #fff; text-align: center;
padding: 20px 0; margin-bottom: 20px; border-radius: 4px; }
.demo.alt { background: #1890ff; }
.demo.alt2 { background: #52c41a; }
h2 { margin: 20px 0 10px; }
</style>
</head>
<body>
<div class="container">
<h1>手写响应式 12 列栅格</h1>
<h2>① 基础栅格:任意屏幕都是 3 等分</h2>
<div class="row">
<div class="col-4"><div class="demo">col-4</div></div>
<div class="col-4"><div class="demo alt">col-4</div></div>
<div class="col-4"><div class="demo alt2">col-4</div></div>
</div>
<h2>② 不等宽:4 + 8 = 12</h2>
<div class="row">
<div class="col-4"><div class="demo">col-4(1/3)</div></div>
<div class="col-8"><div class="demo alt">col-8(2/3)</div></div>
</div>
<h2>③ 响应式:手机每行 1 个,≥768px 每行 3 个</h2>
<div class="row">
<div class="col-12 col-md-4"><div class="demo">col-12 col-md-4</div></div>
<div class="col-12 col-md-4"><div class="demo alt">col-12 col-md-4</div></div>
<div class="col-12 col-md-4"><div class="demo alt2">col-12 col-md-4</div></div>
</div>
<p>把浏览器窗口拉窄到 768px 以下,第 ③ 组会从「一行三个」变成「每个独占一行」。</p>
</div>
</body>
</html>
【代码注释】这套手写栅格逐条对应 Bootstrap 3 的设计:① * { box-sizing: border-box } 是前提,让列的左右 padding 算进 width,百分比才精确;② .row 用 display:flex; flex-wrap:wrap 实现横向排列与自动换行(Bootstrap 3 源码用的是 float,这里用更现代的 Flex,思想一致);③ .row 的 margin: 0 -10px 负边距 抵消列的 padding: 0 10px,使最左/最右的列内容与容器边缘对齐;④ 列宽是 n/12 的百分比 ------col-4 即 33.3333%;⑤ 响应式靠 @media (min-width: 768px) :.col-12 无媒体查询、是「基础值」,.col-md-4 在媒体查询里、屏幕够宽才覆盖它,这正是「移动优先 + min-width 逐级增强」。读懂这一份代码,就读懂了 Bootstrap grid.less 的全部核心。市面应用:不想为一个小项目引入整个 Bootstrap 时,几十行这样的栅格 CSS 就够用;Tailwind、UnoCSS 的栅格工具类也是同一套数学。
【实战要点】
- 经典应用场景:品牌官网品牌色定制、管理后台深色模式、SaaS 产品主题切换
- 常见坑 :CSS 覆盖优先级不够(需
!important或更具体选择器);Less 编译后忘记替换旧 CSS;第三方主题与自定义样式冲突;定制后组件间距不一致 - 性能与最佳实践:使用官方定制工具只包含需要的组件(减少 50%+ 文件大小);生产环境使用压缩版;考虑使用 CSS 变量而非 Less(便于运行时切换主题);Bootswatch 主题适合快速原型,生产环境建议定制
【本章小结】
| 定制方式 | 方法 | 优点 | 缺点 |
|---|---|---|---|
| 官方工具 | 在线配置 | 无需编译环境 | 定制性受限 |
| Less 源码 | 修改变量编译 | 完全自定义 | 需要 Less 环境 |
| CSS 覆盖 | 后加载自定义 CSS | 简单直接 | 优先级问题 |
| 第三方主题 | 引入 Bootswatch | 快速美观 | 需适配品牌色 |
记忆口诀:"官方工具快速,Less 源码深度,CSS 覆盖简单,Bootswatch 多彩"
【面试考点】
Q1:如何实现 Bootstrap 主题切换(明/暗模式)?
A:方案1:加载两套 Bootstrap CSS(如 bootstrap.min.css + bootstrap-dark.min.css),通过 JS 切换 <link> 标签;方案2:用 CSS 变量(Bootstrap 5 支持)或 Less 变量编译多套主题;方案3:用 Bootswatch 主题 + CSS 覆盖。明/暗模式可通过 prefers-color-scheme 媒体查询自动跟随系统,或提供切换按钮保存用户偏好到 localStorage。
Q2:Bootstrap Less 源码定制与 CSS 覆盖的选择依据?
A:Less 源码定制适合深度定制(改变组件结构、移除未使用样式、大量颜色调整),生产环境推荐。CSS 覆盖适合小幅度调整(品牌色、间距),快速原型推荐。Less 编译后的 CSS 文件更小(移除未使用样式),但需要构建流程;CSS 覆盖文件更大(包含所有 Bootstrap 样式 + 覆盖),但无需编译。追问「性能」时答:Less 定制生成的 CSS 更小(可能减少 30-50KB),但需要构建工具;CSS 覆盖浏览器需解析两份样式(先 Bootstrap 后覆盖),但开发更快。
Q3:不用 Bootstrap 能否自己实现 12 列栅格?
A:可以。核心是:父级 display:flex; flex-wrap:wrap 或 float 清除浮动;子级 width: (n/12)*100% + 左右 gutter(padding);响应式则为各断点写 @media (min-width: ...) 改 width。手写一遍能读懂 Bootstrap 3 源码中的 grid.less,也为后续学 Bootstrap 5 / Tailwind 打基础。
总结
知识点回顾(思维导图)
#mermaid-svg-BQezbQn7GRQDd9t8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BQezbQn7GRQDd9t8 .error-icon{fill:#552222;}#mermaid-svg-BQezbQn7GRQDd9t8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BQezbQn7GRQDd9t8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BQezbQn7GRQDd9t8 .marker.cross{stroke:#333333;}#mermaid-svg-BQezbQn7GRQDd9t8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BQezbQn7GRQDd9t8 p{margin:0;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge{stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 text{fill:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth--1{stroke-width:17;}#mermaid-svg-BQezbQn7GRQDd9t8 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-0{stroke-width:14;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-1{stroke-width:11;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 text{fill:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-2{stroke-width:8;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-3{stroke-width:5;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-4{stroke-width:2;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-5{stroke-width:-1;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-6{stroke-width:-4;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-7{stroke-width:-7;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-8{stroke-width:-10;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-9{stroke-width:-13;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 polygon,#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 text{fill:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .edge-depth-10{stroke-width:-16;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled circle,#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:lightgray;}#mermaid-svg-BQezbQn7GRQDd9t8 .disabled text{fill:#efefef;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-root rect,#mermaid-svg-BQezbQn7GRQDd9t8 .section-root path,#mermaid-svg-BQezbQn7GRQDd9t8 .section-root circle,#mermaid-svg-BQezbQn7GRQDd9t8 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-BQezbQn7GRQDd9t8 .section-root text{fill:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-root span{color:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .section-2 span{color:#ffffff;}#mermaid-svg-BQezbQn7GRQDd9t8 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-BQezbQn7GRQDd9t8 .edge{fill:none;}#mermaid-svg-BQezbQn7GRQDd9t8 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-BQezbQn7GRQDd9t8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Bootstrap 前端框架
快速入门
viewport 设置
CDN 引入
jQuery 依赖
基本组件
响应式栅格
12 列系统
断点 xs/sm/md/lg
列偏移与推拉
栅格嵌套
全局样式
排版系统
表格样式
表单样式
辅助类
组件库
导航条
下拉菜单
巨幕
标签页
分页
JavaScript 插件
模态框
轮播图
折叠
工具提示
主题定制
官方定制工具
Less 源码
CSS 覆盖
Bootswatch 主题
【代码注释】思维导图总结全文知识体系:从快速入门引入 Bootstrap,到响应式栅格实现移动优先布局,再到全局样式规范(表格、表单、按钮),组件库(导航、菜单、巨幕等),JavaScript 插件(模态框、轮播图等),最后到主题定制实现品牌化。掌握 Bootstrap 将让你快速构建现代响应式网站。
高频面试题速查
- Bootstrap 3 与 4/5 的区别 --- IE8 支持、jQuery 依赖、Less vs Sass、新增栅格层、Flexbox 工具类
- viewport 元标签作用 --- 移动端视口宽度等于设备宽度,初始缩放比例为 1
- 栅格系统断点 --- xs (<768px)、sm (≥768px)、md (≥992px)、lg (≥1200px)
.row负边距原理 --- 抵消.container的 padding,让列从边缘开始布局- 模态框交互机制 ---
data-toggle="modal"触发插件,添加.in类显示,data-dismiss关闭 - 轮播图自动播放 ---
data-ride="carousel"启动,setInterval每 5 秒切换 - 主题定制方式 --- 官方工具、Less 源码、CSS 覆盖、Bootswatch 第三方主题
- 列偏移与居中 ---
.col-md-offset-*向右偏移,3+3+3=6 可实现居中 - 引入顺序 --- viewport → CSS → jQuery → bootstrap.js
col-md拼写 --- 必须是col不是com
验收自检清单
- 页面含 viewport,CDN 或本地
bootstrap.min.css加载成功 - 能写
row+col-md-*,一行总和为 12 - 会用
form-horizontal或form-group+form-control - 导航条小屏可折叠(
navbar-toggle+collapse) - 模态框 / 轮播图
data-toggle能弹出或切换 - 知道防抖搜索与 Bootstrap 无直接关系(属 JS 性能章节)
常见错误排查表
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 样式全无 | 路径错或未引入 CSS | 检查 Network 面板 200 |
| 下拉/模态无效 | jQuery 未加载或顺序错 | 先 jQuery 后 bootstrap.js |
| 栅格不换行却挤在一起 | 类名拼错 com-md |
改为 col-md |
| 列始终 100% 宽 | 未包在 .row 内 |
结构:container → row → col |
| 模态框不显示 | id 与 data-target 不一致 |
核对 #myModal |
| 轮播不自动播 | 无 data-ride 或 JS 报错 |
首项加 active |
| 定制色不生效 | 优先级不足 | 后加载自定义 CSS |
学习建议
- 练习路径:按 快速入门 → 栅格系统 → 响应式栅格 → 全局样式 → 组件 → 插件 顺序实践,每章的入门与实战示例都动手写一遍
- 深度学习 :
- 阅读 Bootstrap 3 官方文档(中文版)
- 研究 Bootstrap 源码(
less/目录),理解变量和混合机制 - 学习 Bootstrap 定制,实现品牌官网主题
- 探索 Bootswatch 主题源码,学习主题设计技巧
- 实战项目 :
- 用 Bootstrap 重写个人网站或博客
- 构建响应式电商着陆页(产品展示 + 轮播图 + 表单)
- 开发后台管理系统模板(导航条 + 表格 + 分页 + 模态框)
- 定制 Bootstrap 主题,适配企业品牌色
- 进阶方向 :
- 学习 Bootstrap 4/5(Flexbox 栅格、卡片组件、工具类扩展)
- 探索 Bootstrap 替代方案(Tailwind CSS、Bulma、Foundation)
- 研究 Bootstrap 插件源码,学习 jQuery 插件开发模式
- 关注现代 CSS 框架(如 Pure.css、Picnic CSS)的无依赖理念
相关资源: