3

在 Rails 內輕量使用 Vue Component 的最佳實踐

 2 years ago
source link: https://blog.niclin.tw/2020/02/22/rails-hybrid-with-vue-component/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在維護一陣子的 Rails 專案上,難免會看到各處擺放的 JavaScript function 或是 jQuery 各種直接操作 DOM 之類的作法。

在這個前端技術大時代,為了因應更複雜的 UI 需求,有更多易於擴充、提升效能的作法能夠選擇,例如 React / Vue 之類的前端框架。

要解決什麼問題

  • 避免直接操作 DOM
  • 更易維護及套件管理
  • UI 易於管理 LifeCycle

在 Rails 5.1 之後,已經對 webpakcer 有相當好的支持性,在後面的 Rails 6 版本也已經變為預設的前端工具了

那麼在 Rails 專案中陳年已久的 jQuery + Asset pipeline,要怎麼樣才能夠跟這些新技術接軌呢

在有限的資源下要整個重寫,或許共存也是一種選擇。

在這篇文章中,我們的目標是將前端框架,以輕量導入的方式和 Rails 共存

這裡使用 Vue 做為示範,你也可以引入其他框架例如 React 來整合前端技術

怎麼不直接做前後端分離

前後端分離要考慮的事情很多,除非產品有強烈的需求,或是在資源狀況足夠的情況下是可以規劃的,不然很容易變成「前後端分裂」

對於 Rails 來說,你的前後端分離可能會遇到這些事情

  1. 原本好用的 session 要另外處理
  2. Server side rendering(SEO?)

以現有專案開始

加入 gem "webpacker"

直接執行指令來安裝 vue 的相依 JS 套件

rails webpacker:install:vue

這時候你會看到新的目錄

/app/javascript
├── app.vue
└── packs
    ├── application.js
    └── hello_vue.js

packs 下面放的檔案可以當作一個「入口點」,也就是和 asset pipeline 中的 Manifest 一樣。

差別在於你在 layout 中引入時語法有些不同

# Webpacker
<%= javascript_pack_tag 'application' %>

# Asset pipeline
<%= javascript_include_tag 'application' %>

我們可以在 app/javascript/packs/application.js 中修改

import Vue from 'vue/dist/vue.esm'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    el: '[data-behavior="vue-app"]'
  })
})

然後在 app/views/layouts/application.html.erb 中加入 <%= javascript_pack_tag 'application' %>

並且新增 data-behavior="app" 將 Vue 掛載在 DOM element 上(官方不建議掛在 body 上,別這麼懶 XD)

<body>
  <div class="container" data-behavior="vue-app">
    <%= yield %>
  </div>
</body>

打開有用到該 layout 的頁面,應該可以看到瀏覽器 console 顯示 You are running Vue in development mode.

那就可以開始來用 Vue 開發我們的 JS 元件了

Vue component

新增 app/javascript/components/Message.vue

<template>
  <div class="message">
    {{message}}
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Vue card"
    }
  }
}
</script>

並修改 app/javascript/packs/application.js

import Vue from 'vue/dist/vue.esm'
+ import Message from "../components/Message"

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
+    el: '[data-behavior="vue-app"]',
+    components: { Message }
  })
})

這時候你可以在 .erb 裡面插入 Vue 的 component,例如在 app/views/home/index.html.erb

就可以這樣寫

<message></message>

所以如果元件是 MessageCard,在 .erb 裡面就寫 message-card

也因為這樣,你在 app/javascript/ 下的 vue component 是可以和 rails 共享 CSS 的

所以你可以在 asset pipeline 寫 SCSS,在 webpakcer 裡面寫 JS 是可以共存的

直接從 Rails 給 props

有時候一些 JS 元件在初次渲染時,會沒有預設資料,這時候因為是這種 hybird 的寫法,所以你可以透過 controller 先拿好資料,在 view 的部分直接將 props 給 vue component

Ruby 部分
# Message controller
@message = Message.find(params[:id])

# Message View
<message :message="<%= @message.to_json %>"></message>

因為你拿出來的 @message 是 ruby object,這邊可以直接轉 JSON 餵給 component,要更方便的作法就是直接在 controller 內先 serialize 也是不錯的方向。

<template>
  <div class="message">
    <ul>
      <li>ID: {{ message.id }}</li>
      <li>email: {{ message.user.email }}</li>
      <li>content: {{ message.content }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  props: ["message"],
}
</script>

基本上到這裡,你可以在 Rails 的基礎上,使用前端框架來開發更多的 component,以現在這種 MVVM 雙向綁定加上不用直接操作 DOM 做渲染的方式,在未來協作或管理更複雜的資料流及 UI 都是有一定程度的幫助。

正常來說能寫 ES6 就不會想寫原本的 JS,能有 MVVM 就不想回去自己到處綁 element 了

Hybird 做法的優缺點

這樣一來,可以慢慢抽換原本舊有的作法,不用再擔心因為到處寫 jQuery 來做 UI flow 而導致難以維護的問題

  1. 可以直接餵 props 而不用 API 請求
  2. 可以用 Rails cache
  3. 可以用 Rails session
1. I18n 處理

大致上兩種作法

  1. 和 JS 放在一起(比較推薦的作法)
  2. 全部寫在 Rails 裡面,然後倒出來用轉 JSON 餵過去,優點是只要維護一套,缺點是如果語系檔很大會非常慢,後期要單獨將前端拆出去時還要擔心語系耦合問題
2. 前後端混在一起寫

你沒辦法直接招前端工程師變成及戰力,除非他也會 Rails,不然就還要花時間帶了 XD

用這種方式將 UI 邏輯丟給更適合解決問題的框架來做更方便,不管是效能或是撰寫方式都更好,不然常常看到 Rails 裡面各種綁 element 然後到處操作 DOM 真的很頭痛。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK