Rails is a full-stack framework.  It ships with all the tools needed to build amazing web apps on both the front and back end.

在 Rails 6 中有两种不同的工具可以用来管理前端的 CSS、JavaScript 以及 images 等资源,分别是 “Sprockets” 和 “Webpacker”,“Sprockets” 除了 Rails 应用外很少使用,但是 “Webpacker” 不仅在 Rails 中,在其他应用框架中也被广泛的使用。

在 Rails 7 中静态资源的管理已经从 “Webpacker” 改为了 “Import Maps”,用来简化基于 JavaScript 的工具和包管理器 Webpack、Yarn 或者 npm 的使用。

Rails 7 实现了一个愿景,即一种真正的全栈 Web 开发方式,可以同时应对前端和后端的挑战。

Rails 7.0: Fulffilling a vision

本文将使用 Ruby 3.0 和 Rails 7.0 环境,创建 Rails 7 应用,并在该应用中引入 Bootstrap 5。

第一种方式:仅引入 Bootstrap

创建 Rails 项目

首先使用 rails new 命令创建 Rails 项目:

如果创建项目的时间过长,可以考虑更换 Ruby 的 Gem 源,使用 RubyChina 的源:

# 删除国外源,使用 RubyChina 的源

gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/

安装 importmap

项目创建完成后进入项目目录,首先检查config目录下是否包含 “importmap.rb” 文件,如果没有可以通过 rails importmap 命令来安装:

cat config/importmap.rb



# 安装 importmap
rails importmap:install

添加 Bootstrap 5

# 在项目目录下执行 importmap 命令,安装 Bootstrap 5

bin/importmap pin bootstrap

该命令执行后会在 config/importmap.rb 中添加命令行中打印出的内容。

在安装 importmap 是创建的 app/javascript/application.js 文件中导入 ”bootstrap“:

// ...



// 新增内容

import 'bootstrap'



// ...

安装 Bootstrap 5 Ruby Gem

在 Gemfile 中新增如下内容:

# Gemfile

# ...

gem 'bootstrap', '~> 5.2.3'

# ...

保存后执行 bundle install 命令安装依赖。

application.scss 中导入 bootstrap

app/assets/stylesheets/application.css 重命名为app/assets/stylesheets/application.scss

# 更改文件后缀为 scss

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

增加内容:

// app/assets/stylesheets/application.scss

// ...



@import "bootstrap";



// ...

测试 Bootstrap

使用 rails g controller 命令创建一个 home_controller 以及 index 函数:

参考 Bootstrap Navabar 的样式修改 app/views/layouts/application.html.erb 页面

<!DOCTYPE html>

<html>

  <head>

    <title>RailsBootstrap</title>

    <meta name="viewport" content="width=device-width,initial-scale=1">

    <%= csrf\_meta\_tags %>

    <%= csp\_meta\_tag %>



    <%= stylesheet\_link\_tag "application", "data-turbo-track": "reload" %>

    <%= javascript\_importmap\_tags %>

  </head>



  <body>

    <header>

      <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">

        <div class="container-fluid">

          <a class="navbar-brand" href="#">Top navbar</a>

          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">

            <span class="navbar-toggler-icon"></span>

          </button>

          <div class="collapse navbar-collapse" id="navbarCollapse">

            <ul class="navbar-nav me-auto mb-2 mb-md-0">

              <li class="nav-item">

                <a class="nav-link active" aria-current="page" href="#">Home</a>

              </li>

              <li class="nav-item">

                <a class="nav-link" href="#">Link</a>

              </li>

              <li class="nav-item">

                <a class="nav-link disabled">Disabled</a>

              </li>

            </ul>

            <form class="d-flex" role="search">

              <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">

              <button class="btn btn-outline-success" type="submit">Search</button>

            </form>

          </div>

        </div>

      </nav>

    </header>

    <%= yield %>

  </body>

</html>

修改 app/views/home/index.html.erb 页面:

<div class="bg-light p-5 rounded">

  <h1>Navbar example</h1>

  <p class="lead">This example is a quick exercise to illustrate how the top-aligned navbar works. As you scroll, this navbar remains in its original position and moves with the rest of the page.</p>

  <a class="btn btn-lg btn-primary" href="/docs/components/navbar/" role="button">View navbar docs &raquo;</a>

</div>

通过 rails s 命令启动 Rails 应用,在浏览器中输入 localhost:3000/home/index 查看 index 页面

