Skip to content

Commit 75f05ab

Browse files
committed
build setup
1 parent 31d56cc commit 75f05ab

File tree

4 files changed

+100
-35
lines changed

4 files changed

+100
-35
lines changed

en/build-config.md

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ module.exports = merge(baseConfig, {
3030

3131
// Externalize app dependencies. This makes the server build much faster
3232
// and generates a smaller bundle file. Note if you want a dependency
33-
// (e.g. UI lib that provides raw *.vue files) to be processed by webpack,
34-
// don't include it here.
33+
// to be processed by webpack (e.g. UI lib that provides raw *.vue files),
34+
// do NOT include it here.
3535
externals: Object.keys(require('/path/to/package.json').dependencies),
3636

3737
// This is the plugin that turns the entire output of the server build
@@ -62,9 +62,13 @@ The client config can remain largely the same with the base config. Obviously yo
6262

6363
> requires version 2.3.0+
6464
65-
In addition to the server bundle, we can also generate a client build manifest. With the client manifest and the server bundle, the renderer now has information of both the server *and* client builds, so it can automatically infer and inject [preload / prefetch directives](https://css-tricks.com/prefetching-preloading-prebrowsing/) and script tags into the rendered HTML.
65+
In addition to the server bundle, we can also generate a client build manifest. With the client manifest and the server bundle, the renderer now has information of both the server *and* client builds, so it can automatically infer and inject [preload / prefetch directives](https://css-tricks.com/prefetching-preloading-prebrowsing/) and css links / script tags into the rendered HTML.
6666

67-
This is particularly useful when rendering a bundle that leverages webpack's on-demand code splitting features: we can ensure the optimal 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).
67+
The benefits is two-fold:
68+
69+
1. It can replace `html-webpack-plugin` for injecting the correct asset URLs when there are hashes in your generated filenames.
70+
71+
2. When rendering a bundle that leverages webpack's on-demand code splitting features, we can ensure the optimal chunks are preloaded / prefetched, and also intelligently inject `<script>` tags for needed async chunks to avoid waterfall requests on the client, thus improving TTI (time-to-interactive).
6872

6973
To make use of the client manifest, the client config would look something like this:
7074

@@ -97,7 +101,7 @@ You can then use the generated client manifest, together with a page template:
97101
const { createBundleRenderer } = require('vue-server-renderer')
98102

99103
const template = require('fs').readFileSync('/path/to/template.html', 'utf-8')
100-
const serverBundle = require('/path/to/vue-ssr-bundle.json')
104+
const serverBundle = require('/path/to/vue-ssr-server-bundle.json')
101105
const clientManifest = require('/path/to/vue-ssr-client-manifest.json')
102106

103107
const renderer = createBundleRenderer(serverBundle, {
@@ -130,13 +134,71 @@ With this setup, your server-rendered HTML for a build with code-splitting will
130134
</html>`
131135
```
132136

133-
## More About Asset Injection
137+
### Manual Asset Injection
138+
139+
Sometimes you might want finer-grained control over how assets are injected into the template, or maybe you are not using a template at all. In the `renderToString` callback, the `context` object you passed in will expose the following methods:
140+
141+
- `context.renderStyles()`
142+
143+
This will return inline `<style>` tags containing all the critical CSS collected from the `*.vue` components used during the render. See [CSS Management](./css.md) for more details.
144+
145+
If a `clientManifest` is provided, the returned string will also contain `<link rel="stylesheet">` tags for webpack-emitted CSS files (e.g. CSS extracted with `extract-text-webpack-plugin` or imported with `file-loader`)
146+
147+
- `context.renderState(options?: Object)`
148+
149+
This method serializes `context.state` and returns an inline script that embeds the state as `window.__INITIAL_STATE__`.
150+
151+
The context state key and window state key can both be customized by passing an options object:
152+
153+
``` js
154+
context.renderState({
155+
contextKey: 'myCustomState',
156+
windowKey: '__MY_STATE__'
157+
})
158+
159+
// -> <script>window.__MY_STATE__={...}</script>
160+
```
161+
162+
- `context.renderScripts()`
163+
164+
- requires `clientManifest`
165+
166+
This method returns the `<script>` tags needed for the client application to boot. When using async code-splitting in the app code, this method will intelligently infer the correct async chunks to include.
167+
168+
- `context.renderResourceHints()`
169+
170+
- requires `clientManifest`
171+
172+
This method returns the `<link rel="preload/prefetch">` resource hints needed for the current rendered page. By default it will:
173+
174+
- Preload the JavaScript and CSS files needed by the page
175+
- Prefetch async JavaScript chunks that might be needed later
176+
177+
Preloaded files can be further customized with the `shouldPreload` option.
178+
179+
Since the `template` passed to `createBundleRenderer` will be interpolated using `context`, you can make use of these methods inside the template (make sure to use triple-mustaches):
180+
181+
``` html
182+
<html>
183+
<head>
184+
{{{ renderResourceHints() }}}
185+
{{{ renderStyles() }}}
186+
</head>
187+
<body>
188+
<!--vue-ssr-outlet-->
189+
{{{ renderState() }}}
190+
{{{ renderScripts() }}}
191+
</body>
192+
</html>
193+
```
194+
195+
If you are not using `template` at all, you can concatenate the strings yourself.
134196

135197
### `shouldPreload`
136198

137-
By default, only JavaScript chunks used during the server-side render will be preloaded, as they are absolutely needed for your application to boot.
199+
By default, only JavaScript and CSS files will be preloaded, as they are absolutely needed for your application to boot.
138200

139-
For other types of assets such as images or fonts, how much and what to preload will be scenario-dependent. You can control precisely what to preload using the `shouldPreload` option:
201+
For other types of assets such as images or fonts, preloading too much may waste bandwidth and even hurt performance, so what to preload will be scenario-dependent. You can control precisely what to preload using the `shouldPreload` option:
140202

141203
``` js
142204
const renderer = createBundleRenderer(bundle, {
@@ -145,7 +207,7 @@ const renderer = createBundleRenderer(bundle, {
145207
shouldPreload: (file, type) => {
146208
// type is inferred based on the file extension.
147209
// https://fetch.spec.whatwg.org/#concept-request-destination
148-
if (type === 'script') {
210+
if (type === 'script' || type === 'style') {
149211
return true
150212
}
151213
if (type === 'font') {
@@ -159,7 +221,3 @@ const renderer = createBundleRenderer(bundle, {
159221
}
160222
})
161223
```
162-
163-
### Injection without Template
164-
165-

en/bundle-renderer.md

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,22 @@
22

33
## Problems with Basic SSR
44

5-
In our basic usage example, we directly required Vue and created an app instance in our Node.js server code. This is straightforward, however has quite a few issues in practice:
5+
Up to this point, we have assumed that the bundled server-side code will be directly used by the server via `require`:
66

7-
- Typical Vue apps are built with webpack and `vue-loader`, and many webpack-specific features such as importing files via `file-loader`, importing CSS via `css-loader` would not work directly in Node.js.
8-
9-
- Although the latest version of Node.js fully supports ES2015 features, we still need to transpile client-side code to cater to older browsers. This again involves a build step.
10-
11-
- Getting source maps to work with Node.js can be tricky.
7+
``` js
8+
const createApp = require('/path/to/built-server-bundle.js')
9+
```
1210

13-
- Directly requiring Vue app code in the server process means whenever you edit your app source code, you would have to stop and restart the server. Ideally, we want our server-rendering logic to be hot-reloadable too!
11+
This is straightforward, however whenever you edit your app source code, you would have to stop and restart the server. This hurts productivity quite a bit during development. In addition, Node.js doesn't support source maps natively.
1412

1513
## Enter BundleRenderer
1614

17-
![architecture](https://cloud.githubusercontent.com/assets/499550/17607895/786a415a-5fee-11e6-9c11-45a2cfdf085c.png)
18-
19-
`vue-server-renderer` provides an API called `createBundleRenderer` to deal with these problems. The basic idea is that we use the same Vue app source code to generate two bundles - one for the client and one for the server. With a custom webpack plugin, the server bundle is generated as a special JSON file that can be passed to the bundle renderer. Once the bundle renderer is created, usage is the same as the normal renderer, however the bundle renderer provides the following benefits:
15+
`vue-server-renderer` provides an API called `createBundleRenderer` to deal with this problem. With a custom webpack plugin, the server bundle is generated as a special JSON file that can be passed to the bundle renderer. Once the bundle renderer is created, usage is the same as the normal renderer, however the bundle renderer provides the following benefits:
2016

21-
- Reuse the vast majority of the app source code and build configuration for both server and client
22-
23-
- Built-in source map support for runtime errors
17+
- Built-in source map support (with `devtool: 'source-map'`)
2418

2519
- Hot-reload during development and even deployment (by simply reading the updated bundle and re-creating the renderer instance)
2620

27-
- Seamlessly supports webpack code-splitting (lazy loading)
28-
2921
- Critical CSS injection (when using `*.vue` files): automatically inlines the CSS needed by components used during the render. See the [CSS](./css.md) section for more details.
3022

3123
- Asset injection with [clientManifest](./client-manifest.md): automatically infers the optimal preload and prefetch directives, and the code-split chunks needed for the initial render.
@@ -35,13 +27,12 @@ In our basic usage example, we directly required Vue and created an app instance
3527
We will discuss how to configure webpack to generate the build artifacts needed by the bundle renderer in the next section, but for now let's assume we already have what we need, and this is how to create a use a bundle renderer:
3628

3729
``` js
38-
const renderer = require('vue-server-renderer').createBundleRenderer(
39-
bundle, // server bundle generated by the server build
40-
{
41-
template, // page template
42-
clientManifest // client build manifest generated by the client build
43-
}
44-
)
30+
const { createBundleRenderer } = require('vue-server-renderer')
31+
32+
const renderer = createBundleRenderer(serverBundle, {
33+
template, // (optinal) page template
34+
clientManifest // (optional) client build manifest
35+
})
4536

4637
// inside a server handler...
4738
server.get('*', (req, res) => {
@@ -54,3 +45,5 @@ server.get('*', (req, res) => {
5445
})
5546
})
5647
```
48+
49+
When `rendertoString` is called on a bundle renderer, it will automatically execute the function exported by the bundle to create an app instance (passing `context` as the argument) , and then render it.

en/data.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,18 @@ export default context => {
148148
}
149149
```
150150

151+
When using `template`, `context.state` will automatically be embedded in the final HTML as `window.__INITIAL__` state. On the client, the store should pick up the state before mounting the application:
152+
153+
``` js
154+
// entry-client.js
155+
156+
const { app, router, store } = createApp()
157+
158+
if (window.__INITIAL_STATE__) {
159+
store.replaceState(window.__INITIAL_STATE__)
160+
}
161+
```
162+
151163
## Client Data Fetching
152164

153165
On the client, there are two different approaches for handling data fetching:

en/structure.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ So far, we haven't discussed how to deliver the same Vue app to the client yet.
5353

5454
So the basic idea is we will be using webpack to bundle our app for both client and server - the server bundle will be required by the server and used for SSR, while the client bundle is sent to the browser to hydrate the static markup.
5555

56+
![architecture](https://cloud.githubusercontent.com/assets/499550/17607895/786a415a-5fee-11e6-9c11-45a2cfdf085c.png)
57+
5658
We will discuss the details of the setup in later sections - for now, let's just assume we've got the build setup figured out and we can write our Vue app code with webpack enabled.
5759

5860
## Code Structure with Webpack

0 commit comments

Comments
 (0)