這篇文章給大家介紹怎么在vue中使用ssr+koa2構(gòu)建服務(wù)端渲染,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
Vue是一套用于構(gòu)建用戶界面的漸進(jìn)式JavaScript框架,Vue與其它大型框架的區(qū)別是,使用Vue可以自底向上逐層應(yīng)用,其核心庫只關(guān)注視圖層,方便與第三方庫和項(xiàng)目整合,且使用Vue可以采用單文件組件和Vue生態(tài)系統(tǒng)支持的庫開發(fā)復(fù)雜的單頁應(yīng)用。
服務(wù)端渲染
服務(wù)端渲染和單頁面渲染區(qū)別
查看下面兩張圖,可以看到如果是服務(wù)端渲染,那么在瀏覽器中拿到的直接是完整的 html 結(jié)構(gòu)。而單頁面是一些 script 標(biāo)簽引入的js文件,最終將虛擬dom去掛在到 #app
容器上。
@vue/cli 4 來構(gòu)建項(xiàng)目結(jié)構(gòu)
下面代碼使用最精簡的實(shí)例完整代碼會放到 github 上
step1 安裝最新的腳手架初始化項(xiàng)目
yarn global add @vue/cli
step2 添加服務(wù)端文件
啟動一個(gè) web 服務(wù)下方代碼中 http://localhost:9000
就是我們最終要訪問到地址
const Koa = require('koa') const path = require('path') const resolve = file => path.resolve(__dirname, file) const app = new Koa() const router = require('./router') const port = 9000 app.listen(port, () => { console.log(`server started at localhost:${port}`) }) module.exports = app
這里只是啟動了服務(wù),我們需要在去讀取服務(wù)端和客戶端到文件,下面代碼就是服務(wù)端渲染的關(guān)鍵步驟
const fs = require('fs') const path = require('path') const send = require('koa-send') const Router = require('koa-router') const router = new Router() // 獲取當(dāng)前文件的絕對路徑 const resolve = file => path.resolve(__dirname, file) const { createBundleRenderer } = require('vue-server-renderer') const bundle = require('../dist/vue-ssr-server-bundle.json') const clientManifest = require('../dist/vue-ssr-client-manifest.json') // 創(chuàng)建一個(gè) BunleRender 實(shí)例用于 renderer.renderToString 將 bundle 渲染為字符串 const renderer = createBundleRenderer(bundle, { runInNewContext: false, template: fs.readFileSync(resolve('../src/index.temp.html'), 'utf-8'), clientManifest: clientManifest }) const handleRequest = async ctx => { ctx.res.setHeader('Content-Type', 'text/html') // 在 2.5.0+ 版本中,此 callback 回調(diào)函數(shù)是可選項(xiàng)。在不傳遞 callback 時(shí),此方法返回一個(gè) Promise 對象,在其 resolve 后返回最終渲染的 HTML。 ctx.body = await renderer.renderToString(Object.assign({}, ctx.state.deliver, { url })) } router.get('/home',handleRequest) module.exports = router
vue-server-render
提供一個(gè)名為 createBundleRenderer 的 API 使用方法如下
const { createBundleRenderer } = require('vue-server-renderer') const renderer = createBundleRenderer(serverBundle, { runInNewContext: false, // 推薦 template, // (可選)頁面模板 clientManifest // (可選)客戶端構(gòu)建 manifest })
通過上面的 createBundleRenderer 方法生產(chǎn) render 對象最終將 bunlde 渲染為字符串,將最終的 html 返回給客戶端。
bundleRenderer.renderToString([context, callback]): ?Promise<string>
step3 添加 entry-client.js,entry-server.js 入口文件
在 src 中除了這兩個(gè)入口文件,其他的文件都是在客戶端和服務(wù)端公用的。來看下這兩個(gè)入口文件中分別干了什么。
大體的流程就是:服務(wù)端創(chuàng)建 vue 實(shí)例,將頁面中的異步請求的數(shù)據(jù)拿到存儲在容器中 --> 客戶端接收到服務(wù)端發(fā)送的 html 以激活模式進(jìn)行掛載,自動給根元素 #app 上添加 data-server-rendered="true"
特殊屬性
main.js
import Vue from 'vue' import App from './App.vue' ... export function createApp() { // ... const app = new Vue({ router, store, render: h => h(App) }) return { app, router, store } }
entry-server.js
import { createApp } from './main.js' export default context => { // 因?yàn)橛锌赡軙钱惒铰酚摄^子函數(shù)或組件,所以我們將返回一個(gè) Promise, // 以便服務(wù)器能夠等待所有的內(nèi)容在渲染前, // 就已經(jīng)準(zhǔn)備就緒。 return new Promise((resolve, reject) => { const { app, router, store } = createApp() // 設(shè)置服務(wù)器端 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.all( matchedComponents.map(component => { if (component.asyncData) { return component.asyncData({ store, context, route: router.currentRoute }) } }) ).then(() => { // 在所有預(yù)取鉤子(preFetch hook) resolve 后, // 我們的 store 現(xiàn)在已經(jīng)填充入渲染應(yīng)用程序所需的狀態(tài)。 // 當(dāng)我們將狀態(tài)附加到上下文, // 并且 `template` 選項(xiàng)用于 renderer 時(shí), // 狀態(tài)將自動序列化為 `window.__INITIAL_STATE__`,并注入 HTML。 // 否則會導(dǎo)致客戶端和服務(wù)端數(shù)據(jù)不統(tǒng)一造成渲染錯(cuò)誤 context.state = store.state resolve(app) }).catch(reject) }, reject) }) }
entry-client.js
import { createApp } from './main' const { app, router, store } = createApp() if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) } router.onReady(() => { router.beforeResolve((to, from, next) => { const matched = router.getMatchedComponents(to) const prevMatched = router.getMatchedComponents(from) let diffed = false const activated = matched.filter((c, i) => { return diffed || (diffed = prevMatched[i] !== c) }) if (!activated.length) { return next() } Promise.all( activated.map(component => { if (component.asyncData) { component.asyncData({ store, route: to }) } }) ) .then(() => { next() }) .catch(next) }) app.$mount('#app') })
最后
完整代碼參考 github地址
順便貼上這張圖
關(guān)于怎么在vue中使用ssr+koa2構(gòu)建服務(wù)端渲染就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
分享標(biāo)題:怎么在vue中使用ssr+koa2構(gòu)建服務(wù)端渲染-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://jinyejixie.com/article14/dphcge.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、軟件開發(fā)、定制開發(fā)、小程序開發(fā)、網(wǎng)站收錄、品牌網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容