Rails is great. You can still check DHH’s 15-minute blog demo and just appreciate the combination of features and ease of use of the framework. Even though there are even easier ways to make a blog nowadays (cough cough Gatsby), Rails is still a rock-solid choice for crafting digital products.

Rails很棒。 您仍然可以查看DHH的15分钟博客演示,并欣赏功能的组合以及该框架的易用性。 尽管如今有更简单的方法来制作博客(咳嗽咳嗽盖茨比),Rails仍然是制作数字产品的坚实选择。

Rails和现代网络 (Rails and the modern web)

Despite being a Rails advocate, I mainly used it to build APIs, resorting to React for the frontend stuff. Working with some demanding web designers, I felt that React always had a flexibility that I did not get with HTML and JS. Also, having worked on very large codebases for both full Rails with HTML and Javascript (jQuery at the time) and Rails with a SPA on top, the latter always evolved better in terms of technical debt and time needed to implement features.

尽管是Rails的拥护者,但我主要还是用它来构建API,并使用React来处理前端。 与一些苛刻的网页设计师一起工作,我感到React始终具有HTML和JS所没有的灵活性。 另外,在处理具有HTML和Javascript的完整Rails(当时为jQuery)以及在顶部具有SPA的Rails的超大型代码库上,后者在技术负担和实现功能所需的时间方面总是发展得更好。

There are pros and cons to each approach and we like to analyze those on a per-project basis. More and more we see great success on teams going with fully server-side approaches, like Basecamp for example. Github is another case of a mostly server-side Rails app that works great (most of the time).

每种方法都各有利弊,我们希望根据每个项目进行分析。 我们越来越看到在采用完全服务器端方法的团队中取得巨大成功,例如Basecamp。 Github是另一个主要在服务器端使用Rails应用程序的案例,该应用程序运行良好(大部分时间)。

Even with a fully server-side approach, you can create experiences that for end-users, will be indistinguishable from a fully client-side web app. The instant page transitions, components, and cool APIs to interact with the DOM. Let’s see how.

即使采用完全服务器端的方法,您也可以为最终用户创建与完全客户端Web应用程序没有区别的体验。 即时页面转换,组件和出色的API可以与DOM交互。 让我们看看如何。

Turbolinks和远程助手 (Turbolinks and remote helpers)

The big deal about SPAs is on its name. single page applications don’t have page transitions, it’s usually done in the client-side and it looks and feels like a native app. An issue with server-side approaches is that every page change (clicking links) triggers a server request and a subsequent full page load. But you can fix that with Turbolinks.

关于SPA的大事就是它的名字。 单页应用程序没有页面转换,通常是在客户端完成的,外观看起来像本机应用程序。 服务器端方法的一个问题是,每次页面更改(单击链接)都会触发服务器请求以及随后的整个页面加载。 但是您可以使用Turbolinks修复该问题。

Perhaps you’ve heard about it, Turbolinks has been on the Rails ecosystem for quite a while. It basically is a piece of Javascript that detects navigation requests, like clicking a link, and then fetches the page and merges the head and swaps the body. Thus, changing a page without a full refresh.

也许您已经听说过,Turbolinks已经在Rails生态系统中存在了很长时间。 它基本上是一段Javascript,它检测导航请求,例如单击链接,然后获取页面并合并head并交换body 。 因此,无需完全刷新即可更改页面。

How do you install it? Simple, you don’t, Rails comes with it installed and set up by default! For non-Rails projects it’s pretty simple, just check out their documentation

如何安装? 很简单,您不必这么做,Rails随附了它的默认安装和设置! 对于非Rails项目,这非常简单,只需查看其文档即可

Not only Rails has default Turbolinks support, but it also has a ton of small features to assist in implementing instant user interactions. You can submit forms and trigger controller actions with links and buttons with AJAX and no extra code. Their doc page about working with Javascript has a pretty detailed explanation of these “remote elements”.

Rails不仅具有默认的Turbolinks支持,而且还具有大量的小功能,可帮助实现即时的用户交互。 您可以使用AJAX提交表单并使用链接和按钮触发控制器操作,而无需额外的代码。 他们关于使用Javascript的文档页面对这些“远程元素”进行了非常详细的解释。

