ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Bootstrap入门到实战:组件系统、栅格、下拉菜单与表单校验

Bootstrap入门到实战:组件系统、栅格、下拉菜单与表单校验 1. Bootstrap框架真正的定位以及第一行代码前必须想清楚的事说起Bootstrap很多新手第一个反应是“这是个CSS库”然后用的时候发现怎么还有个bootstrap.min.js又冒出个Popper.js开始一头雾水。实际上Bootstrap是一整套组件化前端方案CSS负责长什么样JS负责那些需要交互的部分比如下拉菜单、轮播图、弹窗。我在带新人做后台系统的时候经常说一句话你把它当CSS库用上手很快你把它当JS库用才需要额外花点时间理解它的组件逻辑。很多公司内部管理系统、CMS后台、运维平台到今天仍然在用Bootstrap原因很简单它足够成熟、文档全、组件覆盖率高而且“会写HTML就能快速搭出能看的界面”。对刚入门的前端、后端转前端的同学来说Bootstrap是性价比非常高的切入点尤其是“bootstrap入门”这个阶段的读者最需要的是把框架的运行机制搞清楚而不是急着抄代码。1.1 它不是“一个CSS库”而是一套约定好的组件系统我们写一个按钮原生HTML加CSS通常要准备按钮的底色、hover状态、点击状态、disabled状态还要处理圆角、字号、内边距。Bootstrap的做法简单得多一个classbtn btn-primary就把这些全部解决。它的设计思路叫“约定优于配置”所有组件都有一套既定样式你按它的命名规则去组织class就能得到一套统一、协调的界面。这套约定不仅体现在按钮、表格、导航栏这些基础组件上还包括栅格布局、工具类和JS行为组件。举个例子栅格系统用container、row、col-*三个层级就能切出12列自适应布局弹窗只要写一个按钮加一个>!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleBootstrap示例/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.3/dist/css/bootstrap.min.css relstylesheet /head body h1Hello, Bootstrap!/h1 script srchttps://cdn.jsdelivr.net/npm/bootstrap5.3.3/dist/js/bootstrap.bundle.min.js/script /body /html这里的bootstrap.bundle.min.js是一个打包文件里面已经把Popper.js合并进去了。Popper负责下拉菜单、弹出框这类组件的定位计算少了它下拉菜单在复杂页面上会出现位置偏移。如果只引入bootstrap.min.js而不带Popper点击下拉后菜单虽然能出现但位置可能不对或者干脆不显示。新手最好直接用bundle版本省心很多。2. 栅格系统与常用组件不写自定义样式也能搭出完整页面静态页面搭建是Bootstrap最容易出成果的部分。我第一次用它搭后台界面时整个上午都在感叹原来写一套导航栏加卡片布局连一行自定义CSS都不用写。这节从栅格讲起带大家把一个完整的静态页面搭出来顺带解决“基础组件”怎么串成一个页面的问题。2.1 栅格是怎么把页面“切”出来的Bootstrap的栅格基于flex布局用三个容器层级完成页面切割container负责整体宽度和居中row负责一行里的列布局col负责分配每一列的宽度。栅格把一行分成12份你写col-md-4就是“在中等及以上屏幕里占4份也就是1/3宽度”写col-md-6就是占一半。这里有个值得注意的点断点。Bootstrap把屏幕分成从手机到超宽屏的多个档位分别是默认、sm、md、lg、xl、xxl。col-md-4的意思是在md及以上宽度生效小于md时堆叠成整行。如果你希望手机上也保持三列就写col-4不带断点。这种设计对后台系统开发尤其友好一台笔记本上一个布局推到手机上也不会崩到不能看。实际写页面时容易犯两个错误。一是忘记用row包裹col直接把这些列塞进container导致列间距和换行逻辑都不正常。二是列数加起来超过12Bootstrap不会报错而是自动换行如果你没意识到这一点很容易出现“明明就是三列为什么第四个跑到下一行”的困惑。2.2 一个可直接运行的静态页面示例我把最常用的几个基础组件组合起来导航栏、卡片栅格、按钮、表单控件、页脚构成了一个典型的信息展示页。导航栏用navbar-expand-lg控制折叠断点页面主体用row-cols-1 row-cols-md-3定义“手机一排一个、md以上一排三个”的卡片列表nav classnavbar navbar-expand-lg navbar-light bg-light div classcontainer a classnavbar-brand href#管理后台/a div classcollapse navbar-collapse idmainNav ul classnavbar-nav me-auto li classnav-itema classnav-link active href#首页/a/li li classnav-itema classnav-link href#列表/a/li li classnav-itema classnav-link href#设置/a/li /ul button classbtn btn-primary登录/button /div /div /nav div classcontainer py-4 div classrow row-cols-1 row-cols-md-3 g-4 div classcol div classcard div classcard-body h5 classcard-title成员管理/h5 p classcard-text查看和维护平台账号支持批量操作和权限分配。/p a href# classbtn btn-outline-primary btn-sm进入模块/a /div /div /div div classcol div classcard div classcard-body h5 classcard-title内容发布/h5 p classcard-text管理文章、图片和附件支持草稿与定时发布。/p a href# classbtn btn-outline-primary btn-sm进入模块/a /div /div /div div classcol div classcard div classcard-body h5 classcard-title系统设置/h5 p classcard-text配置站点参数、操作日志和数据备份策略。/p a href# classbtn btn-outline-primary btn-sm进入模块/a /div /div /div /div /div你在浏览器里看到的效果是顶部一条简洁导航主体三张卡片间距均匀屏幕收窄后卡片自动变成一列纵向排列。整个过程不需要自定义样式所有间距用py-4、g-4这类工具类解决代码量比手写CSS少很多而且视觉一致性有保证。2.3 脱离外网把Bootstrap资源本地化CDN方式虽然方便但内网部署、机房离线环境会直接打不开页面。我遇到过生产环境运维把外网禁掉的场景页面整个裸奔排查了半天才发现是CDN被墙了。建议在项目初期就把Bootstrap文件下载到本地放进项目的static或assets目录。去官网首页点下载拿到的是一个压缩包里面包含css、js、字体等目录本地引入时把href和src改为相对路径即可。用npm维护项目的话直接执行npm install bootstrap5然后在构建流程里把node_modules/bootstrap/dist下的文件拷到发布目录。这种方式更适合工程化项目也方便以后升级版本。无论哪种方式都不要在真实项目里长期依赖别人博客里的粘贴路径。3. 下拉菜单是从入门到写后台系统的第一道坎搜“BootStrap下拉菜单”的人特别多因为这个组件看着简单实际用起来出问题的概率极高。我见过不止一个新人粘了官方代码结果点击没有任何反应第一反应是去改CSS折腾半天才发现是JS没引对。所以这一章专门把下拉菜单从原理到排错讲透。3.1 官方下拉菜单的正确使用姿势官方下拉菜单的结构是这样的div classdropdown button classbtn btn-secondary dropdown-toggle typebutton>const dropdownElement document.querySelector(.dropdown .dropdown-toggle); const dropdown bootstrap.Dropdown.getOrCreateInstance(dropdownElement);在新元素渲染完成后找到对应按钮执行一次getOrCreateInstance让它按需初始化。这点在后台系统里特别常见因为很多菜单内容是异步加载的。3.2 用jQuery模拟Bootstrap下拉菜单适合轻量页面的替代法热词里有一条是“jquery 模拟 bootstrap html静态下拉菜单”这说的是一个常见场景项目不需要整套Bootstrap JS或者页面资源非常轻只想靠jQuery做一个类似的展开收起效果。用jQuery模拟官方下拉菜单并不复杂核心是切换一个类名style .dropdown { display: inline-block; } .dropdown-menu { display: none; position: absolute; background: #fff; border: 1px solid #ddd; border-radius: 4px; min-width: 140px; padding: 8px 0; } .dropdown-menu.show { display: block; } .dropdown-item { display: block; padding: 6px 16px; text-decoration: none; color: #333; } .dropdown-item:hover { background: #f8f9fa; } /style div classdropdown button typebutton classbtn btn-primary iddropBtn请选择操作/button div classdropdown-menu iddropMenu a classdropdown-item href#编辑/a a classdropdown-item href#删除/a a classdropdown-item href#导出/a /div /div script srchttps://code.jquery.com/jquery-3.6.0.min.js/script script $(function() { $(#dropBtn).on(click, function(event) { event.stopPropagation(); $(#dropMenu).toggleClass(show); }); $(document).on(click, function() { $(#dropMenu).removeClass(show); }); }); /script这套模拟写法最核心的是两件事一是切换到.show类控制显示隐藏不用inline style二是点击菜单本身时调用stopPropagation()阻止冒泡否则事件会冒泡到document刚点开菜单就立刻被收起。这种方案适合只引入jQuery的老项目或者定制需求多的轻量页面但它没有官方方案里的键盘方向键和屏幕阅读器支持可访问性会差一些。3.3 下拉没反应的经典原因和定位方法下拉菜单点不动90%是JS或版本问题。我整理了一份快速定位清单按照这个顺序查基本能在几分钟内找到原因现象可能原因处理方式点击按钮完全没有反应bundle JS没引入或版本不匹配检查页面是否有bootstrap.bundle.min.js确认版本使用>form classrow g-3 needs-validation novalidate div classcol-md-4 label forusername classform-label用户名/label input typetext classform-control idusername required div classinvalid-feedback请输入用户名。/div /div div classcol-md-4 label foremail classform-label邮箱/label input typeemail classform-control idemail required div classinvalid-feedback请输入正确的邮箱地址。/div /div div classcol-12 button classbtn btn-primary typesubmit提交/button /div /form script (function() { use strict; var forms document.querySelectorAll(.needs-validation); Array.prototype.slice.call(forms).forEach(function(form) { form.addEventListener(submit, function(event) { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } form.classList.add(was-validated); }, false); }); })(); /script这里novalidate的作用很关键它先关掉浏览器的默认气泡再把主动权交给Bootstrap由was-validated类决定什么时候显示红绿反馈。如果去掉novalidate你在Chrome里会看到浏览器自带的“请填写此字段”气泡虽然功能没问题但视觉上很突兀和页面风格不一致。4.2 用少量原生JS完成自定义校验真实项目里的校验规则往往比required复杂比如手机号格式、两次密码一致、身份证号、日期范围。HTML5提供的pattern可以帮你做部分正则校验但跨字段比较就得靠JS。用原生JS配合Bootstrap样式并不复杂也不需要引入大文件function validatePhone(phone) { return /^1[3-9]\d{9}$/.test(phone); } document.getElementById(myForm).addEventListener(submit, function(event) { var phoneInput document.getElementById(phone); var phoneFeedback document.getElementById(phoneFeedback); if (!validatePhone(phoneInput.value.trim())) { phoneInput.classList.add(is-invalid); phoneFeedback.textContent 手机号格式不正确; event.preventDefault(); } else { phoneInput.classList.remove(is-invalid); phoneInput.classList.add(is-valid); } });这里用到了is-invalid和is-valid两个手动类名。和was-validated自动校验不同手动类名更适合服务端返回错误时回显比如后端校验发现用户名重复返回错误信息后前端把对应的输入框标红并显示提示文本。这种方式比清除整个表单再刷新友好很多。4.3 关于表单校验我踩过的几个坑第一个坑是反馈文字放错位置。.invalid-feedback必须作为输入框的同级或父级元素出现且和输入框在同一个.col或.mb-3容器里如果插到容器外面提示文字会跑到form下方甚至页面底部视觉上特别奇怪。第二个坑是disabled输入框在HTML5校验里会被跳过。很多后台系统在某种状态下会把输入框设为disabled但提交时仍需要它的值。这时候不要用disabled要用readonly或者提交前用JS把禁用字段的值放进一个hidden字段里否则后端拿到的是空值。第三个坑是页面有多个form时全局监听写得太宽。我见过有人直接对document绑定submit事件结果一个表单校验失败把另一个表单也标记成了invalid。尽量给每个表单单独绑定或者用document.querySelectorAll(form)做范围隔离。前端校验只是体验优化真正的数据安全防线必须放在后端这个原则要说三遍。5. 和SpringMVC这类后端框架搭配本质是静态资源与页面模板的分工问题热词里有句问话“springmvc 前端可以用bootstrap吗”。这个问题说明有些后端同学对静态资源归属还不太有把握。结论很简单可以而且非常常用。SpringMVC或者其他后端框架负责接收请求、处理业务、返回结果Bootstrap负责让返回的HTML页面变得好用、好看两者完全不相冲突。5.1 SpringMVC前端完全可以用BootstrapSpringMVC返回视图不管是用JSP还是Thymeleaf模板最终都会渲染成包含head和body的HTML。Bootstrap既然只是CSS和JS文件那么只要在这个HTML里通过link和script引入它就能正常工作。你不需要后端为Bootstrap做任何特殊配置它也不依赖Java、Servlet或者Spring的任何能力。有些人的疑问来自“工程化项目一般不都把前后端分离了吗”。其实内部管理系统、中小型项目用后端模板渲染Bootstrap依然是很好的选择。前端同学只管写静态页和组件后端同学把页面片段放进模板双方各管一头高效而且不容易出大问题。5.2 静态资源映射与模板引用的具体配置方式传统SpringMVC项目里静态资源默认会被DispatcherServlet拦截所以必须在Spring配置文件中声明放行。打开spring-mvc.xml加这么一段mvc:resources mapping/static/** location/static/ /这样URL里以/static/开头的请求会直接交给静态资源处理器而不会走到Controller。如果你用Spring Boot路径简单很多把Bootstrap文件放到src/main/resources/static下访问/static/xxx或者根路径下的资源时直接生效。在JSP页面里引入link href${pageContext.request.contextPath}/static/bootstrap/css/bootstrap.min.css relstylesheet script src${pageContext.request.contextPath}/static/bootstrap/js/bootstrap.bundle.min.js/script在Thymeleaf页面里引入link th:href{/static/bootstrap/css/bootstrap.min.css} relstylesheet script th:src{/static/bootstrap/js/bootstrap.bundle.min.js}/script必须用上下文路径或模板表达式拼出完整路径不能写绝对路径。很多人在本地打开没问题一部署到服务器的子目录下就全乱套就是因为写死了/static/...没有带上项目上下文。最初开发时就要养成用相对路径或表达式拼接的习惯。5.3 Ajax、表单提交与后端数据交互怎么串起来后台管理页面里Bootstrap负责界面数据交互通常交给jQuery的$.ajax或者原生fetch。SpringMVC的Controller可以返回一个JSON对象前端拿到后渲染到Bootstrap的表格或卡片里。举个例子用户点击“查询”按钮前端用jQuery把表单序列化提交到/user/list接口后端返回用户数组前端用Bootstrap表格的行模板拼字符串显示。这里的关键是前端不能把Controller返回的整个页面塞进容器而应该只取数据片段。Bootstrap组件在这里没有额外的限制你只需要注意一点Ajax新增的DOM要重新检查是否需要初始化组件实例比如动态生成的日期选择器、下拉框。6. 一些见惯了才说得出口的排错思路和提效习惯Bootstrap用多了就会发现真正难的不是记住每个组件的写法而是遇到问题时能不能快速定位。这一节我把自己排错和提效的习惯列出来算是给前面所有章节做一个补充。6.1 头脑里的“症状表”看到现象直接查根因遇到问题不要先怀疑框架按下面的表去对症状效率会高很多症状最可能的根因查什么页面样式完全乱掉CSS未加载或版本混用网络请求里bootstrap.min.css是否200页面在手机上没自适应缺少viewport标签head里是否有meta viewport宽度看起来不对容器层级错误是否把col直接放在container里下拉/弹窗不出现或错位JS或Popper缺失bundle JS是否引入图标不显示Bootstrap 5的图标库需要单独引入是否引入了bootstrap-icons.css栅格换行位置奇怪一行的列数超过12计算所有col-*的份数总和这张表不能覆盖所有情况但覆盖了八成新人的报错范围。遇到没列出的现象最直接的办法是按F12打开开发者工具看Console的报错信息、看元素实际应用了哪些类名。6.2 快速把Bootstrap变成自己风格的方法Bootstrap默认的蓝色主色调很容易暴露“模板感”。过去要改主题色得用Sass重新编译对多数人来说比较麻烦。Bootstrap 5改用CSS变量后一个项目自定义主题色已经非常轻量了你可以在自己的样式表里直接覆盖:root { --bs-primary: #7c3aed; --bs-primary-rgb: 124, 58, 237; --bs-primary-bg-subtle: #ede9fe; --bs-link-color: #7c3aed; }这样页面里的按钮、链接、选中状态都会跟着变。你还可以通过覆盖--bs-border-radius来统一圆角风格。我用这个方法给很多内部系统换过肤不碰组件源码也能让界面看出不同的品牌感。如果你喜欢定制得更深可以去官方文档的主题定制页面下载Sass源码文件改完变量后编译一份自己的bootstrap.css。不过这套流程需要你对构建工具有一定的了解新手阶段先用CSS变量覆盖就够了。6.3 习惯养成比记忆组件更重要我平日调试Bootstrap页面时习惯把浏览器开发者工具切到设备模拟模式拖动屏幕宽度逐个检查sm、md、lg断点下的布局很多响应式问题还没切回桌面模式就发现了。再利用源码模式阅读bootstrap.css比如看到container的max-width在不同断点是变化的你对“为什么平板和手机上容器宽度不一样”就有直观理解。还有一个小习惯值得分享写页面时先搭结构和组件再微调工具类。不要一开始就在每个元素上堆一堆间距、字体、对齐的类名那样代码很难维护。等布局稳定了再用p-*、m-*、text-*这类工具类统一调细节。可读性和维护成本都会好很多。我从Bootstrap 2时代一直用到5代它在我手上的角色其实一直没变过一套让人能把精力放到业务逻辑上的基础工具。版本在变、组件在更新但“用约定替代重复劳动”的思路始终一致。希望这篇能帮你在入门阶段少走一些我当年绕过的路也欢迎在实践中把你自己发现的坑记录下来那才是成长最快的方式。
返回列表