文章 文章 posts

Nuxt.js 集成 Bootstrap 5 完整方案

本文档详细介绍如何在 Nuxt.js 项目中集成 Bootstrap 5,包括主题定制、CSS / SCSS 变量覆盖、JS 组件使用等内容。

一、项目环境检查

当前项目依赖

依赖 版本 用途
bootstrap ^5.3.8 Bootstrap 核心库
sass 1.70.0 SCSS 预处理器
nuxt ^4.2.1 Nuxt.js 框架

二、基础集成配置

1. 安装依赖

如果项目中尚未安装 Bootstrap 和 Sass,可以通过以下命令安装:

bash
# 使用 npm
npm install bootstrap sass

# 使用 yarn
yarn add bootstrap sass

# 使用 pnpm
pnpm add bootstrap sass

2. 配置全局 CSS

在 nuxt.config.ts 中配置全局 SCSS 文件:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  css: ['~/assets/sass/style.scss'],
  // 其他配置...
})

3. 创建 Bootstrap 主样式文件

在 app/assets/sass/ 目录下创建 style.scss 文件,并导入 Bootstrap SCSS 模块:

scss
// style.scss

// Bootstrap 核心导入栈
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";

// 在这里可以覆盖 Bootstrap 变量
// 例如:$primary: #0074d9;

// 继续导入其他 Bootstrap 模块
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/images";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/dropdown";
@import "bootstrap/scss/button-group";
@import "bootstrap/scss/nav";
@import "bootstrap/scss/navbar";
@import "bootstrap/scss/card";
@import "bootstrap/scss/accordion";
@import "bootstrap/scss/breadcrumb";
@import "bootstrap/scss/pagination";
@import "bootstrap/scss/badge";
@import "bootstrap/scss/alert";
@import "bootstrap/scss/progress";
@import "bootstrap/scss/list-group";
@import "bootstrap/scss/close";
@import "bootstrap/scss/toasts";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/tooltip";
@import "bootstrap/scss/popover";
@import "bootstrap/scss/carousel";
@import "bootstrap/scss/spinners";
@import "bootstrap/scss/offcanvas";
@import "bootstrap/scss/placeholders";
@import "bootstrap/scss/helpers";
@import "bootstrap/scss/utilities/api";

三、主题定制

1. 覆盖 Bootstrap 变量

在 style.scss 文件中,在导入 bootstrap/scss/variables 之后,可以覆盖任何 Bootstrap 变量来自定义主题:

scss
// 覆盖主题颜色
$primary: #0074d9;
$danger: #dc3545;
$secondary: #6c757d;
$success: #198754;
$warning: #ffc107;
$info: #0dcaf0;
$light: #f8f9fa;
$dark: #212529;

// 自定义主题颜色映射
$theme-colors: (
    "primary": $primary,
    "danger": $danger,
    "secondary": $secondary,
    "success": $success,
    "warning": $warning,
    "info": $info,
    "light": $light,
    "dark": $dark
);

// 移除不需要的颜色
$theme-colors: map-remove($theme-colors, "info", "light", "dark");

2. 自定义排版

scss
// 自定义字体
$font-family-sans-serif: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

// 自定义字体大小
$font-size-base: 1rem; // 16px
$font-size-lg: 1.25rem;
$font-size-sm: 0.875rem;

3. 自定义间距

scss
// 自定义间距变量
$spacer: 1rem;
$spacers: (
  0: 0,
  1: $spacer * 0.25,
  2: $spacer * 0.5,
  3: $spacer,
  4: $spacer * 1.5,
  5: $spacer * 3,
  6: $spacer * 4,
  7: $spacer * 5
);

四、Bootstrap JS 组件集成

1. 创建 Bootstrap 客户端插件

在 app/plugins/ 目录下创建 bootstrap.client.js 文件:

javascript
// bootstrap.client.js
import Modal from "bootstrap/js/dist/modal";
import Collapse from "bootstrap/js/dist/collapse";
import Dropdown from "bootstrap/js/dist/dropdown";
import Tooltip from "bootstrap/js/dist/tooltip";
import Popover from "bootstrap/js/dist/popover";

// 可以根据需要导入其他 Bootstrap JS 组件
// import Carousel from "bootstrap/js/dist/carousel";
// import Alert from "bootstrap/js/dist/alert";
// import Toast from "bootstrap/js/dist/toast";

// 导出插件
export default defineNuxtPlugin(() => {
  return {
    provide: {
      bs: {
        Modal,
        Collapse,
        Dropdown,
        Tooltip,
        Popover,
        // Carousel,
        // Alert,
        // Toast
      },
    },
  };
});

2. 在组件中使用 JS 组件

使用 v-bind:ref 和 onMounted

vue
<template>
  <div>
    <!-- 按钮触发模态框 -->
    <button type="button" class="btn btn-primary" id="exampleModalToggle" @click="showModal">
      打开模态框
    </button>

    <!-- 模态框 -->
    <div ref="modalRef" class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">模态框标题</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">
            模态框内容
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
            <button type="button" class="btn btn-primary">保存更改</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
// 导入必要的函数
const { ref, onMounted, onUnmounted } = Vue;
const { $bs } = useNuxtApp();

// 模态框引用
const modalRef = ref(null);
let modalInstance = null;

