Rails 在 Rails 6 中使用 jQuery 和 Bootstrap

Chorder · 2020年02月27日 · 最后由 u1435638317 回复于 2020年03月01日 · 5314 次阅读

0x00 前言

Rails 6.0 发布已经有一段时间了,之前没有什么项目可以练手,最近总算遇到可以使用 Rails 6 开发的场景了。Rails 6 中新增了一些功能和特性,有一些内容官方文档里并没有写的很具体,所以在摸索的过程中也遇到了一些坑点。这里以一个新工程从头到尾的构建,来简单记录在 Rails6 中使用 jQuery 和 Bootstrap 的过程。希望能够给新来的同学做个参考。

编程和码字的水平有限,如有错漏敬请指教,也请多包涵!下面就开始吧。

0x01 环境准备

  • 操作系统: Debian 10 ( 4.19.0-8-amd64 )
  • Ruby 版本: ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
  • Rails 版本: Rails 6.0.2.1
test@debian:~$uname -a
Linux debian 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 GNU/Linux
test@debian:~$ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
test@debian:~$rails -v
Rails 6.0.2.1

0x02 创建项目

这里新建一个项目 “TestApp”,并在其中建立控制器 “Test” 和测试方法 “test”:

test@debian:~$rails new TestApp
      create  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  Gemfile
...
├─ ws@6.2.1
├─ yargs-parser@11.1.1
└─ yargs@12.0.5
Done in 59.74s.
Webpacker successfully installed 🎉 🍰
test@debian:~/TestApp$rails g controller Test test --no-stylesheets
Running via Spring preloader in process 22413
      create  app/controllers/test_controller.rb
       route  get 'test/test'
      invoke  erb
      create    app/views/test
      create    app/views/test/test.html.erb
      invoke  test_unit
      create    test/controllers/test_controller_test.rb
      invoke  helper
      create    app/helpers/test_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    scss
test@debian:~$

创建好之后,修改 test 页面,添加一个表单和按钮,用于稍后测试 Bootstrap。(在执行创建控制器和方法的命令后,Rails 已经为我们自动添加了到 Test 控制器 test 方法的路由,所以不需要我们再新增路由。)

app/views/test/test.html.erb 代码:

<h1>Test#test</h1>
<p>Find me in app/views/test/test.html.erb</p>

<form>
<input id="test" type="text"/>
<button id="test_btn">点我</button>
</form>

启动 Rails 服务,访问http://localhost:3000/test/test

test@debian:~/TestApp$rails s
=> Booting Puma
=> Rails 6.0.2.1 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.6.5-p114), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop

此时应用已经创建成功。

0x03 添加 jQuery 和 Bootstrap 库

这里采用 yarn 作为 Javascript 包管理器。在 TestApp 目录中运行bin/yarn,如果出现以下内容,说明 yarn 没有安装:

test@debian:~/TestApp$bin/yarn
Yarn executable was not detected in the system.
Download Yarn at https://yarnpkg.com/en/docs/install

安装方法在这里, 以当前使用的 Debian 10 为例,安装的方法是:

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

安装好 yarn 之后,就可以用 yarn 添加 jQuery 包和 Bootstrap 包:

test@debian:~/TestApp$yarn add jquery 
yarn add v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.11: The platform "linux" is incompatible with this module.
info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > webpack-dev-server@3.10.3" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > webpack-dev-middleware@3.7.2" has unmet peer dependency "webpack@^4.0.0".
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ jquery@3.4.1
info All dependencies
└─ jquery@3.4.1
Done in 9.98s.
test@debian:~/TestApp$yarn add bootstrap
yarn add v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.11: The platform "linux" is incompatible with this module.
info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > webpack-dev-server@3.10.3" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > webpack-dev-middleware@3.7.2" has unmet peer dependency "webpack@^4.0.0".
warning " > bootstrap@4.4.1" has unmet peer dependency "popper.js@^1.16.0".
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ bootstrap@4.4.1
info All dependencies
└─ bootstrap@4.4.1
Done in 13.33s.

由于 Bootstrap 还需要基于popper.js,不装的话会报依赖错误,所以安装一下:

test@debian:~/TestApp$yarn add popper.js
yarn add v1.21.1
[1/4] Resolving packages...
warning popper.js@1.16.1: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
[2/4] Fetching packages...
info fsevents@1.2.11: The platform "linux" is incompatible with this module.
info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > webpack-dev-server@3.10.3" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > webpack-dev-middleware@3.7.2" has unmet peer dependency "webpack@^4.0.0".
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ popper.js@1.16.1
info All dependencies
└─ popper.js@1.16.1
Done in 11.78s.

安装的执行结果:

因为众所周知的原因,网速慢的同学就尴尬了,这里可能要等很久,所以你需要选一个科学一点的方式。

此时查看TestApp/node_modules目录,所需的前端库已经添加好了:

