Skip to content

Commit 836cb9a

Browse files
committed
head management
1 parent 0b5e520 commit 836cb9a

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

en/bundle-renderer.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ We will discuss how to configure webpack to generate the build artifacts needed
3030
const { createBundleRenderer } = require('vue-server-renderer')
3131

3232
const renderer = createBundleRenderer(serverBundle, {
33+
runInNewContext: false, // recommended
3334
template, // (optinal) page template
3435
clientManifest // (optional) client build manifest
3536
})
@@ -47,3 +48,11 @@ server.get('*', (req, res) => {
4748
```
4849

4950
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.
51+
52+
---
53+
54+
### The `runInNewContext` Option
55+
56+
By default, for each render the bundle renderer will create a fresh V8 context and re-execute the entire bundle. This has some benefits - for example, we don't need to worry about the "stateful singleton" problem we mentioned earlier. However, this mode comes at some considerable performance cost because re-executing the bundle is expensive especially when the app gets bigger.
57+
58+
In `vue-server-renderer >= 2.3.0`, this option still defaults to `true` for backwards compatibility, but it is recommended to use `runInNewContext: false` whenever you can.

en/head.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Head Management
2+
3+
Similar to asset injection, head management follows the same idea: we can dynamically attach data to the render `context` in a component's lifecycle, and then interpolate those data in `template`.
4+
5+
To do that we need to have access to the SSR context inside a nested component. We can simply pass the `context` to `createApp()` and expose it on the root instance's `$options`:
6+
7+
``` js
8+
// app.js
9+
10+
export function createApp (ssrContext) {
11+
// ...
12+
const app = new Vue({
13+
router,
14+
store,
15+
// all child components can access this as this.$root.$options.ssrContext
16+
ssrContext,
17+
render: h => h(App)
18+
})
19+
// ...
20+
}
21+
```
22+
23+
This can also be done via `provide/inject`, but since we know it's going to be on `$root`, we can avoid the injection resolution costs.
24+
25+
With the context injected, we can write a simple mixin to perform title management:
26+
27+
``` js
28+
// title-mixin.js
29+
30+
function getTitle (vm) {
31+
// components can simply provide a `title` option
32+
// which can be either a string or a function
33+
const { title } = vm.$options
34+
if (title) {
35+
return typeof title === 'function'
36+
? title.call(vm)
37+
: title
38+
}
39+
}
40+
41+
const serverTitleMixin = {
42+
created () {
43+
const title = getTitle(this)
44+
if (title) {
45+
this.$root.$options.ssrContext.title = title
46+
}
47+
}
48+
}
49+
50+
const clientTitleMixin = {
51+
mounted () {
52+
const title = getTitle(this)
53+
if (title) {
54+
document.title = `Vue HN 2.0 | ${title}`
55+
}
56+
}
57+
}
58+
59+
// VUE_ENV can be injected with webpack.DefinePlugin
60+
export default process.env.VUE_ENV === 'server'
61+
? serverTitleMixin
62+
: clientTitleMixin
63+
```
64+
65+
Now, a route component can make use of this to control the document title:
66+
67+
``` js
68+
// Item.vue
69+
export default {
70+
mixins: [titleMixin],
71+
title () {
72+
return this.item.title
73+
}
74+
75+
asyncData ({ store, route }) {
76+
return store.dispatch('fetchItem', route.params.id)
77+
},
78+
79+
computed: {
80+
item () {
81+
return this.$store.state.items[this.$route.params.id]
82+
}
83+
}
84+
}
85+
```
86+
87+
And inside the `template` passed to bundle renderer:
88+
89+
``` html
90+
<html>
91+
<head>
92+
<title>{{ title }}</title>
93+
</head>
94+
<body>
95+
...
96+
</body>
97+
</html>
98+
```
99+
100+
**Notes:**
101+
102+
- Use double-mustache (HTML-escaped interpolation) to avoid XSS attacks.
103+
104+
- You should provide a default title when creating the `context` object in case no component has set a title during render.
105+
106+
---
107+
108+
Using the same strategy, you can easily expand this mixin into a generic head management utility.

0 commit comments

Comments
 (0)