Commit 6c9dd49a authored by Evan You's avatar Evan You

more elegant title handling

parent 3e603e88
......@@ -106,7 +106,11 @@ function render (req, res) {
}
}
renderer.renderToString({ url: req.url }, (err, html) => {
const context = {
title: 'Vue HN 2.0', // default title
url: req.url
}
renderer.renderToString(context, (err, html) => {
if (err) {
return handleError(err)
}
......
......@@ -3,8 +3,12 @@ import App from './App.vue'
import { createStore } from './store'
import { createRouter } from './router'
import { sync } from 'vuex-router-sync'
import titleMixin from './util/title'
import * as filters from './util/filters'
// mixin for handling title
Vue.mixin(titleMixin)
// register global utility filters.
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
......@@ -12,7 +16,7 @@ Object.keys(filters).forEach(key => {
// Expose a factory function that creates a fresh set of store, router,
// app instances on each call (which is called for each SSR request)
export function createApp () {
export function createApp (ssrContext) {
// create store and router instances
const store = createStore()
const router = createRouter()
......@@ -22,11 +26,12 @@ export function createApp () {
sync(store, router)
// create the app instance.
// here we inject the router and store to all child components,
// here we inject the router, store and ssr context to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = new Vue({
router,
store,
ssrContext,
render: h => h(App)
})
......
......@@ -10,7 +10,7 @@ const isDev = process.env.NODE_ENV !== 'production'
export default context => {
return new Promise((resolve, reject) => {
const s = isDev && Date.now()
const { app, router, store } = createApp()
const { app, router, store } = createApp(context)
// set router's location
router.push(context.url)
......@@ -29,8 +29,7 @@ export default context => {
Promise.all(matchedComponents.map(component => {
return component.asyncData && component.asyncData({
store,
route: router.currentRoute,
ssrContext: context
route: router.currentRoute
})
})).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
......
export const setTitle = (title, context) => {
title = `Vue HN 2.0 | ${title}`
if (context) {
// server
context.title = title
} else {
// client
document.title = title
function getTitle (vm) {
const { title } = vm.$options
if (title) {
return typeof title === 'function'
? title.call(vm)
: title
}
}
const serverTitleMixin = {
created () {
const title = getTitle(this)
if (title) {
this.$root.$options.ssrContext.title = `Vue HN 2.0 | ${title}`
}
}
}
const clientTitleMixin = {
mounted () {
const title = getTitle(this)
if (title) {
document.title = `Vue HN 2.0 | ${title}`
}
}
}
export default process.env.VUE_ENV === 'server'
? serverTitleMixin
: clientTitleMixin
......@@ -10,12 +10,12 @@ export default function createListView (type) {
return {
name: `${type}-stories-view`,
asyncData ({ store, ssrContext }) {
return store.dispatch('FETCH_LIST_DATA', { type }).then(() => {
setTitle(camelize(type), ssrContext)
})
asyncData ({ store }) {
return store.dispatch('FETCH_LIST_DATA', { type })
},
title: camelize(type),
render (h) {
return h(ItemList, { props: { type }})
}
......
......@@ -49,11 +49,12 @@ export default {
// We only fetch the item itself before entering the view, because
// it might take a long time to load threads with hundreds of comments
// due to how the HN Firebase API works.
asyncData ({ store, route: { params: { id }}, ssrContext }) {
return store.dispatch('FETCH_ITEMS', { ids: [id] }).then(() => {
const item = store.state.items[id]
setTitle(item.title, ssrContext)
})
asyncData ({ store, route: { params: { id }}}) {
return store.dispatch('FETCH_ITEMS', { ids: [id] })
},
title () {
return this.item.title
},
// Fetch comments when mounted on the client
......
......@@ -30,11 +30,14 @@ export default {
}
},
asyncData ({ store, route: { params: { id }}, ssrContext }) {
return store.dispatch('FETCH_USER', { id }).then(() => {
const user = store.state.users[id]
setTitle(user ? user.id : 'User not found', ssrContext)
})
asyncData ({ store, route: { params: { id }}}) {
return store.dispatch('FETCH_USER', { id })
},
title () {
return this.user
? this.user.id
: 'User not found'
}
}
</script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment