如何使用Webpacker在Rails 6中使用Bootstrap、jQuery和其他库

56 阅读5分钟

从Rails 6开始,Webpacker已经取代了旧的资产管道(sprockets)来处理javascript的编译和减化。

Webpacker是一个宝石,它是webpack.js的包装器,webpack.js处理javascript代码的捆绑,而webpacker让我们在Rails应用中与webpack对接。本文不会深入讨论webpacker的工作原理,如果你想了解更多关于webpacker的工作原理,我推荐Prathamesh关于理解webpacker的文章。GoRails也有一个关于这个主题的精彩截屏

本文假设你正在启动一个新的Rails 6项目,并想使用Bootstrap。

安装和使用Bootstrap和jQuery

首先,打开终端,输入以下命令来安装Bootstrap、jQuery和popper.js(某些Bootstrap组件需要):

yarn add bootstrap jquery popper.js

接下来,我们将在app/javascript中创建一个名为stylesheets的文件夹(全路径为app/javascript/stylesheets),其行为类似于app/assets/stylesheets,只是这里的样式表文件通常与javascript模块绑定在一起。(例如:bootstrap模块将其CSS捆绑在一起)。

在app/javascript/stylesheets文件夹中,创建一个文件并命名为 "application.scss"(类似于assets)。然后在这个文件中导入bootstrap的CSS(来自javascript模块):

/* app/javascript/stylesheets/application.scss */
@import "~bootstrap/scss/bootstrap";
/* this will import the scss file inside node_modules/bootstrap/scss/bootstrap.scss

可选的步骤--我理解你可能觉得把样式表文件放在javascript文件夹里面不舒服。如果你喜欢在assets文件夹中组织所有的样式表,你可以在app/assets/stylesheets/application.scss中导入bootstrap(如果它还是.css,则重命名为.scss)来代替:

/* app/assets/stylesheets/application.scss */

@import '../../../node_modules/bootstrap/scss/bootstrap';

然后在app/javascript/packs/application.js中,导入bootstrap和它捆绑的CSS,这样webpack就会加载这些依赖关系:

// app/javascript/packs/application.js

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

// import the bootstrap javascript module
import "bootstrap"

// import the application.scss we created for the bootstrap CSS (if you are not using assets stylesheet)
import "../stylesheets/application"

import css

接下来,我们要在布局文件(app/views/layouts/application.html.erb)中添加styleheet_pack_tag,引用app/javascript/stylesheets/application.scss文件。所以布局可以在那里导入样式表:





  
    Bootstraper
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    
    
    <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  
....

由于Bootstrap需要jQuery和popper.js来运作,我们需要使用ProvidePlugin来使jQuery和popper.js模块对其他模块(即本例中的bootstrap)可用。

在现代模块化的javascript开发中,模块通常不能访问其他模块中的变量,除非你手动导入它们,这是为了鼓励编写独立的模块,使其具有良好的兼容性,不依赖隐藏的依赖关系(例如globals):

isolated modules

通过使用 ProvidePlugin 来进行修整,我们可以向其他模块 "提供 "变量。

ProvidePlugin使得一个包在每个通过webpack编译的模块中作为一个变量可用。如果webpack看到该变量被使用,它将在最终的捆绑中包含给定的包。

provide plugin

打开config/webpack/environment.js,然后添加provide插件,并提供"$"、"jQuery"和 "Popper"变量。

// config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require("webpack");

// Add an additional plugin of your choosing : ProvidePlugin
environment.plugins.append(
  "Provide",
  new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    Popper: ["popper.js", "default"] // for Bootstrap 4
  })
);

module.exports = environment

你可能需要重新启动你的rails服务器,以使environment.js中的变化得到更新。

现在你应该可以在你的视图代码中使用Bootstrap了!

在视图代码中使用jQuery

在这一点上,如果你试图在你的布局代码中使用jQuery(例如:html.erb或html.haml),你会得到一个错误 "Uncaught ReferenceError, $ is not defined" :

uncaught reference error

这是因为我们试图在全局范围内使用"",但"",但""在全局范围内不可用。ProvidePlugin 只让其他模块使用它,而不是在全局范围。

not on global

为了解决这个问题,我们可以把"$"和 "jQuery "变量附加到全局范围(或窗口范围,因为这是在网络浏览器中使用最多的范围)。在包文件**(app/javascript/packs/application.js**)中,将它们附加到全局范围。

// app/javascript/packs/application.js

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

import "bootstrap"
import "../stylesheets/application"

var jQuery = require('jquery')

// include jQuery in global and window scope (so you can access it globally)
// in your web browser, when you type $('.div'), it is actually refering to global.$('.div')
global.$ = global.jQuery = jQuery;
window.$ = window.jQuery = jQuery;

使用一个需要jQuery的库

在这个例子中,我将使用DateRangePicker库,因为我已经在许多网络应用中使用它来选择日期。这个库需要jQuery和moment.js才能发挥作用。

首先,用yarn安装moment.js和daterangepicker,因为它们可以在yarn上找到。

yarn add moment daterangepicker

接下来我们将在全局范围内包含moment,因为DateRangePicker可能需要它,打开pack文件app/javascript/packs/application.js,添加require moment并将其绑定到全局范围。

不要忘记导入daterangepicker,并确保它是bootstrap和jQuery之后导入。

// app/javascript/packs/application.js

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

import "bootstrap"
import "../stylesheets/application"

var moment = require('moment')

var jQuery = require('jquery')

import "daterangepicker"

// include jQuery in global and window scope (so you can access it globally)
// in your web browser, when you type $('.div'), it is actually refering to global.$('.div')
global.$ = global.jQuery = jQuery;
window.$ = window.jQuery = jQuery;

// include moment in global and window scope (so you can access it globally)
global.moment = moment;
window.moment = moment;

由于DateRangePicker带有自己的css,我们需要在样式表包文件中导入它。(app/javascript/stylesheets/application.scss)

/* app/javascript/stylesheets/application.scss */

@import "~bootstrap/scss/bootstrap";

@import "../../../node_modules/daterangepicker/daterangepicker.css";

由于某些原因,使用"~"对.css文件不起作用,所以我们不能使用@import "~daterangepicker/daterangepicker.css" 。所以我们不得不使用".../.../.../node_modules "从/app/javascript/stylesheets到/node_modules这三个文件夹进行导航。

如果节点模块有.scss文件,你可以用前面的"~"符号导入,因为它意味着到node_modules文件夹的路径。

现在在你的视图代码中,你可以设置一个输入为daterangepicker,像这样。

Page#index
Find me in app/views/page/index.html.erb
 


使用npm上没有的自定义javascript库

如果你想使用的javascript库在NPM上不可用怎么办?有一些库只能以独立的.js文件的形式提供,你如何在webpacker中安装和使用这些库呢?

在这个例子中,我将使用select2库,它有一个gem(select2-rails),但是我们要下载它的.js和.css,并将它包含在我们的Rails应用程序中。我们可以在这里下载发布代码:github.com/select2/sel… "dist "文件夹,然后找到select2.min.cssselect2.min.js

通常我会为自定义的javascript库创建一个文件夹,并把它放在app/javascript文件夹内,在这个例子中,app/javascript/select2,然后像这样把css和js文件放在里面。

js css location

然后在你的应用程序包文件**(app/javascript/packs/application.js**)中,像这样导入select2.min.js文件。

// app/javascript/packs/application.js

import "../select2/select2.min"

我们可以省略".js "文件扩展名,确保路径正确。

接下来,我们需要导入我们之前创建的样式表包文件中的css文件**(app/javascript/stylesheets/application.scss)**

/* app/javascript/stylesheets/application.scss */
@import "../select2/select2.min.css"

现在我们可以在视图代码中使用select2功能了。