出现该页面说明引入的 Bootstrap 已经生效。

第二种方式:引入 Bootstrap 和 jQuery

添加 Bootstrap 和 jQuery 的 Ruby Gem

创建一个新的项目 rails-bootstrap-jquery,之后再项目的 Gemfile 中添加如下 Ruby Gems:

gem "sassc-rails"

gem "bootstrap", "~> 5.2.3"

gem "jquery-rails"

执行 bundle install 命令。

接着执行如下命令,修改application.css 后缀,创建 custom.css 以及 my_script.js 文件:

# 修改 application.css 为 application.scss

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

# 创建 custom.scss

touch app/assets/stylesheets/custom.scss

# 创建 my\_script.js

touch app/javascript/my\_script.js

在 application.scss 中添加如下内容:

@import "bootstrap";

@import "custom";

config/environments/development.rb 文件中添加如下内容:

config.sass.inline\_source\_maps = true

删除 tmp 文件夹下的缓存:

$ rm -r tmp/cache/assets

config/importmap.rb 文件中添加如下内容:

# From "jquery-rails" gem

pin "jquery", to: "jquery3.min.js", preload: true

pin "jquery\_ujs", to: "jquery\_ujs.js", preload: true



# From "bootstrap" gem

pin "bootstrap", to: "bootstrap.min.js", preload: true

pin "@popperjs/core", to: "popper.js", preload: true



# Custom JS

pin "my\_script", to: "my\_script.js", preload: true

config/initializers/assets.rb 中添加内容:

Rails.application.config.assets.precompile += %w( jquery3.min.js jquery\_ujs.js bootstrap.min.js popper.js )

app/javascript/application.js 中使用 import 关键字导入:

import "jquery";

import "jquery\_ujs";

import "@popperjs/core";

import "bootstrap";

import "my\_script";

app/javascript/my\_script.js 中添加如下 JS 代码:

document.addEventListener("turbo:load", () => {

  const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');

  const popoverList = [...popoverTriggerList].map((popoverTriggerEl) => new bootstrap.Popover(popoverTriggerEl));

});

app/assets/stylesheets/custom.scss 中添加如下样式:

.body-offset {

  margin-top: 50px;

}

测试 Bootstrap

创建一个 home_controller.rb

rails g controller home index

修改 app/views/layouts/application.html.erb Layout 布局:

<!DOCTYPE html>

<html>

  <head>

    <title>RailsBootstrapJquery</title>

    <meta name="viewport" content="width=device-width,initial-scale=1">

    <%= csrf\_meta\_tags %>

    <%= csp\_meta\_tag %>



    <%= stylesheet\_link\_tag "application", "data-turbo-track": "reload" %>

    <%= javascript\_importmap\_tags %>

  </head>



  <body>

    <header>

      <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">

        <div class="container-fluid">

          <a class="navbar-brand" href="#">Top navbar</a>

          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">

            <span class="navbar-toggler-icon"></span>

          </button>

          <div class="collapse navbar-collapse" id="navbarCollapse">

            <ul class="navbar-nav me-auto mb-2 mb-md-0">

              <li class="nav-item">

                <a class="nav-link active" aria-current="page" href="#">Home</a>

              </li>

              <li class="nav-item">

                <a class="nav-link" href="#">Link</a>

              </li>

              <li class="nav-item">

                <a class="nav-link disabled">Disabled</a>

              </li>

            </ul>

            <form class="d-flex" role="search">

              <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">

              <button class="btn btn-outline-success" type="submit">Search</button>

            </form>

          </div>

        </div>

      </nav>

    </header>

    <%= yield %>

  </body>

</html>

修改 app/views/home/index.html.erb 页面:

<div class="container">

  <div class="row">

    <div class="col-md-8 mt-4">

      <button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover" data-bs-title="Popover title" data-bs-content="And here's some amazing content. It's very engaging. Right?">Click to toggle popover</button>

    </div>

  </div>



  <div class="row mt-4">

    <div class="col-md-8">

      <button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="Top popover">

        Popover on top

      </button>

      <button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="right" data-bs-content="Right popover">

        Popover on right

      </button>

      <button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="bottom" data-bs-content="Bottom popover">

        Popover on bottom

      </button>

      <button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="left" data-bs-content="Left popover">

        Popover on left

      </button>

    </div>

  </div>

</div>

使用 rails s 启动 Rails 项目,查看 http://0.0.0.0:3000/home/index