Volt.HMR.Document (Volt v0.21.0)

Copy Markdown View Source

Lets an open page ask whether its server-rendered HTML is still current.

A change to a template or content file may affect any page, or none. Rather than reloading every open page, Volt's dev client revalidates the page it is on: it requests the page again with the entity tag of the HTML it was served and reloads only when the server answers with something other than 304 Not Modified.

Volt.DevServer does this for every successful HTML response that passes through it, whether it adds the dev client or the page already loads it with <script type="module" src="/@volt/client.js">. A server that sends HTML without going through Volt.DevServer takes part by:

  1. computing etag/1 of the rendered HTML;
  2. answering 304 with an empty body when fresh?/2 is true;
  3. otherwise sending the tag in the etag response header and in the data-volt-etag attribute of the client <script>.

Pages without the attribute reload on every document update.

Summary

Types

Attributes the server set or changed on an element, and those it removed.

A preserved element whose server-rendered attributes changed: its position among the elements matching the preserve selector, and its new attributes.

What the server changed outside the body's contents, which are patched from the new HTML itself

Functions

The attribute of the client <script> that holds the page's entity tag.

Decide how a page gets from one server render to the next.

Compute the entity tag of rendered HTML.

Return whether the request already holds the HTML that etag identifies.

The attribute of the client <script> that lets the client patch the page in place instead of reloading it. Its value is a selector for elements that client code owns, or empty.

Types

attribute_changes()

@type attribute_changes() :: %{
  set: %{required(String.t()) => String.t()},
  remove: [String.t()]
}

Attributes the server set or changed on an element, and those it removed.

owned_change()

@type owned_change() :: %{
  index: non_neg_integer(),
  attributes: %{required(String.t()) => String.t()}
}

A preserved element whose server-rendered attributes changed: its position among the elements matching the preserve selector, and its new attributes.

patch()

@type patch() :: %{
  owned: [owned_change()],
  root: %{required(String.t()) => attribute_changes()},
  head: %{remove: [String.t()], add: [String.t()]}
}

What the server changed outside the body's contents, which are patched from the new HTML itself:

  • :owned — preserved elements whose attributes changed
  • :root — attribute changes on "html" and "body", for those that have any
  • :head — head elements, as HTML, that are gone and that are new. Scripts, stylesheets and the title are not among them.

Functions

attribute()

@spec attribute() :: String.t()

The attribute of the client <script> that holds the page's entity tag.

changes(previous, next, preserve)

@spec changes(String.t(), String.t(), String.t() | nil) :: :reload | {:patch, patch()}

Decide how a page gets from one server render to the next.

Both arguments are HTML the server rendered, so the comparison sees only what the server changed, never what scripts did to the page since. An attribute a script set on <html> is in neither render and is left alone.

Returns :reload when patching cannot apply the change: the scripts the page runs or its stylesheets differ, or elements matching preserve were added or removed. Otherwise returns {:patch, patch}.

etag(html)

@spec etag(iodata()) :: String.t()

Compute the entity tag of rendered HTML.

fresh?(conn, etag)

@spec fresh?(Plug.Conn.t(), String.t()) :: boolean()

Return whether the request already holds the HTML that etag identifies.

morph_attribute()

@spec morph_attribute() :: String.t()

The attribute of the client <script> that lets the client patch the page in place instead of reloading it. Its value is a selector for elements that client code owns, or empty.