vue-router
的路由
你可能已經注意到,我們的服務器代碼使用了一個 *
處理程序,它接受任意 URL。這允許我們將訪問的 URL 傳遞到我們的 Vue 應用程序中,然后對客戶端和服務器復用相同的路由配置!
為此,建議使用官方提供的 vue-router
。我們首先創(chuàng)建一個文件,在其中創(chuàng)建 router。注意,類似于 createApp
,我們也需要給每個請求一個新的 router 實例,所以文件導出一個 createRouter
函數(shù):
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
return new Router({
mode: 'history',
routes: [
// ...
]
})
}
然后更新 app.js
:
// app.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
export function createApp () {
// 創(chuàng)建 router 實例
const router = createRouter()
const app = new Vue({
// 注入 router 到根 Vue 實例
router,
render: h => h(App)
})
// 返回 app 和 router
return { app, router }
}
現(xiàn)在我們需要在 entry-server.js
中實現(xiàn)服務器端路由邏輯 (server-side routing logic):
// entry-server.js
import { createApp } from './app'
export default context => {
// 因為有可能會是異步路由鉤子函數(shù)或組件,所以我們將返回一個 Promise,
// 以便服務器能夠等待所有的內容在渲染前,
// 就已經準備就緒。
return new Promise((resolve, reject) => {
const { app, router } = createApp()
// 設置服務器端 router 的位置
router.push(context.url)
// 等到 router 將可能的異步組件和鉤子函數(shù)解析完
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// 匹配不到的路由,執(zhí)行 reject 函數(shù),并返回 404
if (!matchedComponents.length) {
return reject({ code: 404 })
}
// Promise 應該 resolve 應用程序實例,以便它可以渲染
resolve(app)
}, reject)
})
}
假設服務器 bundle 已經完成構建(請再次忽略現(xiàn)在的構建設置),服務器用法看起來如下:
// server.js
const createApp = require('/path/to/built-server-bundle.js')
server.get('*', (req, res) => {
const context = { url: req.url }
createApp(context).then(app => {
renderer.renderToString(app, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found')
} else {
res.status(500).end('Internal Server Error')
}
} else {
res.end(html)
}
})
})
})
應用程序的代碼分割或惰性加載,有助于減少瀏覽器在初始渲染中下載的資源體積,可以極大地改善大體積 bundle 的可交互時間(TTI - time-to-interactive)。這里的關鍵在于,對初始首屏而言,"只加載所需"。
Vue 提供異步組件作為第一類的概念,將其與 webpack 2 所支持的使用動態(tài)導入作為代碼分割點 相結合,你需要做的是:
// 這里進行修改……
import Foo from './Foo.vue'
// 改為這樣:
const Foo = () => import('./Foo.vue')
在 Vue 2.5 以下的版本中,服務端渲染時異步組件只能用在路由組件上。然而在 2.5+ 的版本中,得益于核心算法的升級,異步組件現(xiàn)在可以在應用中的任何地方使用。
需要注意的是,你仍然需要在掛載 app 之前調用 router.onReady
,因為路由器必須要提前解析路由配置中的異步組件,才能正確地調用組件中可能存在的路由鉤子。這一步我們已經在我們的服務器入口 (server entry) 中實現(xiàn)過了,現(xiàn)在我們只需要更新客戶端入口 (client entry):
// entry-client.js
import { createApp } from './app'
const { app, router } = createApp()
router.onReady(() => {
app.$mount('#app')
})
異步路由組件的路由配置示例:
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
return new Router({
mode: 'history',
routes: [
{ path: '/', component: () => import('./components/Home.vue') },
{ path: '/item/:id', component: () => import('./components/Item.vue') }
]
})
}
更多建議: