用 Hotwire Turbo 实现常驻侧边栏
最近用 Hotwire Turbo 改进了 Geeknote 的首页,减少了重复查询和渲染,想分享一下过程。
问题
Geeknote 的首页有个侧栏区域,区域内展示了一些次要内容,有热门标签、推荐用户,和社区统计等,未来还可能加一些广告代码或友情链接。
问题在于,这个区域的内容并不重要,但是需要的查询并不少。热门标签需要对近期标签使用进行统计排序,推荐用户需要关联当前用户是否关注,社区统计比较简单只是对几个表的总计数。在用户切换 Tab 的时候会反复执行这些查询,并且推荐用户模块由于加了随机性,导致内容不断变动,会引起不必要的关注。
我希望侧栏内容在切换类似页面的时候持久化,减少查询,于是想到了用 Turbo 改造这块区域。
解决
原本的首页渲染基于普通的服务端模版,内容类似于:
<!-- home.html.erb -->
<div class="sidebar">
<!-- hot tags -->
<!-- recommend users -->
<!-- stats -->
</div>
要让这块内容持久化,需要做两个事情:
- 用 Turbo Frame 实现异步加载
- 用
data-turbo-permanent
实现持久化
异步加载
新增一个 action:
class HomeController < ApplicationController
def sidebar
end
end
添加相应的路由:
get "home/sidebar", to: "home#sidebar", as: :home_sidebar
将原先的侧栏模版移到新的视图:
<!-- home/sidebar.html.erb -->
<turbo-frame id="home_sidebar">
<div class="sidebar">
<!-- hot tags -->
<!-- recommend users -->
<!-- stats -->
</div>
</turbo-frame>
注意这里加了一层 <turbo-frame>
标签,这是用来标识后面 turbo frame 需要替换的内容。
如果一切正常,可以访问 /home/sidebar
预览侧栏的内容。你会发现由于没有其他布局元素,侧栏内容显得很宽,如果想好看一点可以在 <turbo-frame>
外层加一些布局元素,例如设置最大宽度和居中显示,这不会影响最终结果。
接着在原先 home 模版 sidebar 的位置添加标签:
<!-- home.html.erb -->
<turbo-frame id="home_sidebar" src="/home/sidebar" target="_top">
<turbo-frame>
解释一下内容:
<turbo-frame src="/home/sidebar">
表示这块内容来自于/home/sidebar
。id="home_sidebar"
表示请求目标页面后,用页面内相同 id 的 turbo_frame 内容替换这个 turbo_frame。(对应于 sidebar 模版内的 turbo_frame)target="_top"
表示点击 turbo_frame 内的链接将导航整个页面,而不局限于 turbo_frame 以内。
完成以后预览效果,可以看到侧栏是异步加载的:
咋一看这好像是负优化,每次切换 Tab 都发出两个请求,重复查询和推荐用户一直变动的问题也没解决。接下来我们让侧栏持久化。
持久化
在 <turbo-frame>
上添加一个 data-turbo-permanent
属性:
<!-- home.html.erb -->
<turbo-frame id="home_sidebar" src="/home/sidebar" target="_top" data-turbo-permanent>
<turbo-frame>
完成了!这个属性告诉 Turbo,在换页的时候把这个标签视为持久化内容,将上一页的内容直接移动到新页面。而在上一个页面这个 turbo_frame 已经加载过,所以不会再次加载。
效果如下:
在第一次访问首页的时候,依然需要访问一次 home/sidebar
,但之后切换 Tab,侧栏内容不会重复请求。
这就解决了侧栏重复查询和内容不断变动的问题。
总结
这是一个用 Hotwire 渐进式增强交互体验的例子。我可以先用传统的服务端渲染实现所有功能,然后再挑选值得优化的地方改进。
试想一下如果是前后端分离的项目,可能一开始就为侧栏内容设计多个 API,然后渲染逻辑在前端实现。但对我来说,一开始填充侧栏内容的时候还没想好要展示什么,服务端渲染可以轻易的尝试不同的展示逻辑,在服务端用变量传递数据要比 API 轻量很多。
希望这篇文章能为你提供帮助。