什么是好的 view
为什么需要最佳实践?
- 项目太大,难懂
- 团队编程风格不同
你的 View 可能正面临的问题
- Complex UI with logic UI 和代码逻辑纠缠在一起
- Too long to maintain 代码太长难以维护
- Low performance 性能不好
- Security issues 容易产生安全问题
什么是好的代码
- Readability 易读,容易了解
- Flexibility 弹性,容易扩充
- Effective 效率,写码快速
- Maintainability 维护性,容易找到问题
- Consistency 一致性,遵循管理无须死背
- Testability 可测行,容易编写测试
18招技巧
No.1 Move logic to helper
BAD:
1 | <% if current_user && current_user == post.user %> |
GOOD:
1 | <% if editable?(post) %> |
No.2 Pre-decorate with Helper
经常用的地方先用 helper 整理
BAD:
1 | <%= @topic.content %> |
1 | <%= auto_link(truncate(simple_format(topic.content)), :length => 100) %> |
GOOD:
1 | <%= render_post_content(@topic.content) %> |
Common Case
- render_post_author
- render_post_published_date
- render_post_title
- render_post_content
No.3 Use Ruby in Helper ALL THE TIME
在 Helper 中只使用 Ruby 代码,而不要混杂 HTML。因为如果在 Helper 中最后调用 raw 或者 html_safe 会让代码有被 javascript 攻击的危险
BAD:
1 | def render_post_taglist(post, opts = {}) |
GOOD:
1 | def render_post_taglist(post, opts = {}) |
No.4 mix Helper & Partial
在 Helper 中可以混合使用 Partial
BAD:
1 | def render_post_title(post) |
GOOD:
1 | def render_post_title(post) |
No.5 Tell, Don’t ask
先 Query 再传入 Helper。这样可以避免效能低下的问题。
BAD:
1 | def render_post_taglist(post, opts = {}) |
1 | <% @posts.each do |post| %> |
GOOD:
1 | def render_post_taglist(tags, opts = {}) |
1 | <% @posts.each do |post| %> |
No.6 Wrap into a method
属性尽量包装成 method 而不是放在 helper 中
BAD:
1 | def render_comment_author(comment) |
GOOD:
1 | def render_comment_author(comment) |
No.7 Move code to Partial
view code 超过两页请注意
- highly duplicated 内容高度重复
- independent blocks 可以独立作为功能区块
Common Case
- nav/user_info
- nav/admin_menu
- vendor_js/google_analytics
- vendor_js/disqus_js
- global/footer
No.8 Use presenter to clean the view
使⽤ Presenter 解決 login in view 问题
BAD:
1 | <%= if user_rofile.has_experience? && user_rofile.experience_public? %> |
GOOD:
1 | <% user_profile.with_experience do %> |
1 | class ProfilePresenter < ::Presenter |
关于 presenter 的延伸阅读
- Exhibit vs Presenter
- Using Presenters in rails
- Presenters from Scratch
- Rails: Presenter Pattern
- Rails Presenters – Skinny Everything
No.9 Cache Digest
过去的页面缓存,我们需要自己管理版本,很难维护。新的 digest 缓存策略会自动失效。
BAD:
1 | <% cache [v15,@project] do %> |
GOOD:
1 | <% cache @project do %> |
关于 Cache Digest 的延伸阅读
No.10 Cells
当同一个页面含有多个 Tab ,它会造成如下问题:
- Controller code 特别长,因为需要取出很多不同数据存入的变量
- 在 controller & view 有性能问题不好找到
- 很难设置 action 级别的缓存
BAD:
1 | class UsersController < ApplicationController |
1 | <%= render :partial => "users/recent_post", :collection => @recent_posts %> |
GOOD:
1 | <%= render_cell :user, :rencent_posts, :user => @user %> |
1 | class UsersController < ApplicationController |
1 | class UserCell < Cell::Rails |
关于 Cells 的延伸阅读
No.11 content_for / yield
根据 Yahoo 大神的建议,应该把 javascript 和 css 代码放在页面的最下面。以便加快加载,但是会造成一些问题。比如你自定义的 js 代码比 jquery 库更早加载,以至于无法运行你的代码。
例子1:
1 | <!-- application.html.erb --> |
例子2:
1 | <!-- application.html.erb --> |
No.12 Decoration in Controller
有个别情况,可以把一些代码放在 controller 中
BAD:
1 | <%= content_for :meta do %> |
GOOD:
1 | def show |
1 | <%= stylesheet_tag "application" %> |
No.13 Decoration using I18n
善用 I18n 也可以帮你减少一部分 View 代码
BAD:
1 | def render_user_geneder(user) |
GOOD:
1 | def render_user_gender(user) |
No.14 Decoration using Decorator
不要把所有代码都往 Model 中扔,下面的代码演示了一种情况,你需要格式化一种显示发布时间的方法
BAD:
1 | # first helper |
下面的代码演示使用 https://github.com/drapergem/draper 之后
GOOD:
1 | <%= @article.publication_status %> |
1 | class ArticleDecorator < Draper::Decorator |
关于 draper 的延伸阅读
No.15 Decoration using View Object
有时候,我们希望在用户中,不列出我自己
BAD:
1 | <dl class="event-detail"> |
GOOD:
1 | class EventDetailView |
1 | <dl class="event-detail"> |
No.16 Form builder
- simple_form
- bootstrap_form
No.17 Form Object
在 FORM 中包装逻辑,而不是 model 或 controller。比如我们经常遇到的一个情况,只有同意某某条款才能继续注册表单。
Reform Decouples your models from form validation, presentation and workflows.
https://github.com/apotonick/reform
No.18 Policy Object / Rule Engine
权限控制
BAD:
1 | def render_post_edit_option(post) |
针对于这种情况建议使用权限控制系统,另外近来似乎大家都从 Cancan 转移到 Pundit 去了
Pundit Minimal authorization through OO design and pure Ruby classes
https://github.com/elabs/pundit
Cancan Authorization Gem for Ruby on Rails
https://github.com/ryanb/cancan
总结
- 总是假设这块正在实现的功能代码需要 decorated(修饰)
- 把逻辑移到 methods/classes
- 避免在 View/Helper 中做数据库查询
- 当代码很难懂的时候,把它抽取到一个新的控制中心比如:form object, Policy Object 之类的
延伸阅读
- http://blog.xdite.net
- https://github.com/bloudermilk/maintainable_templates http://pivotallabs.com/form-backing-objects-for-fun-and-profit/
- http://saturnflyer.com/blog/jim/2013/10/21/how-to-make-your-code-imply-responsibilities/
- http://objectsonrails.com/
出处
以上内容由 Victor 整理,本人只是记录在blog中。