Ruby-on-Rails中ViewComponents教程和实例

256 阅读3分钟

什么是ViewComponent,为什么要使用它们?

一个ViewComponent只是一个Ruby对象和一个模板。你应该使用ViewComponents有五个主要原因:

  1. 单一的责任:与其将视图相关的逻辑分散在项目中,不如将模板所需的所有逻辑整合到一个单一的类中。
  2. 易于测试:与传统的Rails模板不同,ViewComponents可以进行单元测试。另一个好处是,这些单元测试通常比类似的控制器测试快一百倍以上。
  3. 明确的数据流:由于ViewComponents使用标准的Ruby初始化器,渲染模板所需的数据是明确知道的。这与传统的Rails模板相反,后者有一个隐含的接口。
  4. 改进性能:平均而言,ViewComponents的速度大约是partials的10倍。
  5. 提高代码质量:由于ViewComponents只是普通的Ruby对象,它们很容易执行代码质量标准。

ViewComponents是不断重复使用的模板的理想选择,也可以直接进行测试。如果你有大量的嵌入Ruby的参数或模板,它们可能会成为好的ViewComponents。

一个典型的用例是导航栏,例如在Bootstrap和Rails应用程序中。

现在你知道了什么是ViewComponents以及为什么要使用它们,现在是时候自己实现它了

创建一个新的Rails应用程序

$> ruby -v  
ruby 3.1.2p20 # Please use Ruby 3! 
$> rails -v  
Rails 7.0.2.4 # And Rails 7 to keep things fresh
$> bundle -v  
Bundler version 2.3.14 # Bundler 2.xx

现在进入你的工作区,输入

rails new -G myapp # -G so it doesn't initialize a git repository
cd myapp

现在在gemfile中,添加

gem "view_component"

然后运行这个命令来完成设置

bundle install

实现你的第一个ViewComponent

就像你可以做 "rails generate controller/model",你也可以做rails generate component!组件生成器接受一个组件名称和一个参数列表。

bin/rails generate component Example title
# Which will result in
create  app/components/example_component.rb
invoke  test_unit
create    test/components/example_component_test.rb
invoke  erb
create    app/components/example_component.html.erb
# example_component.rb should look like this
class ExampleComponent < ViewComponent::Base
  def initialize(title:)
    @title = title
  end
end
  • 注意在example_component.rb中,exampleComponent继承自ViewComponent::Base。为了使其与Rails模型/控制器更加相似,你可以建立一个继承于ViewComponent::Base的ApplicationComponent类,然后让你所有的组件都继承于ApplicationComponent。
  • 确保以它们所呈现的内容来命名组件,而不是它们所接受的参数。
  • 记住ViewComponent是什么;一个Ruby对象和一个模板。看看它是如何实现的:一个Ruby文件和一个具有相同基本名称的相应模板文件。
  • 组件被保存在app/components中
  • 组件类的名字应该总是以Component结尾

现在,覆盖原本在模板文件(example_component.html.erb)中的内容

echo '<span title="<%= @title %>"><%= content %></span>' > app/components/example_component.html.erb
  • @title取自组件类
  • 请注意内容访问器,它一会儿就会派上用场

创建一个控制器

# inside app/controllers/home_controller.rb  
class HomeController < ApplicationController  
end  

配置一个默认路由

# inside config/routes.rb  
Rails.application.routes.draw do  
  get "home/index"  
  root to: "home#index"  
end  

现在创建一个主页文件夹和一个主页#index的视图

mkdir app/views/home
echo '<%= render(ExampleComponent.new(title: "my title")) do %>' > app/views/home/index.html.erb 
echo '  Hello, World!' >> app/views/home/index.html.erb 
echo '<% end %>' >> app/views/home/index.html.erb 
  • 注意当渲染一个组件时,它只是一个普通的Ruby初始化器,你可以只看一下类,看看需要传递什么。作为一个块传递给ViewComponent的内容被捕获并分配给内容访问器。

现在,运行

bin/rails server

现在你应该可以通过在浏览器中粘贴http://127.0.0.1:3000,看到这个视图!

image.png

这就是你在视图中渲染ViewComponent的方法。你也可以在控制器中直接渲染ViewComponents,方法如下

render(ExampleComponent.new(title: "My Title")) { "Hello, World!" }

我如何对一个组件进行单元测试?

请注意,当你先前生成一个组件时,它也创建了一个组件测试文件。把这个文件粘贴到`test/components/example_component_test.rb中。

require "test_helper"

class ExampleComponentTest < ViewComponent::TestCase
  def test_render_component
    render_inline(ExampleComponent.new(title: "my title")) { "Hello, World!" }

    assert_selector("span[title='my title']", text: "Hello, World!")
    # or, to just assert against the text:
    assert_text("Hello, World!")
  end
end

然后简单地运行

bin/rails test

你看,控制器根本就没有参与进来!"。

生产就绪的Ruby-on-Rails示例

有时候,仅仅阅读代码既是一种很好的学习方式,也是一种放松的活动。让我们来探索GitHub上railsdevs的源代码

这里是app/components/conversations/read_indicator_component.html.erb

<% return unless render? %>
<% if show_read? %>
  <div class="flex justify-end my-3 mr-4">
    <div class="flex items-center justify-center space-x-2 text-sm">
      <%= inline_svg_tag "icons/solid/check.svg", class: "h-5 w-5 text-green-600" %>
      <p class="text-gray-500"><%= t(".read") %></p>
    </div>
  </div>
<% else %>
  <div class="flex justify-end my-3 mr-4">
    <div class="flex items-center justify-center space-x-2 text-sm">
      <p class="text-gray-500"><%= t(".unread") %></p>
    </div>
  </div>
<% end %>

而这是app/components/conversations/read_indicator_component.rb

module Conversations
  class ReadIndicatorComponent < ApplicationComponent
    def initialize(user, conversation:)
      @user = user
      @conversation = conversation
    end

    def render?
      @conversation.latest_message&.sender?(@user)
    end

    def show_read?
      @conversation.latest_message_read_by_other_recipient?(@user)
    end
  end
end

有什么你觉得奇怪的地方吗?Google一下,或者克隆repo,然后把代码库洗劫一空(在本地!),直到你发现事情是如何运作的,这是提高技能的绝佳方法。

总结

你可以看到,渲染组件所需的所有逻辑都被封装在一个单一的类中。你可以很容易地知道哪些东西必须被传入组件中才能被正确渲染。最后,你能够对ViewComponents进行单元测试,这使得编写无错误的代码变得非常容易。所有这些原因加在一起,足以让你从现在开始在所有的Rails项目中使用ViewComponents。