手记

如何使用 Rails 8 和 Turbo 8 构建一个 Twitter 复刻版

Hotwire、StimulusJS 和 Turbo 已经问世了将近一半的十年,而就在过去的一年里,Turbo 广播和流模式也经历了重大变化。所有这些功能都包含在 Rails 8 中,并且还有其他新特性,例如 SolidQueue、SolidCache 和 SolidCable,使开发变得更加迅速。其中的两个功能使得快速搭建这样的克隆应用变得非常简单!

注意:当我快速制作一些东西时,我会使用Bootstrap。你可以在views/layout/application.html.erb中添加一个资产标签,或者在运行rails new命令之前安装yarn,然后在rails --new命令中添加Bootstrap。此外,我使用--main标志从Github的主分支安装Rails,以获取尚未发布但已接近完成的Rails 8。

不啰嗦,让我们开始一个新的应用:

    rails new blabber --main --css=boostrap

进入全屏模式 退出全屏模式

Rails 会完成以下操作:
安装应用程序。
安装 gems。
处理 Bootstrap 安装。
安装 yarn 包。

Hotwire 和 Turbo 现在已经是 Rails 的一部分了,所以无需安装任何额外的东西!只需运行 bin/dev 并开始编写代码。

现在,让我们创建一个模型来保存克隆推文的数据,在这个名为 Blabber 的推特克隆中。所有基本属性:

    rails g model Post username body:text likes_count:integer repost_count:integer

进入全屏模式 退出全屏模式

为了使这与之前的文章保持相似,我们将在Post模型中添加相同的验证:

    class Post < ApplicationRecord
     turbo_refreshes_with

     validates :body, length: { minimum: 1, maximum: 280 }
    end

进入全屏模式 退出全屏模式

一个区别是 broadcasts_refreshes 这一行。这一行是 Turbo 8 的一部分,后端的变更(这里是指模型的变更)会广播为 Turbo 的 refresh 事件,由 turbo.js 前端接收。幕后隐藏的是 Rails 所做的所有工作,以连接浏览器,使其能够接收特定模型实例的更新,以及所有相关的连接部分,如 ActiveJob、ActionCable、SolidQueue 和 SolidCable。

我们将对生成的迁移文件进行一些微小的调整,以添加一些数据库级别的默认值(并允许我们在 Post 创建时保持代码简单)。

    class CreatePosts < ActiveRecord::Migration[6.0]
      def change
     create_table :posts do |t|
     t.string :username, default: 'Blabby'
     t.text :body
     t.integer :likes_count, default: 0
     t.integer :repost_count, default: 0

     t.timestamps
        end
      end
    end

进入全屏模式 退出全屏模式

好的!现在,我们可以运行这个迁移了!

    rails db:migrate

进入全屏模式 退出全屏模式

另一个变化是,在这个 refreshes 和流的过程中,我们的控制器现在可以不了解这些流,不需要根据格式来响应或拥有与应用程序非动态部分使用的相同片段非常相似的模板文件。

    class PostsController < ApplicationController

      def index
        @posts = Post.all.order(created_at: :desc)
        @post = Post.new
      end

      def create
        @post = Post.new(post_params)
        respond_to do |format|
          if @post.save
            redirect_to posts_path
          else
            render :index
          end
        end
      end

      def like
        Post.find_by(id: params[:post_id]).increment(:likes_count).save
        redirect_to posts_path
      end

      def repost
        Post.find_by(id: params[:post_id]).increment(:repost_count).save
        redirect_to posts_path
      end

      private

      def post_params
        params.require(:post).permit(:body)
      end
    end

进入全屏模式 退出全屏模式

简单的控制器。index 动作返回一个帖子列表给 @postcreate 使用 StrongParameters 创建一个新的帖子,并重定向回 index 模板。likerepost 类似,只是它们会分别增加相应的计数列。

让我们为这些控制器动作设置几个路由。是的,这些路由并不完全是 RESTful 的,但 1) 它们可以工作。2) 这是一个 10 分钟的教程。3) 在我的小应用程序中,POST 请求并没有让其他浏览器窗口更新变化。(如果有人知道原因,我很乐意听听!)

    Rails.application.routes.draw do
     resources :posts, only: %i[index create] do
     get 'like'
     get 'repost'
      end

     root to: 'posts#index'
    end

进入全屏模式 退出全屏模式