// 显示模态框
const showModal = () => {
  if (modalInstance) {
    modalInstance.show();
  }
};

// 组件挂载时初始化模态框
onMounted(() => {
  if (modalRef.value) {
    modalInstance = new $bs.Modal(modalRef.value);
  }
});

// 组件卸载时销毁模态框实例
onUnmounted(() => {
  if (modalInstance) {
    modalInstance.dispose();
  }
});
</script>

使用工具类组件

vue
<template>
  <div>
    <!-- 按钮组 -->
    <div class="btn-group" role="group" aria-label="Basic mixed styles example">
      <button type="button" class="btn btn-danger">Danger</button>
      <button type="button" class="btn btn-warning">Warning</button>
      <button type="button" class="btn btn-success">Success</button>
    </div>

    <!-- 卡片 -->
    <div class="card" style="width: 18rem;">
      <img src="..." class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">Card title</h5>
        <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
      </div>
    </div>
  </div>
</template>

五、响应式设计

Bootstrap 内置了强大的响应式设计系统,可以通过断点类来控制不同屏幕尺寸下的元素显示:

vue
<template>
  <div>
    <!-- 响应式布局 -->
    <div class="container">
      <div class="row">
        <!-- 在移动设备上占满宽度,在平板上占6列,在桌面设备上占4列 -->
        <div class="col-12 col-md-6 col-lg-4">
          <!-- 内容 -->
        </div>
        <div class="col-12 col-md-6 col-lg-4">
          <!-- 内容 -->
        </div>
        <div class="col-12 col-md-6 col-lg-4">
          <!-- 内容 -->
        </div>
      </div>
    </div>

    <!-- 响应式显示/隐藏 -->
    <div class="d-none d-md-block">
      <!-- 仅在中等及以上屏幕显示 -->
    </div>
    <div class="d-block d-sm-none">
      <!-- 仅在移动设备显示 -->
    </div>
  </div>
</template>

六、最佳实践

1. 按需导入组件

在 bootstrap.client.js 中,只导入项目中实际需要的 Bootstrap JS 组件,以减小打包体积:

javascript
// 只导入需要的组件
import Modal from "bootstrap/js/dist/modal";
import Collapse from "bootstrap/js/dist/collapse";
// 不需要的组件不要导入
// import Carousel from "bootstrap/js/dist/carousel";

2. 使用自定义主题

通过覆盖 Bootstrap 变量来创建符合项目需求的自定义主题,而不是直接修改 Bootstrap 源文件:

scss
// 正确:覆盖变量
$primary: #0074d9;
$border-radius: 8px;

// 错误:直接修改组件样式
.btn {
  border-radius: 8px !important;
}

3. 避免过度使用 !important

尽量通过正确的 CSS 选择器优先级来覆盖样式,避免过度使用 !important:

scss
// 正确:使用更具体的选择器
.my-custom-button {
  border-radius: 8px;
}

// 错误:过度使用 !important
.btn {
  border-radius: 8px !important;
}

4. 使用组合式 API

在 Nuxt 3 中,推荐使用组合式 API 来管理 Bootstrap 组件的生命周期:

vue
<script setup>
const { ref, onMounted, onUnmounted } = Vue;
const { $bs } = useNuxtApp();

const tooltipRef = ref(null);
let tooltipInstance = null;

onMounted(() => {
  tooltipInstance = new $bs.Tooltip(tooltipRef.value);
});

onUnmounted(() => {
  tooltipInstance.dispose();
});
</script>

5. 结合 Nuxt 特性

将 Bootstrap 与 Nuxt 的特性结合使用,如布局系统、路由等:

vue
<!-- 在布局组件中使用 Bootstrap 导航 -->
<template>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
      <NuxtLink class="navbar-brand" to="/">Logo</NuxtLink>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
          <li class="nav-item">
            <NuxtLink class="nav-link active" aria-current="page" to="/">首页</NuxtLink>
          </li>
          <li class="nav-item">
            <NuxtLink class="nav-link" to="/about">关于我们</NuxtLink>
          </li>
          <li class="nav-item">
            <NuxtLink class="nav-link" to="/services">服务</NuxtLink>
          </li>
          <li class="nav-item">
            <NuxtLink class="nav-link" to="/contact">联系我们</NuxtLink>
          </li>
        </ul>
      </div>
    </div>
  </nav>
  <main>
    <slot />
  </main>
</template>

七、常见问题与解决方案

1. Bootstrap JS 组件不工作

  • 确保已正确创建并配置 bootstrap.client.js 插件
  • 确保在客户端环境中使用 JS 组件(process.client)
  • 确保组件引用(ref)正确绑定

2. 自定义变量不生效

  • 确保变量覆盖在 @import "bootstrap/scss/variables" 之后
  • 确保变量覆盖在其他 Bootstrap SCSS 模块导入之前
  • 检查变量名称是否与 Bootstrap 定义的一致

3. 样式冲突

  • 使用更具体的 CSS 选择器
  • 考虑使用 CSS 模块或作用域样式
  • 避免直接修改 Bootstrap 源文件

以上就是 Nuxt.js 集成 Bootstrap 5 的完整方案。通过合理配置和使用 Bootstrap,可以快速构建美观、响应式的 Web 应用。