|
|
@@ -9,6 +9,7 @@ This package offers Node.js server-side rendering for Vue 2.0.
|
|
|
- [Renderer Options](#renderer-options)
|
|
|
- [Why Use `bundleRenderer`?](#why-use-bundlerenderer)
|
|
|
- [Creating the Server Bundle](#creating-the-server-bundle)
|
|
|
+- [Creating the Client Manifest](#creating-the-client-manifest)
|
|
|
- [Component Caching](#component-caching)
|
|
|
- [Client Side Hydration](#client-side-hydration)
|
|
|
|
|
|
@@ -213,12 +214,60 @@ const renderer = createRenderer({
|
|
|
|
|
|
> New in 2.2.0
|
|
|
|
|
|
+- only used in `createBundleRenderer`
|
|
|
+
|
|
|
Explicitly declare the base directory for the server bundle to resolve node_modules from. This is only needed if your generated bundle file is placed in a different location from where the externalized NPM dependencies are installed.
|
|
|
|
|
|
Note that the `basedir` is automatically inferred if you use `vue-ssr-webpack-plugin` or provide an absolute path to `createBundleRenderer` as the first argument, so in most cases you don't need to provide this option. However, this option does allow you to explicitly overwrite the inferred value.
|
|
|
|
|
|
---
|
|
|
|
|
|
+### clientManifest
|
|
|
+
|
|
|
+> New in 2.3.0
|
|
|
+
|
|
|
+- only used in `createBundleRenderer`
|
|
|
+- only used when the `template` option is also provided
|
|
|
+
|
|
|
+Provide a client build manifest object generated by `vue-ssr-webpack-plugin`. With the client manifest, the `bundleRenderer` has webpack build information of both the server and client, and thus will be able to automatically infer and inject asset links into the rendered HTML. For more details, see [Creating the Client Manifest](#creating-the-client-manifest).
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### shouldPreload
|
|
|
+
|
|
|
+> New in 2.3.0
|
|
|
+
|
|
|
+- only used in `createBundleRenderer`
|
|
|
+- only used when the `template` and `clientManifest` options are also provided
|
|
|
+
|
|
|
+When a client manifest is present, the renderer will automatically inject [preload and prefetch directives](https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf) into the `<head>` section. By default, an asset will be preloaded when:
|
|
|
+
|
|
|
+- It is used during the render. For example, if your bundle uses code split, only assets used by the chunks that were used during a render will be injected.
|
|
|
+
|
|
|
+- It is of type `script` or `font`, because these are crucial for faster time-to-fisrt-paint / time-to-interaction. Images and other types of assets are **not** preloaded by default.
|
|
|
+
|
|
|
+If you do want to preload assets other than scripts and fonts, `shouldPreload` gives you full control:
|
|
|
+
|
|
|
+``` js
|
|
|
+const renderer = createBundleRenderer(serverBundle, {
|
|
|
+ template,
|
|
|
+ clientManifest,
|
|
|
+ shouldPreload: (file, type) => {
|
|
|
+ // type is inferred based on the file extension.
|
|
|
+ // https://fetch.spec.whatwg.org/#concept-request-destination
|
|
|
+ if (type === 'script' || type === 'font') {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if (type === 'image') {
|
|
|
+ // only preload important images
|
|
|
+ return file === 'hero.jpg'
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
### directives
|
|
|
|
|
|
Allows you to provide server-side implementations for your custom directives:
|
|
|
@@ -307,6 +356,74 @@ module.exports = {
|
|
|
|
|
|
Since externalized modules will be shared across every request, you need to make sure that the dependency is **idempotent**. That is, using it across different requests should always yield the same result - it cannot have global state that may be changed by your application. Interactions between externalized modules are fine (e.g. using a Vue plugin).
|
|
|
|
|
|
+## Creating the Client Manifest
|
|
|
+
|
|
|
+`vue-server-renderer` 2.2 supports rendering the entire HTML page with the `template` option. 2.3 introduces another new feature, which allows us to pass a manifest of our client-side build to the `bundleRenderer`. This provides the renderer with information of both the server AND client builds, so it can automatically infer and inject preload / prefetch directives and script tags into the rendered HTML. This is particularly useful when rendering a bundle that leverages webpack's on-demand code splitting features: we can ensure the right chunks are preloaded / prefetched, and also directly embed `<script>` tags for needed async chunks in the HTML to avoid waterfall requests on the client, thus improving TTI (time-to-interactive).
|
|
|
+
|
|
|
+To generate a client manifest, you need to add the client plugin to your client webpack config. In addition:
|
|
|
+
|
|
|
+- Make sure to use `CommonsChunkPlugin` to split the webpack runtime into its own entry chunk, so that async chunks can be injected **after** the runtime and **before** your main app code.
|
|
|
+
|
|
|
+- Since in this case `vue-server-renderer` will be dynamically injecting the asset links, you don't need to use `html-webpack-plugin`. However, the setup only handles JavaScript. If you want to use `html-webpack-plugin` for embedding other types of assets (e.g fonts), you can still use it - just make sure to configure it with `inject: false` so that it doesn't duplicate-inject the scripts.
|
|
|
+
|
|
|
+``` js
|
|
|
+// in your webpack client bundle config
|
|
|
+const webpack = require('webpack')
|
|
|
+const { VueSSRClientPlugin } = require('vue-ssr-webpack-plugin')
|
|
|
+
|
|
|
+module.exports = {
|
|
|
+ // ...
|
|
|
+ plugins: [
|
|
|
+ // this splits the webpack runtime into a leading chunk
|
|
|
+ // so that async chunks can be injected right after it.
|
|
|
+ // this also enables better caching for your app/vendor code.
|
|
|
+ new webpack.optimize.CommonsChunkPlugin({
|
|
|
+ name: 'manifest',
|
|
|
+ minChunks: Infinity
|
|
|
+ }),
|
|
|
+ // this will generate the client manifest JSON file.
|
|
|
+ new VueSSRClientPlugin()
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+This will generate an additional `vue-ssr-client-manifest.json` file in your build output. Simply require and pass it to the `bundleRenderer`:
|
|
|
+
|
|
|
+``` js
|
|
|
+const { createBundleRenderer } = require('vue-server-renderer')
|
|
|
+
|
|
|
+const template = require('fs').readFileSync('/path/to/template.html', 'utf-8')
|
|
|
+const serverBundle = require('/path/to/vue-ssr-bundle.json')
|
|
|
+const clientManifest = require('/path/to/vue-ssr-client-manifest.json')
|
|
|
+
|
|
|
+const renderer = createBundleRenderer(serverBundle, {
|
|
|
+ template,
|
|
|
+ clientManifest
|
|
|
+})
|
|
|
+```
|
|
|
+
|
|
|
+With this setup, your server-rendered HTML for a build with code-splitting will look something like this:
|
|
|
+
|
|
|
+``` html
|
|
|
+<html><head>
|
|
|
+ <!-- chunks used for this render should have preload -->
|
|
|
+ <link rel="preload" href="/manifest.js" as="script">
|
|
|
+ <link rel="preload" href="/main.js" as="script">
|
|
|
+ <link rel="preload" href="/0.js" as="script">
|
|
|
+ <!-- unused async chunks should have prefetch -->
|
|
|
+ <link rel="prefetch" href="/1.js" as="script">
|
|
|
+</head><body>
|
|
|
+ <div data-server-rendered="true"><div>async</div></div>
|
|
|
+ <!-- manifest chunk should be first -->
|
|
|
+ <script src="/manifest.js"></script>
|
|
|
+ <!-- async chunks should be before main chunk -->
|
|
|
+ <script src="/0.js"></script>
|
|
|
+ <script src="/main.js"></script>
|
|
|
+</body></html>`
|
|
|
+```
|
|
|
+
|
|
|
+You can also control precisely what to preload with the [shouldPreload](#shouldpreload) option.
|
|
|
+
|
|
|
## Component Caching
|
|
|
|
|
|
You can easily cache components during SSR by implementing the `serverCacheKey` function:
|