在这篇文章中我们会介绍 WebpackerYarnSprockets
.

如何在rails 6中使用js_经验分享

 

和以往的版本不同,6之前的js脚本存放目录是app/assets/javascripts,但是到了6,官方推荐的js脚本存放位置是app/javascript目录

在这篇文章中,我会一步一步地介绍如何将bootstrap和FontAwesome 5加入到rails 6项目中

NPM

这个没啥好介绍的,他就是node.js的包管理工具,就像python的pip,ruby的gem和php的composer

npm install bootstrap

npm会自动将下载下来的包存放到node_modules目录中,并在package.json中维护一个列表

{
  "name": "demo",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.2.2",
    "turbolinks": "^5.2.0",
	"markdown": "0.5.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3.10.3"
  }
}

Yarn

yarn也是js的包管理工具,但是它比npm更强大

yarn会根据package.json文件中的内容自动更新我们所需要的包,你可以在yarn.lock文件中锁定我们的包版本,这样yarn就不会自动更新他们了

在rails 6中,如果我们需要一个js库,我们只需要执行yarn add package_name即可

ES6

js的新标准,他提供了一系列新的特性,这里暂且不表

Babel

因为大部分浏览器都还不支持ES6,所以我们需要Babel将ES6编译成ES5,Babel就是这个负责转换ES6到ES5的工具

Webpack

现在我们有了Babel和Yarn和他们的配置文件,现在我们需要一个工具使得这些东西能够自动执行和配置,以此让我们更在专注于代码的编写

Webpack将会帮助我们编译assets,webpack会把他们编译成适当的插件,然后这些插件又会被我们的程序使用进行各种处理工作

以下是webpack可以帮我们做到的:

  • 处理ES6代码
  • 使用babel-loader插件使得Babel自动将ES6代码转换为ES5代码
  • 将输出结果打包到一个我们可以将其包含到html dom中的js文件如下所示:
<script type="text/javascript" src="path-to-es5-javascript-pack.js"></script>
Webpacker

注意不要将其和Webpack混淆

Webpacker是一个gem,他将webpack包含进我们的rails应用中。它自身带有一些初始配置(足够我们起步),这样我们就可以不用操心配置的问题,直接开始编写实际的代码

Webpacker的默认配置:

  • app/javascript/packs目录应该包含你的js代码
  • 你可以在你的视图中使用如下方式包含js代码javascrit_pack_tag '<pack_name>'

举个例子:

<%= javascript_pack_tag 'my_app' %>

对应的html代码就是:src="app/javascript/packs/my_app.js")

在文章的最后我将会讲解所有的这些到底是如何工作的,不过在此之前我们先讲一下Sprockets

有的读者肯能会问,既然js已经转移到了app/javascript目录,那么css文件呢?

这一点我们可以通过查看config/webpacker.yml文件来解答:

# Extract and emit a css file
extract_css: false

通过注释我们可以看到,webpacker是可以做到将css也放在新的位置而不是app/assets/stylesheets中,而且webpacker也提供了对应的引用标签stylesheet_pack_tag,但是我们有看到extract_css的值是false,可以得知默认css的提取是关闭的,所以我们依然可以使用Sprockets,使用旧目录app/assets/stylesheets

另外一点值得注意的地方是,你可能会认为在运行rails assets:precompile时,rails只会预编译app/assets中的文件,但是实际上rails会将app/javascrit和Sprockets的app/assets中的文件都进行预编译

Sprockets 4

正如Webpack,Sprockets也是一个资源管道,这意味着她也是负责处理资源文件(js、css、images、字体文件等等),将它们转换为目标格式

在rails 6中webpack(er)代替了Sprockets成为了在rails应用中编写js的新标准。尽管如此,Sprockets仍然是css文件的默认处理方式

在rails 5,Sprockets 3中,我们需要将assets清单添加到config.assets.precompile文件中

而在rails 6,Sprockets 4中,我们则需要将assets清单写到app/assets/config/manifest.js

如果你想向Sprockets添加资源,你要完成以下事情

  • 讲你的css文件放到app/assets/stylesheets/目录下
  • 确保你的app/assets/config/manifest.jsstylesheet_link_tag或者link_treelink_directorylink语句是可用的
  • 使stylesheet_link_tag将其引入到你的视图中<%= stylesheet_link_tag 'my_makeup' %>

当然,也有其他的引入方式

不要以使用Sprockets的方式使用Webpacker

理解下面的内容对你来说将是很重要的,它可以帮你省去好几个小时的时间

在编译模块这方面,Webpack和Sprockets是有很大的差异的

在rails 6中,你所编写的js都被模块化了,存放在不同的命名空间下,不能直接被全局访问,你需要先导入模块,然后方可调用相应的方法,看一下下面的例子你就明白了

这是使用Sprockets的写法
app/assets/javascripts/hello.js

内容如下:

function hello(name) {
  console.log("Hello " + name + "!");
}

app/assets/javascripts/user_greeting.js
内容如下

function greet_user(last_name, first_name) {
  hello(last_name + " " + first_name);
}

视图app/views/my_controller/index.html.erb内容如下:

<%= javascript_link_tag 'hello' %>
<%= javascript_link_tag 'user_greeting' %>
<button onclick="greet_user('Dire', 'Straits')">Hey!</button>

上面的例子很好理解,没有什么特殊的地方

但是在Webpacker中怎么写呢

如果你认为只需要直接将上面两个js文件移动到app/javascript/packs那你就大错特错了

因为hello()会被编译成ES6 模块,user_greeting()也一样,尽管包含这两个文件的相应的编译后的js文件你被包含了进来,其实hello方法也是不存在的

那么我们应该怎么做呢?

app/javascript/packs/hello.js:

export function hello(name) {
  console.log("Hello " + name + "!");
}

app/javascript/packs/user_greeting.js:

import { hello } from './hello';
function greet_user(last_name, first_name) {
  hello(last_name + " " + first_name);
}

app/views/my_controller/index.html.erb:

<%= javascript_pack_tag 'user_greeting' %>
<button onclick="greet_user('Dire', 'Straits')">Hey!</button>

这样就行了吗?

大师是否定的,因为greet_user是不可被访问到的,一旦被编译,它就被隐藏到了一个模块中

最终我们找到了这一章节的重点:

  • Sprockets:视图可以直接和js文件进行交互,比如调用js中的方法和变量等等等
  • webpack:视图没办法直接访问js

那么我们到底该怎么做呢?

请看下面的

我们使用jquery举例子:

import $ from 'jquery';
import { hello } from './hello';
function greet_user(last_name, first_name) {
  hello(last_name + " " + first_name);
}
$(document).ready(function() {
  $('button#greet-user-button').on(
    'click',
    function() {
      greet_user('Dire', 'Strait');
    }
  );
});
/* Or the ES6 version for this: */
$(() =>
  $('button#greet-user-button').on('click', () => greet_user('Dire', 'Strait'))
);

app/views/my_controller/index.html.erb:

<%= javascript_pack_tag 'user_greeting' %>
<button id="greet-user-button">Hey!</button>

我们使用<%= javascript_pack_tag 'user_greeting' %>引入app/javascript/packs/user_greeting.js
可以看到我们是监听事件,而不是直接在视图中调用js中的function

更多细节参考原文链接