Skip to content

Commit 6c9dd49

Browse files
committed
more elegant title handling
1 parent 3e603e8 commit 6c9dd49

File tree

7 files changed

+60
-28
lines changed

7 files changed

+60
-28
lines changed

server.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ function render (req, res) {
106106
}
107107
}
108108

109-
renderer.renderToString({ url: req.url }, (err, html) => {
109+
const context = {
110+
title: 'Vue HN 2.0', // default title
111+
url: req.url
112+
}
113+
renderer.renderToString(context, (err, html) => {
110114
if (err) {
111115
return handleError(err)
112116
}

src/app.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ import App from './App.vue'
33
import { createStore } from './store'
44
import { createRouter } from './router'
55
import { sync } from 'vuex-router-sync'
6+
import titleMixin from './util/title'
67
import * as filters from './util/filters'
78

9+
// mixin for handling title
10+
Vue.mixin(titleMixin)
11+
812
// register global utility filters.
913
Object.keys(filters).forEach(key => {
1014
Vue.filter(key, filters[key])
1115
})
1216

1317
// Expose a factory function that creates a fresh set of store, router,
1418
// app instances on each call (which is called for each SSR request)
15-
export function createApp () {
19+
export function createApp (ssrContext) {
1620
// create store and router instances
1721
const store = createStore()
1822
const router = createRouter()
@@ -22,11 +26,12 @@ export function createApp () {
2226
sync(store, router)
2327

2428
// create the app instance.
25-
// here we inject the router and store to all child components,
29+
// here we inject the router, store and ssr context to all child components,
2630
// making them available everywhere as `this.$router` and `this.$store`.
2731
const app = new Vue({
2832
router,
2933
store,
34+
ssrContext,
3035
render: h => h(App)
3136
})
3237

src/entry-server.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const isDev = process.env.NODE_ENV !== 'production'
1010
export default context => {
1111
return new Promise((resolve, reject) => {
1212
const s = isDev && Date.now()
13-
const { app, router, store } = createApp()
13+
const { app, router, store } = createApp(context)
1414

1515
// set router's location
1616
router.push(context.url)
@@ -29,8 +29,7 @@ export default context => {
2929
Promise.all(matchedComponents.map(component => {
3030
return component.asyncData && component.asyncData({
3131
store,
32-
route: router.currentRoute,
33-
ssrContext: context
32+
route: router.currentRoute
3433
})
3534
})).then(() => {
3635
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)

src/util/title.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
1-
export const setTitle = (title, context) => {
2-
title = `Vue HN 2.0 | ${title}`
3-
if (context) {
4-
// server
5-
context.title = title
6-
} else {
7-
// client
8-
document.title = title
1+
function getTitle (vm) {
2+
const { title } = vm.$options
3+
if (title) {
4+
return typeof title === 'function'
5+
? title.call(vm)
6+
: title
97
}
108
}
9+
10+
const serverTitleMixin = {
11+
created () {
12+
const title = getTitle(this)
13+
if (title) {
14+
this.$root.$options.ssrContext.title = `Vue HN 2.0 | ${title}`
15+
}
16+
}
17+
}
18+
19+
const clientTitleMixin = {
20+
mounted () {
21+
const title = getTitle(this)
22+
if (title) {
23+
document.title = `Vue HN 2.0 | ${title}`
24+
}
25+
}
26+
}
27+
28+
export default process.env.VUE_ENV === 'server'
29+
? serverTitleMixin
30+
: clientTitleMixin

src/views/CreateListView.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ export default function createListView (type) {
1010
return {
1111
name: `${type}-stories-view`,
1212

13-
asyncData ({ store, ssrContext }) {
14-
return store.dispatch('FETCH_LIST_DATA', { type }).then(() => {
15-
setTitle(camelize(type), ssrContext)
16-
})
13+
asyncData ({ store }) {
14+
return store.dispatch('FETCH_LIST_DATA', { type })
1715
},
1816

17+
title: camelize(type),
18+
1919
render (h) {
2020
return h(ItemList, { props: { type }})
2121
}

src/views/ItemView.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ export default {
4949
// We only fetch the item itself before entering the view, because
5050
// it might take a long time to load threads with hundreds of comments
5151
// due to how the HN Firebase API works.
52-
asyncData ({ store, route: { params: { id }}, ssrContext }) {
53-
return store.dispatch('FETCH_ITEMS', { ids: [id] }).then(() => {
54-
const item = store.state.items[id]
55-
setTitle(item.title, ssrContext)
56-
})
52+
asyncData ({ store, route: { params: { id }}}) {
53+
return store.dispatch('FETCH_ITEMS', { ids: [id] })
54+
},
55+
56+
title () {
57+
return this.item.title
5758
},
5859
5960
// Fetch comments when mounted on the client

src/views/UserView.vue

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ export default {
3030
}
3131
},
3232
33-
asyncData ({ store, route: { params: { id }}, ssrContext }) {
34-
return store.dispatch('FETCH_USER', { id }).then(() => {
35-
const user = store.state.users[id]
36-
setTitle(user ? user.id : 'User not found', ssrContext)
37-
})
33+
asyncData ({ store, route: { params: { id }}}) {
34+
return store.dispatch('FETCH_USER', { id })
35+
},
36+
37+
title () {
38+
return this.user
39+
? this.user.id
40+
: 'User not found'
3841
}
3942
}
4043
</script>

0 commit comments

Comments
 (0)