Page MenuHomePhabricator

Pass server-rendered search form state to client on initial render of Vue.js search
Closed, ResolvedPublic

Description

Transitioning from the server-rendered HTML to the new Vue.js JavaScript experience is complicated and important. This initial render is called client-side hydration. This task tracks the work needed smoothly transition from server state to client state.

Complications and concerns

  • The server-rendered HTML is generated with PHP, not Vue.js.
  • The searchSatisfaction client instrumentation loads on every pageview and alters the DOM whenever the form is submitted
  • Per T249306, the search experience does not load until the user focus on the search form which means the user is likely to be actively typing their query when dependency loading completes. In other words, focus state, selection state, and input value must be preserved. If the DOM changes, the CSS transition styles cause the focus to flicker.
  • Per T254695, we want the transition to be seamless and perceived as prompt.
  • Other JavaScript, such as UniversalLanguageSelector's Input Methods (also called "input tools") load by default on some wiki (on user input focus!) and bind to the inputs. Another example is the DWIM gadget which is a default on hewiki.
  • Value and focus must be persisted manually even when client hydration is performed correctly. (At least the former is a documented caveat.)
  • Search input and results are Vue.js powered. The input has a clear button and the results are generated on the fly. The structure of the input itself differs from Vector's SSR and requires multiple elements.

Options

  1. Render the whole search form. This provides a deeper Vue.js integration that would allow for rich interactivity in the form itself, not just the results. However, it also has all the concerns identified above.
    1. Properly hydrate by adding data-server-rendered to the SSR form.
    2. Manually pass state across.
  2. Render the results only. Very similar to old search. There's not much to hydrate on the client.
  3. Render the search input itself only. Requires SSR HTML changes but there's little opportunity for other scripts to break hydration.
  4. Something else.

Acceptance criteria

  • No changes to loading strategy. Vue.js and dependencies are loaded on user input focus.
  • No regressions to other integrations.
  • The transition from server-rendered state to client-rendered state is seamless.

References

Questions

  • What server styles do we need to isolate in WVUI for sharing? They would need to load on every pageview.

Developer notes

Option 1.a is the most desirable but must challenging. The Vue.js client renderer implementation expects exact synchronization with the server-renderer. Even a blank text node can cause runtime errors or hydration failures. For example:

Client template
<div id="app">
	<div>foo</div>
</div>
Server template (working)
<div id="app" data-server-rendered="true"><div>foo2</div></div>
Server template (breaks client)
<div id="app" data-server-rendered="true"> <div>foo2</div></div>

In the second server template example, note the extra space after the opening div which breaks the client. Note also that the server-rendered outputs are minified. This is to help the WVUI consumer (Vector) avoid server-rendered differences by assuming a deterministic minified structure and is made with the following vue-loader configuration:

vue-loader config
// Process single-file components (SFCs). This matches loader extensions to the SFC
// language attributes.
{
	test: /\.vue$/,
	use: {
		loader: 'vue-loader',
		options: {
			compilerOptions: {
				// Any whitespace or comment differences between tags causes client
				// hydration to fail. When whitespace is preserved, it is difficult as a
				// client to anticipate where these occur as Vue.js is not always used
				// to generate the HTML server-side. Remove all whitespace so clients
				// can structure their elements deterministically.
				whitespace: 'condense'
			}
		}
	}
}

However, this structure also varies in development and production mode and I haven't figured out yet how to configure that. Since Vector does not use Vue.js on the backend for rendering, no variation is permitted. For example, whitespace differences can cause cryptic errors like Uncaught DOMException: Node.appendChild: Cannot add children to a Text or Uncaught TypeError: nodeOps.tagName(...) is undefined or just simple client hydration failures which are only reported in development mode:

Parent:  <div class="wvui-input"> vue.runtime.esm.js:6426

Mismatching childNodes vs. VNodes: NodeList [ input#searchInput.wvui-input__input ] Array(3) [ {…}, {…}, {…} ] vue.runtime.esm.js:6427

[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render. vue.runtime.esm.js:619

In summary, the Vue.js client renderer seems to expect a Vue.js server renderer and has little tolerance. This does not work well with Vector's PHP implementation and the confounding factors identified above.


Option 1.b seems like the next best thing but the code is fragile and imperative. Worse, because the DOM is replaced, the focus state is lost momentarily and the CSS transitions cause a noticeable flicker. This could be worked around but will require more hacks.

Explorations

Event Timeline

Niedzielski added a subscriber: ovasileva.

@ovasileva, I'm working on this as part of Vue.js search so moving into the kabanana.

Change 616312 had a related patch set uploaded (by Niedzielski; owner: Stephen Niedzielski):
[mediawiki/skins/Vector@feat/search] Hydrate WVUI search

https://gerrit.wikimedia.org/r/616312

Change 616312 merged by jenkins-bot:
[mediawiki/skins/Vector@feat/search] Hydrate WVUI search

https://gerrit.wikimedia.org/r/616312

@Niedzielski: I followed the default process for this card and moved it to Needs QA. If it shouldn't be there, then LMK.

@phuedx / @Niedzielski can you provide some test steps or testable criteria, please?

Hehe yeah wondering about testing criteria too 😅

Niedzielski added a subscriber: Edtadros.

Sorry, but this feature is not available for testing yet as new search is not deployed to the beta cluster.

phuedx claimed this task.

Closing this as resolved since I tested @Niedzielski's patch locally prior to merging it and we're deferring QA of the feature until after the feature branch is merged into master and deployed to the BC.