有了模型、控制器和路由,我们现在可以组合一些视图模板。接下来,我们可以向应用程序布局中添加“魔法调料”,以监听 refresh 广播。

    <!DOCTYPE html>
    <html>
      <head>
        <title><%= content_for(:title) || "Blabber" %></title>
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="mobile-web-app-capable" content="yes" />
        <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= turbo_refreshes_with method:
        :replace, scroll: :preserve %> <%= yield :head %> <%# 启用 PWA manifest 以支持可安装的应用程序(确保在 config/routes.rb 中也启用) %> <%#=
        tag.link rel: "manifest", href: pwa_manifest_path %>

        <link rel="icon" href="/icon.png" type="image/png" />
        <link rel="icon" href="/icon.svg" type="image/svg+xml" />
        <link rel="apple-touch-icon" href="/icon.png" />

        <%# 包含 app/views/stylesheets 目录下的所有样式表文件 %> <%=
        stylesheet_link_tag :app, "data-turbo-track": "reload" %> <%=
        javascript_importmap_tags %> <%= stylesheet_link_tag "application",
        "data-turbo-track": "reload" %>
      </head>

      <body>
        <%= yield %>
      </body>
    </html>

进入全屏模式 退出全屏模式

The yield :headrails new 命令生成,并在其上方我们添加了 <%= turbo_refreshes_with method: :replace, scroll: :preserve %> 以告知应用程序监听 refreshes

第一个视图模板是 app/views/posts/index.html.erb

    <div class="container">
      <h1>Blabber</h1>
      <h4>一个 Rails、Hotwire 演示</h4>

      <%= render partial: 'form' %> <%= turbo_stream_from 'posts' %> <%= render
      @posts %>
    </div>

进入全屏模式 退出全屏模式

视图是一个相对简单的Rails模板。它使用render @posts方法来渲染@posts集合,使用的是posts/_post片段。turbo_stream_from:posts将视图层与Post模型的广播连接起来。

这个部分是一些标准的单个对象标记,加入了一些 Bootstrap 类来让它看起来还算不错:

app/views/posts/_posts.html.erb

    <%= turbo_stream_from post %>
    <div class="card mb-2" id="<%= dom_id(post) %>">
      <div class="card-body">
        <h5 class="card-title text-muted">
          <small class="float-right"> 发布于 <%= post.created_at %> </small>
          <%= post.username %>
        </h5>
        <div class="card-text lead mb-2"><%= post.body %></div>
        <%= link_to post_repost_path(post), { class: 'card-link', data: {
        turbo_prefetch: false }} do %> 转发 (<%= post.repost_count %>) <% end %>
        <%= link_to post_like_path(post), { class: 'card-link', data: {
        turbo_prefetch: false }} do %> 点赞 (<%= post.likes_count %>) <% end %>
      </div>
    </div>

进入全屏模式 退出全屏模式

另一个 turbo_stream 命令用于监听广播的更新和删除操作,而 dom_id 辅助函数用于创建传统的 Rails 视图 ID model_resource-id。在这种情况下,它会看起来像 posts_1 等。

接下来是一个简单的 Rails 表单:

    <%= form_with model: @post, id: dom_id(@post), html: {class: 'my-4' } do |f| %>
    <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= pluralize(@post.errors.count, "错误") %> 阻止了此帖子的保存:
      </h2>

      <ul>
        <% @post.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
    <% end %>
    <div class="form-group">
      <%= f.text_area :body, placeholder: '输入你的吐槽', class: 'form-control',
      rows: 3 %>
    </div>

    <div class="actions"><%= f.submit class: "btn btn-primary" %></div>
    <% end %>

进入全屏模式 退出全屏模式

如果你还记得 index.html.erb,另一个框架包裹了表单的渲染调用。因此,我们包含了 dom_id 以允许控制器的 create 动作启用 Turbo 管理流的标记。

现在我们将回到控制器,深入讨论创建动作。

    def 创建
        @post = Post.new(post_params)
        respond_to do |format|
          if @post.save
            redirect_to posts_path
          else
            render :index
          end
        end
      end

进入全屏模式 退出全屏模式

之前,在这一点上,我们会讨论如何在标记中返回流以追加到顶级框架,但现在 Turbo 8 的刷新和广播功能可以处理这一切,无需任何额外的更改。

此时,你应该有一个可以在两个浏览器中打开的应用程序,它应该看起来像这样!

好了!如果一切顺利,这将是迄今为止最快的Twitter克隆教程,而且可能需要的代码行数最少!时间会证明这一切如何在复杂的现实世界Rails应用程序中协同工作和扩展,但就目前而言,开发人员复杂性的降低是一个巨大的胜利!

0人推荐
随时随地看视频
慕课网APP