test@debian:~/TestApp$ls node_modules/jquery/
AUTHORS.txt  bower.json  dist  external  LICENSE.txt  package.json  README.md  src
test@debian:~/TestApp$ls node_modules/bootstrap/
dist  js  LICENSE  package.json  README.md  scss
test@debian:~/TestApp$ls node_modules/popper.js/
dist  index.d.ts  index.js.flow  package.json  README.md  src
test@debian:~/TestApp$

0x04 使用 Bootstrap

跟 Rails5 有所不同的是,Rails6 采用 webpack 打包的方式,把需要打包的资源统一放在一个文件里(Rails6 之前是采用 Gemfile 的方式引入第三方库,再通过 Asset Pipeline 汇聚)。

于是在安装好所需的前端库之后,就需要在app/javascript/packs/application.js中先引用它:

以 Bootstrap 为例,在其中添加import 'bootstrap'引入语句:

与此同时还需要在application.scss中引入 Bootstrap 的 CSS 样式表:

app/assets/stylesheets/application.scss文件中添加@import "bootstrap/dist/css/bootstrap";

2020/05/28 Patch

纠个错,如果想要通过@import "bootstrap/dist/css/bootstrap";的方式引用 bootstrap 相关的 CSS,需要把app/assets/stylesheets/application.css文件重命名为app/assets/stylesheets/application.scss。 如果直接使用app/assets/stylesheets/application.css的话,那么采用如下方式来引用:

/*
 * File: app/assets/stylesheets/application.css 
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require bootstrap/dist/css/bootstrap
 *= require_tree .
 *= require_self
 */

Ending Patch

接着继续修改app/views/test/test.html.erb文件中的代码,添加相应的 CSS 类来测试 Bootstrap 是否已经引入成功:

<h1>Test#test</h1>
<p>Find me in app/views/test/test.html.erb</p>

<form>
    <div class="form-group">
        <input id="test" type="text" class="form-control"/>
        <button id="test_btn" class="btn btn-success">点我</button>
    </div>
</form>

启动应用,再次访问测试页面:

虽然界面有点丑,但是从渲染效果来看,Bootstrap 已经成功引入了。

0x05 使用 jQuery

继续修改app/javascript/packs/application.js,添加用于测试 jQuery 的代码:

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import 'bootstrap'

$(document).on('turbolinks:load', function(){
    $("#test_btn").click(function(){
        alert($("#test").val());
    });
});

保存并运行,刷新https://localhost:3000/test/test页面,发现添加的代码无法执行,控制台中有报错:

ReferenceError: $ is not defined 
application.js:21

这是因为 jQuery 库也需要引入一下,但是引入的方式有些不同,并不是在页面中直接引入,而是需要在config/webpack/environment.js中添加以下引入代码:

var webpack = require('webpack');
environment.plugins.append( 'Provide',
    new webpack.ProvidePlugin({
        $: 'jquery',
    })
)

通过声明一个全局的导出,就可以在全局的 JS 代码文件中使用"$"符号了。

刷新并再次访问,代码能够运行,控制台也没有再报错,说明 jQuery 已经成功集成:

另一种方法

还有一种方法是通过在 JS 文件按中使用import $ from 'jquery'来使"$"符生效,但是这样比较繁琐,需要在每个 JS 文件中出现,所以不再介绍了,当然,也提供一下详细的代码参考

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import 'bootstrap'
import $ from 'jquery' // 在每个JS文件中加上这一行,也可以替代声明全局变量。

$(document).on('turbolinks:load', function(){
    $("#test_btn").click(function(){
        alert($("#test").val());
    });
});

0x06 总结

在网上主要参考了这篇文章。Rails 6 中的变化还是挺大的,不知道 webpack 会不会真的成为 Rails 的未来,感觉之前使用 coffee script 来写 JS 的同学不是很多,但是我倒是已经非常习惯于使用它来开发前端功能了。

但是想必 Rails 的先驱们有着他们更成熟的考虑吧,那就一起继续探索好了。


本文同步发表在我的博客: 在 Rails6 中使用 jQuery 和 bootstrap( Using jQuery and Bootstrap on Rails 6 )

很详细的教程。我个人目前也采用着默认的打包策略,按我理解这种情况下,Javascript 走的是 webpacker 的编译,CSS 走的是 sprocket 的编译。

//= link_tree ../images
//= link_directory ../stylesheets .css

然而 bootstrap 是通过 npm 包来安装而不是通过 gem 包,有时候我觉着要不把资源统一用 webpacker 来打包会不会更好? https://github.com/rails/webpacker/blob/master/docs/css.md。 理论上只需要把 css 在 application.js 文件中 import 进去,然后把extract_css设置成 false 即可。

lanzhiheng 回复

统一用 webpacker 来打包,从处理流程来看,显得更加一体化,不失为一个好方法。每个人最终都会找到自己觉得最舒服的方式,怎么快乐就怎么用。😀

前端的 JS 和 CSS 一直都用的 webpack 打包,和 rails 完全分离开了,好处是升级不用管 rails,相对容易些。

以前也用 coffee,自从用 typescript 重写了 coffee 以后,就离不开 TS 了!

你遇到了 Bootstrap 中的 madal 方法不能使用的问题吗?帮看一下 /topics/39550 这个问题吧。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册