The only thing that is not handled by default in Rails and Turbolinks is when a controller action renders a new template. Turbolinks supports redirect_to out of the box, but when a template is rendered using render, it causes a full page refresh. This often happens after you submit a form. Even if you are using the form_with helper, which triggers an AJAX request instead of the traditional form submission, you will still re-render the page.

在Rails和Turbolinks中,默认情况下唯一未处理的是控制器操作呈现新模板时。 Turbolinks开箱即用地支持redirect_to ,但是当使用render模板时,它会刷新整个页面。 提交表单后通常会发生这种情况。 即使您使用的是form_with帮助程序,它会触发AJAX请求而不是传统的表单提交,您仍将重新呈现页面。

Take for instance, this create controller action:


def create
@schedule = Schedule.new(schedule_params)
@statuses = User.statuses.keys
if @schedule.valid?
redirect_to(new_schedule_path, notice: "Scheduled!")
flash.now[:alert] = "Invalid!"
render action: "new"

By default, Turbolinks handles the case where the form is valid because we use redirect_to. But when the form is invalid, we will have a full page refresh, because we are re-rendering with the render method.

默认情况下,Turbolinks处理表单有效的情况,因为我们使用redirect_to 。 但是,当表单无效时,我们将刷新整页,因为我们正在使用render方法重新渲染。

Solution? Just install this handy gem made by Jorge Manrubia. That’s it. Right now, at the time of writing, Turbolinks version 5 doesn’t support this, but the upcoming version 6 will handle this with no extra setup.

解? 只需安装 Jorge Manrubia制成的方便的宝石 。 而已。 目前,在撰写本文时,Turbolinks版本5不支持此功能,但是即将发布的版本6将无需额外设置即可处理此问题。

组件 (Components)

Right now, developers and designers on the web design and implement UI with a component-based approach. It’s easier to organize and re-use code this way. It’s also the way design systems are usually maintained. And now with design systems becoming more and more popular, components even feel like the way to make web applications.

现在,开发人员和设计人员可以通过基于组件的方法在Web上设计和实现UI。 通过这种方式组织和重用代码更加容易。 这也是设计系统通常的维护方式。 现在通过设计系统越来越受欢迎,甚至组件感觉,使Web应用程序的方式

The traditional SPA assumes this model, usually, and it’s pretty easy to do it. However, on Rails, there is no notion of this, and you are on your own to implement your components. The most popular way is just to use CSS. Naming schemes like SuitCSS and BEM allow you to define reusable CSS classes for your project. But what happens when you also need markup or behavior?

传统的SPA通常采用这种模型,并且很容易做到。 但是,在Rails上没有这个概念,您可以自己实现组件。 最受欢迎的方法就是使用CSS。 SuitCSS和BEM等命名方案使您可以为项目定义可重用CSS类。 但是,当您还需要标记或行为时会发生什么呢?

You can always implement your components using view partials, and your behavior as regular Javascript files, but that isn’t ideal. What if there was a way to encapsulate CSS, JS, and HTML on a nice package, and re-use that throughout your app?

您始终可以使用视图局部视图来实现组件,并且可以将行为作为常规Javascript文件来实现,但这并不理想。 如果有一种方法可以将CSS,JS和HTML封装在一个不错的程序包中,然后在整个应用程序中重复使用,该怎么办?

Introducing view_component, made by Github. Using this library, you can define all of your components on app/components.

介绍view_component ,由Github制作。 使用此库,您可以在app/components上定义所有app/components

A component is a combination of a Ruby class, an ERB template, a CSS file, and a JS file. You can control server-side behavior on that Ruby class, define your markup on your ERB template and then control the frontend side of things with CSS and JS.

组件是Ruby类,ERB模板,CSS文件和JS文件的组合。 您可以控制该Ruby类的服务器端行为,在ERB模板上定义标记,然后使用CSS和JS控制事物的前端。

As an example, I’ll show you the implementation of a flash alert component, that we can use to render Rails flash notices and errors.

