本文分享 Turbolinks 的扩展,用于加速网页访问。
最近发现了 InstantClick 这种对网页预加载的小技巧,这种方式能有效的改进网站的访问速度。
大概原理是当用户鼠标经过链接的时候,会提前用 Ajax 的方式将网页预先加载好存入 cache,等用户点击的时候,用之前的 cache 直接渲染。
于是我还发了 Twitter 说这个事情:
Rails 内置的 Turbolinks 实际上有类似的 cache 机制,在用户来回点击页面的时候会利用 cache 提前渲染,只是没有在用户鼠标 hover 的时候进行预处理。
查了查,发现 Turblinks 的 Issue 里面也有讨论这个 turbolinks/turbolinks#313,仔细看找到了一个实现 参考,于是封装了一下,并做了改进实现了一个 Turbolinks 的扩展。
https://github.com/huacnlee/turbolinks-prefetch
同时我在实现里面额外调整了 Turbolinks 的 visit 动作,如果已经有 prefetch 的动作,会直接 render 不会再次请求页面。
如你所见,目前 Ruby China 已经开启了这个功能(香港服务器),在 prefetch 产生效果的时候,基本上页面打开犹如本地网页。
hover --> [prefetch] --<no cache>--> [XHR fetch] -> [Turbolinks cache.put]
              |
          <exist cache / in fetching>
              |
            ignore
click --<check cache>-- exist --> [isPrefetch] -> [Turbolinks.visit advance] ---> [render page]
             |                         |                 |
             |                         |                 --async-> [fetch background] -> [render if updated]
             |                         |
             |                       <Yes>
             |                         |--- [Turbolinks.visit restore] --> render -> nothing
          No cahce
             |
             ---> [Turbolinks.visit]
$ yarn add turbolinks-prefetch
import Turbolinks from 'turbolinks';
window.Turbolinks = Turbolinks;
import TurbolinksPrefetch from 'turbolinks-prefetch';
TurbolinksPrefetch.start();
当 Prefetch 请求的时候,将会额外发送 Purpose: prefetch 的 HTTP header,如果你需要特别忽略某些动作,你可以用到它。
比如更新阅读状态、访问量更新等动作:
class TopicsController < ApplicationController
  def show
    if request.headers["Purpose"] != "prefetch"
      # 不在 prefetch 的时候更新访问量
      @topic.increment_hit
    end
  end
end
默认情况下,Turbolinks Prefetch 将会对所有的链接开启行为。
除了下面这些情况:
target="_blank";data-remote 属性的链接;data-method 属性的链接;data-prefetch="false" 属性的链接;应该说大多数的默认情况下,你不需要处理,类似 Rails UJS 这样的默认行为已经在 Turbolinks Prefetch 处理好了。
于是你可以这样来对部分链接禁用 prefetch:
<a href="https://google.com">Google</>
<a href="/topics/123" target="_blank">Open in new window</a>
<a href="/topics/123" data-method="PUT" data-remote>Put</a>
<a href="/topics/123" data-method="DELETE">Delete</a>
<a href="/topics/123" data-prefetch="false">Disable by directly</a>
🎊 不要犹豫了,立即在你们已经有使用 Tubrolinks 的项目里面用起来吧,基本上是无缝支持。