Skip to content
This repository has been archived by the owner on Sep 25, 2021. It is now read-only.

Deferred snapshot caching #390

Merged
merged 6 commits into from
Jul 5, 2018
Merged

Deferred snapshot caching #390

merged 6 commits into from
Jul 5, 2018

Conversation

sstephenson
Copy link
Contributor

@sstephenson sstephenson commented May 23, 2018

This pull request modifies Turbolinks to save a snapshot of the previous page to its cache asynchronously after rendering, which means:

  • Pages display more quickly, since the call to body.cloneNode(true) no longer happens inside the render frame.
  • Stimulus controllers can use their disconnect() callbacks to prepare the page for caching.

To understand what’s changed, consider the timeline of the current rendering process:
Timeline of the current rendering process

The rendering process takes place inside a requestAnimationFrame callback, a technique which enables Turbolinks to synchronize page replacement and scroll positioning without visual flicker.

Turbolinks dispatches a turbolinks:before-cache event just before rendering, which allows applications to prepare the page for caching. Then it clones the document and saves the result to the snapshot cache. After merging <head> and swapping <body>, Turbolinks dispatches a turbolinks:render event.

The browser notifies any MutationObserver instances watching for childList changes on the <html> element about the <body> swap in a microtask at the end of the animation frame. Stimulus uses a mutation observer to detect and notify controllers when their elements connect to and disconnect from the document. It’s natural to expect to be able to “tear down” a controller’s element in a Stimulus disconnect() callback, but under the current rendering process, any changes to the element are ignored since the callback happens after the document has been cached by Turbolinks.

Timeline of the new rendering process with deferred snapshot caching

In the revised rendering process, Turbolinks defers snapshot caching using a setTimeout callback, moving the expensive cloneNode() operation outside of the animation frame.

Deferring the clone can have a significant impact on rendering performance with large pages, and informal testing with Basecamp shows a speedup of up to 67% during rendering. Additionally, by deferring caching, it’s now possible for Stimulus controllers (and other code which uses MutationObserver) to prepare elements for caching in response to mutation records.

One caveat: permanent elements need to be cloned “on the way out” before they move to a new <body>. The new rendering process handles this automatically (78d1e5d).

@javan
Copy link
Contributor

javan commented May 24, 2018

Currently, Stimulus controllers can determine Am I in a cached page? in their connect() callbacks, but they can't determine Am I about to be cached? with certainty on disconnect(). What do you think about adding a companion to the data-turbolinks-preview annotation for that?

Otherwise, we re-insert a clone of the permanent element while the outgoing body is still attached, which may trigger unnecessary connect()/disconnect() callbacks in a Stimulus application.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants