惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
云风的 BLOG
云风的 BLOG
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
A
About on SuperTechFans
WordPress大学
WordPress大学
B
Blog
Martin Fowler
Martin Fowler
Jina AI
Jina AI
I
InfoQ
P
Proofpoint News Feed
小众软件
小众软件
S
SegmentFault 最新的问题
V
V2EX
B
Blog RSS Feed
量子位
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
MongoDB | Blog
MongoDB | Blog
美团技术团队

Nic Lin's Blog

謝明真 - 高效領導力的課後筆記 NFT 開發實戰!基礎智能合約入門 (3) NFT 開發實戰!基礎智能合約入門 (2) NFT 開發實戰!基礎智能合約入門 (1) 如何自我檢測 log4j CVE 漏洞 Rails 如何在資料寫入時記錄來源 IP 位置 如何經營工程師 Youtube 頻道 - Part 8 營收篇 如何經營工程師 Youtube 頻道 - Part 7 酸民文化篇 如何經營工程師 Youtube 頻道 - Part 5 設備器材篇 如何經營工程師 Youtube 頻道 - Part 4 後製剪輯篇 如何經營工程師 Youtube 頻道 - Part 3 文案企劃篇 如何經營工程師 Youtube 頻道 - Part 2 設備器材篇 如何經營工程師 Youtube 頻道 - Part 1 制訂頻道方向篇 如何經營工程師 Youtube 頻道 - Part 0 Rails 中避免 race condition 的最佳實踐(二) Rails 中避免 race condition 的最佳實踐(一) 10 分鐘整合 google sheet 做自動化開發功能週報 經營 Side Project 300 天所帶來的收穫及挑戰 我的 Youtube 影片製作流程 API 設計時必須注意的 HTTP header 底線問題 如何提升你的程式可讀性之實務技巧(三) 如何提升你的程式可讀性之實務技巧(二) 如何提升你的程式可讀性之實務技巧(一) Ruby 中使用 freeze 優化效能的時機 避免 React 中的 useEffect 無限 render 如何在區域網路用 Docker 架設有 SSL 的 Gitlab 從被問到問人,那些我常問的面試問題 [Rails] 如何漂亮寫出可維護的 query (Maintainable Rails Query) 在已知長度情況下優化 slice 的性能 [ReactNative] 如何在 iOS APP 上主動要求用戶評分
在 Rails 內輕量使用 Vue Component 的最佳實踐
Nic Lin · 2020-02-22 · via Nic Lin's Blog

在維護一陣子的 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 也是不錯的方向。

JS 部分
<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 真的很頭痛。