作为示例,我将向您展示一个Flash Alert组件的实现,我们可以使用它来呈现Rails的Flash通知和错误。

First, we need to define the Ruby class that renders the component. In its basic form, it’s just a regular constructor that you use to set instance variables, kinda like a controller.

首先,我们需要定义呈现组件的Ruby类。 在其基本形式中,它只是用于设置实例变量的常规构造函数,有点像控制器。

The render? method can be used to determine if we should render the markup of the component. In this case, we don't render anything at all if we don't have any flash messages.

render? 方法可用于确定是否应渲染组件的标记。 在这种情况下,如果没有任何Flash消息,我们将根本不渲染任何内容。



class FlashMessageComponent < ViewComponent::Base
def initialize(flash:)
@flash = flash
def render?

Then we just define markup like we would for a regular Rails view. You can use any template engine you like ( slim for example). I prefer traditional ERB.

然后,我们就像定义常规的Rails视图一样定义标记。 您可以使用任何喜欢的模板引擎(例如slim )。 我更喜欢传统的ERB。



<% @flash.each do |key, value| %>
<div class="Flash Flash-<%= key %>">
<%= value %>
<% end %>

Some basic styling:




.Flash {
position: fixed;
top: 12px;
left: 50%;
display: inline;
padding: 0.5rem;
color: white;
transform: translateX(-50%);
.Flash-notice {
background-color: cadetblue;
.Flash-alert {
background-color: red;

And that’s all it takes to define a component. To use it, we just reference it like this:

这就是定义组件所需的全部。 要使用它,我们只需要像这样引用它:

<%= render(FlashMessageComponent.new(flash: flash)) %>

You can do this for all sorts of UI elements and it allows you to encapsulate UI logic on these re-usable bits. Now, you probably noticed that we don’t have any JS here. Will touch that in a bit, let’s just talk about Stimulus first.

您可以对所有类型的UI元素执行此操作,并且可以将UI逻辑封装在这些可重复使用的位上。 现在,您可能已经注意到我们这里没有任何JS。 稍后,我们先谈谈刺激。

刺激物 (Stimulus)

Now, our previous example was pretty nice, but we lack JS to make the flash component behave. When I first started working with Rails (back in 2014) the defacto way was to just use jQuery to make stuff all interactive and nice. And we all know how that ends up with large codebases.

现在,我们前面的示例相当不错,但是我们缺少JS来使Flash组件表现良好 。 当我刚开始使用Rails时(早在2014年),事实上的方法是仅使用jQuery使所有内容交互且美观。 我们都知道大型代码库的结局。

Image for post
A dish of spaghetti with meatballs

Spaghetti code get it? Yes, it’s a lame joke.

意大利面条代码了吗? 是的,这是一个la脚的笑话。

The folks at Basecamp, who pretty much invented Rails anyway, created this simple library (not a framework) to solve the mess of having a bunch of random Javascript with jQuery all over the place. Stimulus uses simple data attributes and small Javascript controllers to add rich interactions to Rails apps (also works without Rails).

无论如何,Basecamp的人们还是发明了Rails,他们创建了这个简单的库(不是框架)来解决随处可见的一堆带有jQuery的随机Javascript的麻烦。 Stimulus使用简单的数据属性和小型Javascript控制器向Rails应用程序添加了丰富的交互功能(也可以在没有Rails的情况下使用)。

I’m not going super in-depth on Stimulus, their docs are pretty thorough and easy to pick up. Still, let’s see how we can combine Stimulus with view_component to make our flash component nicer. Currently, when a flash message is rendered, it stays there forever, so lets us make it go away after some time and also add a button to dismiss it.

我不会对Stimulus进行超级深入的研究,他们的文档非常详尽并且易于掌握。 仍然,让我们看看如何将Stimulusview_component结合使用以使Flash组件更好。 当前,当呈现即时消息时,它会一直停留在那儿,因此让我们在一段时间后使其消失,并添加一个按钮将其关闭。

Refer to Stimulus docs to set it up. Then refer to this part of the view_component docs to integrate both libraries. By default Stimulus just searches for controllers on the app/javascript/controllers path. With these integrations, we can also define controllers on the app/components folder.

请参考Stimulus文档进行设置。 然后参考view_component文档的这一部分来集成两个库。 默认情况下,Stimulus仅在app/javascript/controllers路径上搜索app/javascript/controllers 。 通过这些集成,我们还可以在app/components文件夹中定义控制器。

Now, back to our flash component. Let’s change our old markup a little bit. We need to add the button and the respective Stimulus related attributes.

现在,回到我们的Flash组件。 让我们稍微更改一下旧标记。 我们需要添加按钮和与刺激相关的属性。



<div data-controller="flash-message">
<% @flash.each do |key, value| %>
<div class="Flash Flash-<%= key %>">
<%= value %>
<button data-action="click->flash-message#close">Close</button>
<% end %>

We added the data-controller attribute, to tie this markup with its respective Stimulus controller. Then we added a button with a data-action attribute. We specify click->flash-message#close, which means we are mapping the click event with the close function of our flash-message Stimulus controller.

我们添加了data-controller属性,以将该标记与其各自的Stimulus控制器绑定在一起。 然后,我们添加了一个带有data-action属性的按钮。 我们指定click->flash-message#close ,这意味着我们正在将click事件与flash-message Stimulus控制器的close函数进行映射。

The naming of these files matters a lot, like most Rails stuff. The flash_message_controller defines the flash_message controller. If you want to use it on your markup, you must reference it that way, always. And yes you can use it in different places on your HTML. Warning, controllers on app/components can have name collisions with components on app/javascript/controllers.

这些文件的命名非常重要,就像大多数Rails一样。 flash_message_controller定义flash_message控制器。 如果要在标记中使用它,则必须始终以这种方式引用它。 是的,您可以在HTML的不同位置使用它。 警告app/components上的控制器可能与app/javascript/controllers上的app/components发生名称冲突。

Now, the controller:




import { Controller } from "stimulus";
export default class extends Controller {
connect = () => {
this.timeout = setTimeout(() => this.close(), 5000);
disconnect = () => {
close = () => {

Stimulus controllers have a bunch of lifecycle related functions, you can check those on the docs, but here we are redefining connect and disconnect. We use those to set a timeout to close the flash message and also to clear that message if the controller is unloaded. Just a nice touch.

刺激控制器具有许多与生命周期相关的功能,您可以在文档中进行检查,但是这里我们重新定义了connectdisconnect 。 我们使用它们来设置超时以关闭Flash消息,如果控制器已卸载,也将清除该消息。 只是一个很好的接触。

Then, we get to our close function that we use on the markup. Every Stimulus controller has access to the respective DOM element where it's being used. That data-controller attribute takes care of that. When we use this.element we are reaching for that DOM piece, then we can just call .remove to delete that from the DOM, making the flash message go away.

然后,我们进入在标记中使用的close函数。 每个Stimulus控制器都可以访问使用它的相应DOM元素。 该data-controller属性负责这一点。 当我们使用this.element到达那个DOM片段时,我们可以调用.remove从DOM中删除它,从而使flash消息消失。

一个有趣的选择 (A fun alternative)

Remember, pick technologies that make your team happy and productive! In our case, everyone loves React and everyone loves Rails, but it’s refreshing to just go full Rails once in a while and break out of the norm. If you are interested in solving problems more on the server-side, keep tuned, as we are going to talk about StimulusReflex for Rails and also LiveView for Phoenix.

请记住,选择使您的团队快乐和高效的技术! 在我们的案例中,每个人都喜欢React,每个人都喜欢Rails,但是偶尔进行一次完整的Rails打破常规是令人耳目一新的。 如果您有兴趣在服务器端更多地解决问题,请继续关注,因为我们将谈论StimulusReflex for Rails和LiveView for Phoenix。

The modern web is great because you have a ton of choices on how to do things, let’s cheers on that.


翻译自: https://medium.com/finiam/spicing-up-your-rails-frontend-experience-fb692ef2bf39


  • 0
  • 0
  • 0


  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助




当前余额3.43前往充值 >
领取后你会自动成为博主和红包主的粉丝 规则
钱包余额 0