This repository has been archived by the owner on Sep 25, 2021. It is now read-only.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This pull request modifies Turbolinks to save a snapshot of the previous page to its cache asynchronously after rendering, which means:
body.cloneNode(true)
no longer happens inside the render frame.disconnect()
callbacks to prepare the page for caching.To understand what’s changed, consider the 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 aturbolinks: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 Stimulusdisconnect()
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.In the revised rendering process, Turbolinks defers snapshot caching using a
setTimeout
callback, moving the expensivecloneNode()
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).