Rails 如何处理好复杂逻辑的 View?

huacnlee · 2011年11月23日 · 最后由 pokkalee 回复于 2012年02月04日 · 5468 次阅读

刚刚才 Twitter 上面和 @xdite 讨论到这个话题,顺便也拋出来聊聊 以之前 zheye 这个项目为例,比如这个页面: https://github.com/huacnlee/quora/blob/master/app/views/logs/_log.html.erb

业务需求就是有这么多的逻辑,而且是由于项目不断变化带来愈来愈多的逻辑,有些页面就必然会变得这么复杂,后面维护起来就成问题了。

大家有没有什么好的方法处理复杂的页面?

Decorator,draper 我用了一下,很适合处理 View 里的复杂逻辑

#1 楼 @cqpx 能详细说一下 Decorator 么?

#2 楼 @huacnlee 我也是从 railscast 上学的 (http://railscasts.com/episodes/286-draper ),概念很简单,就是把 view 里的逻辑全部独立出来。大哥去看看就明白了

#3 楼 @cqpx 哦,这类写法我现在放在 Model 里面,比如

class Post
  field :state, :type => Integer

  # Custom attributes
  def state_s
    ["allow", "disable"][self.state]
  end
end

不过这类写法只是也谢简单的字符值,没有逻辑的东西

Hi, 我當時是這樣 refactor zheye 的 code 的

https://github.com/techbang/q17/blob/master/app/views/logs/_log.html.erb https://github.com/techbang/q17/blob/master/app/views/logs/_asklog.erb

當然還沒有接下去拆的太乾淨。因為光拆到這樣就很費力了 XD

光 model 裡的 Type 就花了一些時間切....

老實說我反對 Decorator 的觀念(尤其是 Draper 的實作)。

在 台灣 Rubyconf 時,我有跟 Cell 的作者討論到 view 的寫作方式。 其實 Controller 和 View 本來就要切的很開,所以我們都討厭 Presenter 這樣的東西。

何況是把 Model 和 View 混的更深的 Decorator。

我們 team 的標準 practice 是

  • 「描述」寫在 helper 的 method 裡。或者是 Translation 檔的裡的一個 value
  • 而 Model 只負責吐 true / false ,或是屬於 model 內應該做的純物質 do_some_logic

我用过 draper,会明显增加 view render 的时间。 对于 view 代码的复用,我还是比较喜欢用 cells http://rails-bestpractices.com/posts/89-use-cells-to-abstract-view-widgets

#7 楼 @flyerhzm 但是 Cells 似乎因对的场景有限

#5 楼 @xdite partial 会不会带来性能问题

之前曾經寫過的 MVP 與 Cells 的文章 http://wp.xdite.net/?p=3063

其實就是 MiniController 與 Partial View 的組合

只要你不要在 partial 裡 query something,其實就不會慢。

真的得要 query something,我就會用 cell 再起一個 mini controller 去解這件事。

因為 partial 速度其實都算蠻快的,大家在抱怨慢是慢在 query in view 這一段。而 Draper 根本沒有「解決」這件事...反而把事情弄的更糟了。

@huacnlee 请问下 ruby-china 的 ip 库用的是什么?

我是這樣整理 code 的。

  1. 避免 logic in view,即便要趕功能,寫完之後會記在 issue tracking ,提醒自己有幹了一件骯髒的事。

比如說


<% if current_user || current_user.is_admin ? %>
  <%= link_to("EDIT", edit_post_path(@post) %>
<% end 

  1. 接著我會做下一步的 refactor
<% if editable_by?(current_user) %>
  <%= render :partial => "post_toolbar" %>
<% end 

而 editable_by? 會抽去放在 posts_helper 。 為什麼要抽出成 editable? 。因為可以管理者也許權限需要擴增。 到處都是硬寫死的邏輯,將來就要去把整個 project 到處挖來看

def editable?(user)
  user && user.is_admin?
end

中間這一段又可以拆成 cancan 的 method 去做....。因為 Rule Engine 式清晰很多。

  • 主要思路是別把 View 如 html tag、提示訊息,沾到 controller 或 model 層去
  • Controller 只處理 去 call Model 的 method 和 routing
  • Model 只負責資料的邏輯加工運算
  • View 的邏輯由 helper 控制,View 的表現由 HTML 控制。程式的邏輯由 Model 處理,路徑的落及由 Controller 處理。

#12 楼 @ytwman 木有 IP 库,那个地区是手动设置的,别跑题

#13 楼 @xdite 这个是我目前用的很频繁的做法

owner? # 判断 current_user 是否是参数1的创建者
admin? # 是否是管理员

Draper 可以将与模型绑定的视图逻辑分离出来 损失的性能可以用 cache 找回来

找不回來的。因為 heat cache 還是有成本,就看誰倒楣剛好碰上當那個去 heat 的人。

而 query in view 一次的代價是幾百 ms,同樣的 query 在 controller 可能只要幾 ms。詳見我的 http://wp.xdite.net/?p=3063

會換上 cells 就是因為當初我們也是用 cache 去解 heavy view 的問題。但是 heat cache 實在太沈重,於是就轉變成想要 background heat cache 的方式。

但 Rails 原始的 cache 架構很難實做 background heat cache ,於是我們找阿找的,找到 cells,這一套就能讓我們實作 background heat cache。

本來我們用 cells 也是這個原因而已。後來我找到時間仔細讀 Cells 的架構,才發覺他其實就是 MiniController + View 的實作。本來我最痛的就是無法把 View 裡面的 query 拆出去,尤其是三層邏輯 View,拆無可拆,只好 cache。

我把三層邏輯 View 拆成三層 MiniController 後,速度從 500ms 變成了 15ms...。連 Cache 都不必上了。

在开发稍微大的 project 就会遇到这个问题。看了 railsconf 2011, Fat Models Aren't Enough slide(http://assets.en.oreilly.com/1/event/59/Fat%20Models%20Aren_t%20Enough%20Presentation.pdf) 介绍的 presenter pattern. 这一招经过我的实战,发现在处理复杂逻辑时还是不理想,因为我真不知道如何布局越来越多的代码,它们虽然已经是 abstract class 了,但还没有组成一个个组件。 所以后来把@xdite说过的 cell 再仔细阅读,发现却是一个很好的解耦的方法。

query in view 在 asset pipeline 下用户体验很好...

之前一直会担心 partial 过细后面回头看会很麻烦,现在这么一回头看,其实相比之下,将复杂的页面细分成多个 partial 确实会让 view 干净很多(实际上是,之前担心 partial 性能的问题,汗)

看起来 @xdite 似乎将 Cells 用得很广泛,我目前得项目里面顶多一两个 Cells 都是也写常用的功能,比如 Commentable, AttachmentList

#20 楼 @huacnlee helper 拯救你

piginzoo 吐槽一下 widget 中提及了此贴 04月03日 10:56
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册