Vue.js SSR 路由和代碼分割

2021-01-07 15:58 更新

使用 vue-router 的路由

你可能已經(jīng)注意到,我們的服務(wù)器代碼使用了一個 * 處理程序,它接受任意 URL。這允許我們將訪問的 URL 傳遞到我們的 Vue 應(yīng)用程序中,然后對客戶端和服務(wù)器復(fù)用相同的路由配置!

為此,建議使用官方提供的 vue-router。我們首先創(chuàng)建一個文件,在其中創(chuàng)建 router。注意,類似于 createApp,我們也需要給每個請求一個新的 router 實(shí)例,所以文件導(dǎo)出一個 createRouter 函數(shù):

  1. // router.js
  2. import Vue from 'vue'
  3. import Router from 'vue-router'
  4. Vue.use(Router)
  5. export function createRouter () {
  6. return new Router({
  7. mode: 'history',
  8. routes: [
  9. // ...
  10. ]
  11. })
  12. }

然后更新 app.js:

  1. // app.js
  2. import Vue from 'vue'
  3. import App from './App.vue'
  4. import { createRouter } from './router'
  5. export function createApp () {
  6. // 創(chuàng)建 router 實(shí)例
  7. const router = createRouter()
  8. const app = new Vue({
  9. // 注入 router 到根 Vue 實(shí)例
  10. router,
  11. render: h => h(App)
  12. })
  13. // 返回 app 和 router
  14. return { app, router }
  15. }

現(xiàn)在我們需要在 entry-server.js 中實(shí)現(xiàn)服務(wù)器端路由邏輯 (server-side routing logic):

  1. // entry-server.js
  2. import { createApp } from './app'
  3. export default context => {
  4. // 因?yàn)橛锌赡軙钱惒铰酚摄^子函數(shù)或組件,所以我們將返回一個 Promise,
  5. // 以便服務(wù)器能夠等待所有的內(nèi)容在渲染前,
  6. // 就已經(jīng)準(zhǔn)備就緒。
  7. return new Promise((resolve, reject) => {
  8. const { app, router } = createApp()
  9. // 設(shè)置服務(wù)器端 router 的位置
  10. router.push(context.url)
  11. // 等到 router 將可能的異步組件和鉤子函數(shù)解析完
  12. router.onReady(() => {
  13. const matchedComponents = router.getMatchedComponents()
  14. // 匹配不到的路由,執(zhí)行 reject 函數(shù),并返回 404
  15. if (!matchedComponents.length) {
  16. return reject({ code: 404 })
  17. }
  18. // Promise 應(yīng)該 resolve 應(yīng)用程序?qū)嵗员闼梢凿秩? resolve(app)
  19. }, reject)
  20. })
  21. }

假設(shè)服務(wù)器 bundle 已經(jīng)完成構(gòu)建(請?jiān)俅魏雎袁F(xiàn)在的構(gòu)建設(shè)置),服務(wù)器用法看起來如下:

  1. // server.js
  2. const createApp = require('/path/to/built-server-bundle.js')
  3. server.get('*', (req, res) => {
  4. const context = { url: req.url }
  5. createApp(context).then(app => {
  6. renderer.renderToString(app, (err, html) => {
  7. if (err) {
  8. if (err.code === 404) {
  9. res.status(404).end('Page not found')
  10. } else {
  11. res.status(500).end('Internal Server Error')
  12. }
  13. } else {
  14. res.end(html)
  15. }
  16. })
  17. })
  18. })

代碼分割

應(yīng)用程序的代碼分割或惰性加載,有助于減少瀏覽器在初始渲染中下載的資源體積,可以極大地改善大體積 bundle 的可交互時間(TTI - time-to-interactive)。這里的關(guān)鍵在于,對初始首屏而言,"只加載所需"。

Vue 提供異步組件作為第一類的概念,將其與 webpack 2 所支持的使用動態(tài)導(dǎo)入作為代碼分割點(diǎn) 相結(jié)合,你需要做的是:

  1. // 這里進(jìn)行修改……
  2. import Foo from './Foo.vue'
  3. // 改為這樣:
  4. const Foo = () => import('./Foo.vue')

在 Vue 2.5 以下的版本中,服務(wù)端渲染時異步組件只能用在路由組件上。然而在 2.5+ 的版本中,得益于核心算法的升級,異步組件現(xiàn)在可以在應(yīng)用中的任何地方使用。

需要注意的是,你仍然需要在掛載 app 之前調(diào)用 router.onReady,因?yàn)槁酚善鞅仨氁崆敖馕雎酚膳渲弥械漠惒浇M件,才能正確地調(diào)用組件中可能存在的路由鉤子。這一步我們已經(jīng)在我們的服務(wù)器入口 (server entry) 中實(shí)現(xiàn)過了,現(xiàn)在我們只需要更新客戶端入口 (client entry):

  1. // entry-client.js
  2. import { createApp } from './app'
  3. const { app, router } = createApp()
  4. router.onReady(() => {
  5. app.$mount('#app')
  6. })

異步路由組件的路由配置示例:

  1. // router.js
  2. import Vue from 'vue'
  3. import Router from 'vue-router'
  4. Vue.use(Router)
  5. export function createRouter () {
  6. return new Router({
  7. mode: 'history',
  8. routes: [
  9. { path: '/', component: () => import('./components/Home.vue') },
  10. { path: '/item/:id', component: () => import('./components/Item.vue') }
  11. ]
  12. })
  13. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號