简介:这份以Bootstrap 4.0为主线的「Bootstrap从入门到项目实战」压缩包,面向希望系统学习响应式、移动优先网站开发的前端初学者与进阶开发者,帮助读者从基础结构过渡到组件组合、交互插件与项目落地。内容围绕栅格系统的容器、行与列布局展开,覆盖导航条、按钮、表单、下拉菜单、模态框、轮播等常用组件,并延伸到卡片、警告、徽章、进度条等样式工具,以及Collapse、Tab、Tooltip、Popover等JavaScript插件和Sass变量主题定制。包内共1389个文件,以html案例页、scss与less源码、css样式、js脚本为主体,另含map映射、png/jpg/svg图片和woff/ttf等字体素材,压缩包约111.24MB,源码与可运行页面兼具,便于对照案例拆解布局、交互和适配思路。已有716人学习,适合课程练习、作品集模板搭建及企业站页面实践,也可作为前端课程作业与毕业设计的参考素材,通过逐章研读案例理解Bootstrap的定制方式与响应式设计方法。
1. 一周交付后台界面,我为什么又翻出了 Bootstrap 实战包
某公司的工单系统改版,设计稿只给了 PC 端,要求两周内交出能点的原型,前端就我一个人。手写 flex 加媒体查询写到第三天开始翻车:按钮在 Safari 上高度不齐,表格一进小屏就横向溢出。后来把 Bootstrap 这套东西重新捡起来,两天把骨架搭完。这份「从入门到项目实战」的包,主体是一套教程加配套源码,前半段讲栅格、工具类、组件,后半段是一个能跑起来的多页项目,登录、列表、表单、弹窗这些后台高频页面都在里面。它不教你背类名,给的是一个能直接改的起点。适合三类人:后端转全栈要快速出管理界面的、接私活需要短周期交付的、以及已经在用 Bootstrap 却一直靠覆盖 CSS 打补丁、想搞清楚 Sass 变量怎么改的。下面按能跑起来的顺序拆。
2. 栅格与断点:12 列怎么算,断点怎么选
2.1 容器、行、列的三层嵌套关系
栅格出问题,九成是结构没按container > row > col三层来。这三层各干一件事:容器负责最大宽度和水平居中,行负责用负外边距抵消列的 gutter,列负责自己那一份宽度和内边距。少一层,间距就会错位;多套一层容器,宽度会突然缩水。
具体到数值,.row默认带--bs-gutter-x: 1.5rem,它通过左右各-0.75rem的负 margin 把两边撑出去,然后每个.col再各带0.75rem的 padding,视觉上相邻两列之间就是 1.5rem。所以你在.col里再手动加margin-left就会叠加,看起来「怎么对不齐」,其实是 gutter 被算了两遍。
要统一调整间距,别去改每个列,改变量就够了:
/* 全局收紧栅格间距:后台系统信息密度高,1.5rem 通常偏大 */ :root { --bs-gutter-x: 1rem; /* 列与列之间的水平间距 */ --bs-gutter-y: 1rem; /* 换行后行与行之间的垂直间距 */ }这段声明放在 Bootstrap CSS 之后才会生效,因为它覆盖的是自定义属性而不是选择器优先级。改完的效果是所有.row的间距同步变化,包括那些你没动过的组件内部的行。参数只有两个,x管水平、y管换行后的垂直,后台表格页建议x取0.75rem到1rem,营销页可以保持默认。
2.2 断点命名与响应式类的组合规则
Bootstrap 5 的断点是移动优先的min-width,也就是你写的col-md-6表示「宽度大于等于 md 时占 6 列」,小于 md 时回落到上一个生效的规则。断点表如下:
| 别名 | 触发宽度 | 典型场景 | 类前缀 |
|---|---|---|---|
| sm | ≥576px | 大屏手机横屏 | col-sm-* |
| md | ≥768px | 平板竖屏 | col-md-* |
| lg | ≥992px | 笔记本 | col-lg-* |
| xl | ≥1200px | 常规桌面 | col-xl-* |
| xxl | ≥1400px | 宽屏显示器 | col-xxl-* |
组合写法要按从小到大排,col-12 col-md-6 col-xl-3读起来是:手机一行一个、平板一行两个、宽屏一行四个。顺序反了浏览器也能解析,但后面维护的人会骂人。除了列宽,所有工具类都支持断点后缀,比如d-none d-lg-block、mt-3 mt-lg-0、text-center text-md-start,这套后缀机制是后面所有布局微调的基础。
有个容易忽略的点:不带断点的col是等分剩余空间,带断点的col-md也是等分,但只在 md 以上等分、以下变成整行。做后台侧边栏时用col-lg-2而不是col-2,否则手机上一进页面就被挤成一条缝。
2.3 一个可复现的后台布局骨架
下面这段是包里示例项目的骨架简化版,去掉业务内容,只留结构,可以直接存成index.html打开看效果:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="./assets/css/bootstrap.min.css"> <title>工单后台</title> </head> <body class="bg-light"> <!-- container-fluid:后台系统占满视口,不用固定最大宽度 --> <div class="container-fluid"> <div class="row min-vh-100"> <!-- 侧边栏:lg 以下隐藏,lg 及以上占 2 列 --> <aside class="col-lg-2 d-none d-lg-block bg-dark text-white p-3"> <nav class="nav flex-column"> <a class="nav-link text-white" href="#">工单列表</a> <a class="nav-link text-white-50" href="#">数据看板</a> </nav> </aside> <!-- 主内容区:lg 及以上占 10 列,以下占满 12 列 --> <main class="col-lg-10 p-4"> <!-- g-3 控制这一行内部的间距,不影响外层 --> <div class="row g-3"> <div class="col-12 col-md-6 col-xl-3"> <div class="card"><div class="card-body">待处理 12</div></div> </div> <div class="col-12 col-md-6 col-xl-3"> <div class="card"><div class="card-body">处理中 5</div></div> </div> <div class="col-12 col-md-6 col-xl-3"> <div class="card"><div class="card-body">已关闭 87</div></div> </div> <div class="col-12 col-md-6 col-xl-3"> <div class="card"><div class="card-body">超时 3</div></div> </div> </div> </main> </div> </div> <script src="./assets/js/bootstrap.bundle.min.js"></script> </body> </html>逻辑说明:外层row min-vh-100把整页撑到视口高度,侧边栏和主内容区才能等高;侧边栏用d-none d-lg-block做断点显隐,比写媒体查询少十几行;卡片区用col-12 col-md-6 col-xl-3完成三档切换。参数上,g-3是 gutter 的缩写,取值 0 到 5 对应 0 到 3rem,g-3约 1rem;如果想让卡片之间有更明显的分隔,用g-4。min-vh-100依赖视口单位,在移动端浏览器地址栏收起时会有一点跳动,后台系统一般可以接受,C 端页面要谨慎。
2.4 工具类替代手写 CSS 的边界
工具类好用,但滥用会让 HTML 变成一长串类名。我的判断标准是:一次性、跟结构强相关的间距和显隐,用工具类;会在多处复用、带状态的视觉样式,写成自己的类再配合 Sass。比如卡片的圆角、阴影、hover 态,就别在每张卡片上堆rounded-3 shadow-sm border-0,抽一个.stat-card出来更省事。
还有个实际边界是优先级。工具类大多带!important,你在自定义类里写的 padding 会被它盖掉。排查时打开开发者工具看那条规则有没有!important标记,比一行行翻代码快得多。包里示例项目对这些地方有注释标注,拿到后搜索!important能快速定位哪些是工具类在起作用。
3. 组件与交互:JS 插件怎么初始化
3.1 bundle 版与分离引入的区别
Bootstrap 的交互组件(模态框、下拉菜单、Tooltip、Popover)依赖一个定位库。官方提供两种产物:bootstrap.js需要你另外引入定位库,bootstrap.bundle.js已经把定位库打进去了。新手最常见的报错就是只引了前者,点开下拉菜单没反应,控制台一行Popper is not defined。
引入顺序也有讲究:CSS 放<head>,JS 放</body>前,或者放<head>里加defer。放 head 里同步执行会导致 JS 跑的时候 DOM 还没解析完,document.querySelector拿到 null。我一般直接用 bundle 版放 body 末尾,少一个依赖少一个坑。
<!-- head 里只放样式 --> <link rel="stylesheet" href="./assets/css/bootstrap.min.css"> <!-- body 末尾放脚本,bundle 已内置定位库 --> <script src="./assets/js/bootstrap.bundle.min.js"></script>参数说明:bundle版体积比分离版多几十 KB,但对后台系统来说省掉的排错时间更值钱。如果项目里已经单独用了定位库的其他版本,才需要考虑分离引入并锁定版本一致,否则一律用 bundle。
3.2>// 拿到模态框元素 const modalEl = document.getElementById('ticketModal'); // backdrop: 'static' 禁止点遮罩关闭;keyboard: false 禁止 Esc 关闭 const ticketModal = new bootstrap.Modal(modalEl, { backdrop: 'static', keyboard: false, focus: true // 打开后自动聚焦到第一个可聚焦元素 }); // 打开前先重置上一次的校验状态,避免红框残留 document.getElementById('openTicketBtn').addEventListener('click', () => { const form = document.getElementById('ticketForm'); form.classList.remove('was-validated'); form.reset(); ticketModal.show(); });
参数说明:backdrop取true(默认,点遮罩关闭)、'static'(点遮罩不关闭)或false(不渲染遮罩);keyboard控制 Esc 键;focus控制打开时是否自动聚焦。事件方面,模态框会依次触发show.bs.modal、shown.bs.modal、hide.bs.modal、hidden.bs.modal,需要在弹窗显示后初始化图表或拉数据的,挂在shown上,别挂在show上,后者触发时元素还没完成布局。
3.3 表单校验落地:needs-validation 的正确用法
Bootstrap 的表单校验样式依赖两个类:needs-validation表示这个表单要校验,was-validated表示已经提交过一次、该显示红框和错误文案了。只在提交后加was-validated,是为了避免用户刚进页面就看到一片红。
<form id="ticketForm" class="needs-validation" novalidate> <div class="mb-3"> <label for="title" class="form-label">标题</label> <!-- required 和 minlength 是原生约束,Bootstrap 只负责样式 --> <input type="text" class="form-control" id="title" required minlength="4"> <div class="invalid-feedback">标题至少 4 个字符。</div> </div> <div class="mb-3"> <label for="level" class="form-label">优先级</label> <select class="form-select" id="level" required> <option value="">请选择</option> <option value="p1">P1</option> <option value="p2">P2</option> </select> <div class="invalid-feedback">必须选择优先级。</div> </div> <button type="submit" class="btn btn-primary">提交</button> </form>配套的提交处理:
const form = document.getElementById('ticketForm'); form.addEventListener('submit', (event) => { event.preventDefault(); // 阻止原生提交,改走自己的逻辑 event.stopPropagation(); // 阻止冒泡,避免外层也处理 // checkValidity 会跑一遍 required、minlength 等原生约束 if (!form.checkValidity()) { form.classList.add('was-validated'); return; } // 校验通过,这里换成自己的接口调用 submitTicket(new FormData(form)); });三个参数必须对:novalidate关掉浏览器自带的提示气泡,否则会跟 Bootstrap 的样式打架;invalid-feedback必须紧跟在输入控件之后,它靠兄弟选择器显示;was-validated加在 form 上而不是 input 上,加错位置红框不出来。这套校验只覆盖原生约束,密码一致性、手机号格式这类要自己写setCustomValidity。
4. Sass 定制:改主题色不是覆盖 CSS
4.1 !default 机制决定了覆盖顺序
Bootstrap 源码里每个变量都带!default,意思是「如果之前没人给它赋值,就用这个默认值」。所以你要改主题色,必须在引入variables之前先声明$primary,而不是在编译后的 CSS 里再写一遍.btn-primary覆盖。后者会漏掉所有用到主题色的地方——链接、焦点环、选中态、进度条,改一个漏三个。
这个机制也解释了为什么很多人照着教程写 SCSS 还是没生效:他们把@import "bootstrap"放到了第一行,后面再写变量,此时!default已经完成赋值,后面的声明只能算普通变量,对已生成的类没有影响。
4.2 按需引入与编译链路
完整引入的 CSS 压缩后也不小,后台系统用不到轮播、画廊、Offcanvas 这些,可以只引需要的模块。前提是顺序不能乱:函数 → 变量 → 映射 → 混合 → 工具类 → 具体组件。
// src/custom.scss // 1. 函数必须先引入,后面改颜色要用到它内部的颜色计算函数 @import "bootstrap/scss/functions"; // 2. 在变量之前声明你的覆盖值 $primary: #2f5bea; $border-radius: .5rem; $enable-shadows: true; $font-size-base: .9375rem; // 15px,后台系统常用的紧凑字号 // 3. 引入变量、暗色变量、映射、混合、工具类 @import "bootstrap/scss/variables"; @import "bootstrap/scss/variables-dark"; @import "bootstrap/scss/maps"; @import "bootstrap/scss/mixins"; @import "bootstrap/scss/utilities"; // 4. 按需引入:用不到的组件直接不写 @import "bootstrap/scss/root"; @import "bootstrap/scss/reboot"; @import "bootstrap/scss/type"; @import "bootstrap/scss/containers"; @import "bootstrap/scss/grid"; @import "bootstrap/scss/forms"; @import "bootstrap/scss/buttons"; @import "bootstrap/scss/card"; @import "bootstrap/scss/modal"; @import "bootstrap/scss/utilities/api";逻辑说明:第 2 步的变量覆盖只对后面引入的模块生效,所以位置必须在variables之前;第 4 步每多引一个文件,产物就大一点,删掉card就不会生成卡片类。参数上,$primary换成品牌色后,所有派生色(hover、active、焦点环)会由内部函数自动算出来,不用一个个改;$font-size-base改小是后台系统的常规操作,能让表格和表单更紧凑。
编译命令放在package.json里:
{ "scripts": { "css": "sass --style=compressed --no-source-map src/custom.scss dist/css/app.min.css", "css:watch": "sass --watch src/custom.scss:dist/css/app.min.css" } }参数说明:--style=compressed输出压缩后的 CSS;--no-source-map关掉 sourcemap,线上产物不需要;--watch开发时用,改 SCSS 自动重编。编译时如果出现@import的弃用警告,是因为新版 Sass 推@use,而 Bootstrap 5 的源码体系仍在使用@import写法,加--silence-deprecation=import屏蔽即可,不影响产物。
4.3 主题色替换后要检查的两处
改完$primary不代表全部到位,还有两处容易漏。一是 CSS 变量层,Bootstrap 5 把颜色同时输出成--bs-primary和--bs-primary-rgb,前者给普通属性用,后者给需要透明度的场景用(比如rgba(var(--bs-primary-rgb), .25)做焦点环)。如果你在运行时用 JS 动态改主题,两个都要改,只改一个会出现焦点环还是蓝色的情况。
二是暗色模式。variables-dark里有独立的暗色主题色映射,如果你的项目开了># 编译后直接验证主题色有没有写进去 grep -o "\-\-bs-primary-rgb: [0-9, ]*" dist/css/app.min.css
这条命令会打印出 RGB 三元组,47,91,234 对应 #2f5bea,对不上就说明变量位置写错了。
5. 避坑与排查:五条翻车记录
5.1 自定义样式不生效,先看引入顺序
现象:写在style.css里的按钮颜色怎么都不管用,开发者工具里被划掉。
原因:bootstrap.min.css在style.css之后引入,同优先级下后者被覆盖;或者反过来,你写了.btn但 Bootstrap 用的是.btn-primary这种更具体的选择器。
解决:样式表顺序固定为「Bootstrap → 自己的 CSS → 页面级内联样式」,自己的类名尽量带业务前缀(如.ticket-btn),避开.btn、.card这些框架类名,从根上绕开优先级纠缠。
5.2 控制台报 bootstrap is not defined
现象:点击按钮没反应,控制台一条红色报错。
原因:初始化脚本写在了<head>里的内联<script>中,执行时 Bootstrap 的 JS 还没加载;或者只引了 CSS 没引 JS;再或者引了bootstrap.js但没引定位库。
解决:初始化代码放到 bundle 脚本之后,或者整体加defer;确认路径大小写正确,Linux 服务器区分大小写,本地 Windows 能跑线上 404 是常事。这类问题用浏览器 Network 面板看脚本有没有 200 返回,三秒钟能定位。
5.3 小屏出现横向滚动条
现象:手机上页面能左右拖动,右侧一片空白。
原因:把.row直接放在了body下而没包容器,负 margin 把内容撑出视口;或者某个列里放了固定宽度的表格、white-space: nowrap的长文本。
解决:先确认结构是container > row > col;再给表格外层套.table-responsive;最后检查有没有元素写了width: 100vw,视口宽度包含滚动条宽度,换成100%就好。排查时给body临时加outline: 1px solid red找不到元凶,用开发者工具逐个隐藏子元素更快。
5.4 Sass 编译报 Undefined variable
现象:sass编译直接失败,提示$primary未定义。
原因:变量声明写在了@import "bootstrap/scss/variables"之后;或者自定义文件里用了@use但没写as *,变量被命名空间隔离了。
解决:所有覆盖变量集中在文件顶部、函数引入之后;混用@use和@import时,Bootstrap 内部仍是@import体系,自定义文件也统一用@import,别一半一半。
5.5 从 4.x 升上来,data-* 属性全失效
现象:下拉菜单、折叠面板的style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />