从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"
接下来,我们要在布局文件(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):
通过使用 ProvidePlugin 来进行修整,我们可以向其他模块 "提供 "变量。
该
ProvidePlugin
使得一个包在每个通过webpack编译的模块中作为一个变量可用。如果webpack看到该变量被使用,它将在最终的捆绑中包含给定的包。
打开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" :
这是因为我们试图在全局范围内使用""在全局范围内不可用。ProvidePlugin 只让其他模块使用它,而不是在全局范围。
为了解决这个问题,我们可以把"$"和 "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.css和select2.min.js。
通常我会为自定义的javascript库创建一个文件夹,并把它放在app/javascript文件夹内,在这个例子中,app/javascript/select2,然后像这样把css和js文件放在里面。
然后在你的应用程序包文件**(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功能了。