基础
- onReady:onReady 是 Vue Router 的一个钩子函数,用于在路由导航完成后执行特定的操作,它可以在路由导航完成后执行异步任务、处理动画效果或其他需要在视图渲染之后执行的逻辑
- createRenderer:用于在服务器端进行 Vue.js 应用程序渲染的 Vue 2.x 库模块。它允许您在服务器上将 Vue 组件渲染为 HTML 字符串,从而实现服务器端渲染
实践
index.js
import Koa2 from 'koa'
import staticFiles from 'koa-static'
import { createRenderer } from 'vue-server-renderer'
import Vue from 'vue'
import App from './App.vue'
import VueMeta from 'vue-meta'
import { createRouter, routerReady } from './route'
import { createStore } from './pages/vuex/store'
import { getServerAxios } from './util/getAxios'
Vue.use(VueMeta)
const renderer = createRenderer()
const app = new Koa2()
app.use(staticFiles('public'))
app.use(async function (ctx) {
const req = ctx.request
if (req.path === '/favicon.ico') {
ctx.body = ''
return false
}
const router = createRouter()
const store = createStore(getServerAxios(ctx))
const vm = new Vue({
router,
store,
render: (h) => h(App),
})
const meta_obj = vm.$meta()
router.push(req.url)
await routerReady(router)
const matchedComponents = router.getMatchedComponents()
if (!matchedComponents.length) {
ctx.body = '没有找到该网页,404'
return
}
ctx.set('Content-Type', 'text/html;charset=utf-8')
let htmlString
const context = { title: 'hello' }
try {
await Promise.all(
matchedComponents.map((Component) => {
if (Component.asyncData) {
return Component.asyncData({
store,
route: router.currentRoute,
})
}
})
)
htmlString = await renderer.renderToString(vm, context)
} catch (error) {
ctx.status = 500
ctx.body = 'Internal Server Error'
}
const result = meta_obj.inject()
const { title, meta } = result
ctx.body = `<html>
<head>
${title ? title.text() : ''}
${meta ? meta.text() : ''}
${context.styles ? context.styles : ''}
</head>
<body>
${htmlString}
<script>
var context = {
state: ${JSON.stringify(store.state)}
}
</script>
<script src="./index.js"></script>
</body>
</html>`
})
app.listen(3000)
route.js
export const routerReady = async (router) => {
return new Promise((resolve) => {
router.onReady(() => {
resolve(null);
});
});
};
client/index.js
import Vue from 'vue'
import App from '../App.vue'
import { createRouter } from '../route'
import VueMeta from 'vue-meta'
import { createStore } from '../pages/vuex/store'
import { getClientAxios } from '../util/getAxios'
Vue.config.productionTip = false
Vue.use(VueMeta)
const router = createRouter()
const store = createStore(getClientAxios())
if (window.context && window.context.state) {
store.replaceState(window.context.state)
}
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#root', true)