摘要:從零開(kāi)始搭建一個(gè)背景是什么全拼是,服務(wù)端渲染。大家不妨可以打開(kāi)一些頁(yè)面或者一些公司的網(wǎng)站,查看源代碼,你會(huì)發(fā)現(xiàn),也是有這個(gè)標(biāo)記。這時(shí)候,我們發(fā)現(xiàn)頁(yè)面的路由切換生效了,并且不同頁(yè)面的源代碼也不一樣了。從零開(kāi)始搭建一個(gè)下項(xiàng)目源碼
從零開(kāi)始搭建一個(gè)vue-ssr 背景 What?SSR是什么?
SSR全拼是Server-Side Rendering,服務(wù)端渲染。
所謂服務(wù)端渲染,指的是把vue組件在服務(wù)器端渲染為組裝好的HTML字符串,然后將它們直接發(fā)送到瀏覽器,最后需要將這些靜態(tài)標(biāo)記混合在客戶端上完全可交互的應(yīng)用程序。
①滿足seo需求,傳統(tǒng)的spa數(shù)據(jù)都是異步加載的,爬蟲(chóng)引擎無(wú)法加載,需要利用ssr將數(shù)據(jù)直出渲染在頁(yè)面源代碼中。
②更寬的內(nèi)容達(dá)到時(shí)間(首屏加載更快),當(dāng)請(qǐng)求頁(yè)面的時(shí)候,服務(wù)端渲染完數(shù)據(jù)之后,把渲染好的頁(yè)面直接發(fā)送給瀏覽器,并進(jìn)行渲染。瀏覽器只需要解析html不需要去解析js。
借用下面的一張圖,我們來(lái)簡(jiǎn)單闡述一下vue-ssr的原理。
我們可以看到,左側(cè)Source部分就是我們所編寫的源代碼,所有代碼有一個(gè)公共入口,就是app.js,緊接著就是服務(wù)端的入口
(entry-server.js)和客戶端的入口(entry-client.js)。當(dāng)完成所有源代碼的編寫之后,我們通過(guò)webpack的構(gòu)建,打包出兩個(gè)bundle,分別是server bundle和client bundle;當(dāng)用戶進(jìn)行頁(yè)面訪問(wèn)的時(shí)候,先是經(jīng)過(guò)服務(wù)端的入口,將vue組建組裝為html字符串,并混入客戶端所訪問(wèn)的html模板中,最終就完成了整個(gè)ssr渲染的過(guò)程。
在終端輸入以下命令
mkdir ssr-demo cd ssr-demo npm init
由于我們這個(gè)只是一個(gè)demo項(xiàng)目,可以直接一路按回車鍵,直接忽略配置。
完成之后我們可以看到文件夾里面有一個(gè)package.json的文件,這就是配置表。
該項(xiàng)目需要四個(gè)依賴,依次安裝
npm install express npm install vue npm install vue-router npm install vue-server-renderer
其中express使我們node端的框架,vue用于創(chuàng)建vue實(shí)例,vue-router則用于實(shí)現(xiàn)路由控制,最后vue-server-renderer尤為關(guān)鍵,我們實(shí)現(xiàn)的vue-ssr依靠于這個(gè)庫(kù)提供的API。
在安裝依賴完畢之后,我們看到package.json中已經(jīng)把四個(gè)依賴都寫上了。
"express": "^4.17.1", "vue": "^2.6.10", "vue-router": "^3.0.6", "vue-server-renderer": "^2.6.10"創(chuàng)建一個(gè)node服務(wù)
在根目錄下我們新建一個(gè)server.js,用戶搭建node服務(wù)
const express = require("express"); const app = express(); app.get("*", (request, response) => { response.end("hello, ssr"); }) app.listen(3001, () => { console.log("服務(wù)已開(kāi)啟") })
接著為了后續(xù)開(kāi)發(fā)的便利,我們?cè)趐ackage.json中添加一個(gè)啟動(dòng)命令:
"scripts": { "test": "echo "Error: no test specified" && exit 1", "server": "node index.js" },
接著我們?cè)诮K端輸入 npm run server,然后再瀏覽器輸入localhost:3001,便可以看到頁(yè)面中的文字被成功渲染。
渲染html頁(yè)面在上一步我們已經(jīng)能成功渲染出一個(gè)文字,但是ssr并不是主要為了渲染文字,而是渲染一個(gè)html模板。
那么,接下來(lái),我們得告知瀏覽器,我們需要渲染的是html,而不只是text,因此我們需要修改響應(yīng)頭。
同時(shí),引入vue-server-renderer中的createRenderer對(duì)象,有一個(gè)renderToString的方法,可以將vue實(shí)例轉(zhuǎn)成html的形式。(renderToString這個(gè)方法接受的第一個(gè)參數(shù)是vue的實(shí)例,第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),如果不想使用回調(diào)函數(shù)的話,這個(gè)方法也返回了一個(gè)Promise對(duì)象,當(dāng)方法執(zhí)行成功之后,會(huì)在then函數(shù)里面返回html結(jié)構(gòu)。)
修改server.js如下:
const express = require("express"); const app = express(); const Vue = require("vue"); const vueServerRender = require("vue-server-renderer").createRenderer(); app.get("*", (request, response) => { const vueApp = new Vue({ data:{ message: "hello, ssr" }, template: `{{message}}
` }); response.status(200); response.setHeader("Content-type", "text/html;charset-utf-8"); vueServerRender.renderToString(vueApp).then((html) => { response.end(html); }).catch(err => console.log(err)) }) app.listen(3001, () => { console.log("服務(wù)已開(kāi)啟") })
保存代碼,重啟服務(wù),然后重新刷新頁(yè)面。我們發(fā)現(xiàn),頁(yè)面好像沒(méi)什么不同,就是字體變粗了而已。其實(shí)并不是,你可以嘗試查看頁(yè)面源代碼,我們發(fā)現(xiàn)在源代碼中,已經(jīng)存在一個(gè)標(biāo)簽對(duì)h1,這就是html模板的雛形。同時(shí),細(xì)心的同學(xué)還會(huì)發(fā)現(xiàn),h1上面有一個(gè)屬性:
data-server-rendered="true",那這個(gè)屬性是干什么的呢?這個(gè)是一個(gè)標(biāo)記,表明這個(gè)頁(yè)面是由vue-ssr渲染而來(lái)的。大家不妨可以打開(kāi)一些seo頁(yè)面或者一些公司的網(wǎng)站,查看源代碼,你會(huì)發(fā)現(xiàn),也是有這個(gè)標(biāo)記。
雖然h1標(biāo)簽對(duì)被成功渲染,但是我們發(fā)現(xiàn)這個(gè)html頁(yè)面并不完整, 他缺少了文檔聲明,html標(biāo)簽,body標(biāo)簽,title標(biāo)簽等。
創(chuàng)建一個(gè)index.html,用于掛載Vue實(shí)例。
Hello, SSR
注意,body中的注釋不能去掉,這是Vue掛載的占位符。
然后修改server.js,將html模板引進(jìn)去。這里我們?cè)赾reateRenderer函數(shù)可以接收一個(gè)對(duì)象作為配置參數(shù)。配置參數(shù)中有一項(xiàng)為template,這項(xiàng)配置的就是我們即將使用的Html模板。這個(gè)接收的不是一個(gè)單純的路徑,我們需要使用fs模塊將html模板讀取出來(lái)。
let path = require("path"); const vueServerRender = require("vue-server-renderer").createRenderer({ template:require("fs").readFileSync(path.join(__dirname,"./index.html"),"utf-8") });
保存代碼,重啟服務(wù),然后重新刷新頁(yè)面。我們查看源代碼,發(fā)現(xiàn),已經(jīng)能成功渲染出一個(gè)完整的頁(yè)面了。
創(chuàng)建一個(gè)Vue項(xiàng)目的開(kāi)發(fā)目錄上面的開(kāi)發(fā)模式,很顯然只是一個(gè)demo而已,接下來(lái)我們模擬一下正常的vue開(kāi)發(fā)的目錄結(jié)構(gòu)。
創(chuàng)建一個(gè)src文件夾,里面有一個(gè)router文件夾,再有一個(gè)index,js用作路由,并創(chuàng)建一個(gè)app.js,用作vue的入口,如下圖:
修改router/index.js
const vueRouter = require("vue-router"); const Vue = require("vue"); Vue.use(vueRouter); module.exports = () => { return new vueRouter({ mode:"history", routes:[ { path:"/", component:{ template:`this is home page
` }, name:"home" }, { path:"/about", component:{ template:`this is about page
` }, name:"about" } ] }) }
修改app.js
const Vue = require("vue"); const createRouter = require("./router") module.exports = (context) => { const router = createRouter(); return new Vue({ router, data:{ message:"Hello,Vue SSR!", }, template:`` }); }{{message}}
home about
然后在server.js中,將app.js引入
const express = require("express"); const app = express(); const vueApp = require("./src/app.js"); let path = require("path"); const vueServerRender = require("vue-server-renderer").createRenderer({ template:require("fs").readFileSync(path.join(__dirname,"./index.html"),"utf-8") }); app.get("*", (request, response) => { let vm = vueApp({}); response.status(200); response.setHeader("Content-type", "text/html;charset-utf-8"); vueServerRender.renderToString(vm).then((html) => { response.end(html); }).catch(err => console.log(err)) }) app.listen(3001, () => { console.log("服務(wù)已開(kāi)啟") })
保存代碼,重啟服務(wù),然后重新刷新頁(yè)面。然后我們可以看到瀏覽器的路由已經(jīng)被成功渲染了,但是無(wú)論怎么點(diǎn)擊都沒(méi)反應(yīng),瀏覽器的url有更改,但是頁(yè)面內(nèi)容不變。
這是因?yàn)槲覀冎皇菍㈨?yè)面渲染的工作交給服務(wù)端,而頁(yè)面路由切換,還是在前端執(zhí)行,服務(wù)端并未能接收到該指令,因此無(wú)論怎么切換路由,服務(wù)端渲染出來(lái)的頁(yè)面根本沒(méi)變化。
在src中創(chuàng)建一個(gè)entry-server.js文件,該文件為服務(wù)端入口文件,接收app和router實(shí)例:
const createApp = require("./app.js"); module.exports = (context) => { return new Promise(async (reslove,reject) => { let {url} = context; let {app,router} = createApp(context); router.push(url); // router回調(diào)函數(shù) // 當(dāng)所有異步請(qǐng)求完成之后就會(huì)觸發(fā) router.onReady(() => { let matchedComponents = router.getMatchedComponents(); if(!matchedComponents.length){ return reject(); } reslove(app); },reject) }) }
在src中創(chuàng)建一個(gè)entry-client.js文件,該文件為客戶端入口,負(fù)責(zé)將路由掛載到app里面。
const createApp = require("./app.js"); let {app,router} = createApp({}); router.onReady(() => { app.$mount("#app") });
修改app.js,將router和vue實(shí)例暴露出去
const Vue = require("vue"); const createRouter = require("./router") module.exports = (context) => { const router = createRouter(); const app = new Vue({ router, data:{ message:"Hello,Vue SSR!", }, template:`` }); return { app, router } }{{message}}
home about
最終修改server.js
const express = require("express"); const app = express(); const App = require("./src/entry-server.js"); let path = require("path"); const vueServerRender = require("vue-server-renderer").createRenderer({ template:require("fs").readFileSync(path.join(__dirname,"./index.html"),"utf-8") }); app.get("*", async(request, response) => { response.status(200); response.setHeader("Content-type", "text/html;charset-utf-8"); let {url} = request; let vm; vm = await App({url}) vueServerRender.renderToString(vm).then((html) => { response.end(html); }).catch(err => console.log(err)) }) app.listen(3001, () => { console.log("服務(wù)已開(kāi)啟") })
保存代碼,重啟服務(wù),然后重新刷新頁(yè)面。這時(shí)候,我們發(fā)現(xiàn)頁(yè)面的路由切換生效了,并且不同頁(yè)面的源代碼也不一樣了。
數(shù)據(jù)傳遞既然是服務(wù)端渲染,數(shù)據(jù)的接收也是來(lái)源于服務(wù)端,那怎樣才能把服務(wù)端接收到的數(shù)據(jù)傳輸給前端,然后進(jìn)行渲染呢?
修改entry-server.js,進(jìn)行同步或者異步獲取數(shù)據(jù)
const createApp = require("./app.js"); const getData = function(){ return new Promise((reslove, reject) => { let str = "this is a async data!"; reslove(str); }) } module.exports = (context) => { return new Promise(async (reslove,reject) => { let {url} = context; // 數(shù)據(jù)傳遞 context.propsData = "this is a data from props!" context.asyncData = await getData(); let {app,router} = createApp(context); router.push(url); // router回調(diào)函數(shù) // 當(dāng)所有異步請(qǐng)求完成之后就會(huì)觸發(fā) router.onReady(() => { let matchedComponents = router.getMatchedComponents(); if(!matchedComponents.length){ return reject(); } reslove(app); },reject) }) }
修改app.js,接收數(shù)據(jù)并渲染
const Vue = require("vue"); const createRouter = require("./router") module.exports = (context) => { const router = createRouter(); const app = new Vue({ router, data:{ message:"Hello,Vue SSR!", propsData: context.propsData, asyncData: context.asyncData }, template:`` }); return { app, router } }{{message}}
{{asyncData}}
{{propsData}}
home about
最后我們可以看到無(wú)論是同步還是異步獲取的數(shù)據(jù),都能成功地通過(guò)服務(wù)端渲染,展示在頁(yè)面源代碼中。
另外,你也可以在server.js中的request中,將數(shù)據(jù)傳遞下去。
實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版本的vue-ssr,下期我們會(huì)依賴于vue-cli,進(jìn)行webpack改造,實(shí)現(xiàn)一個(gè)通用且更實(shí)用的vue-ssr框架。從零開(kāi)始搭建一個(gè)vue-ssr(下)
項(xiàng)目源碼https://github.com/TheWalking...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105156.html
摘要:開(kāi)始改建補(bǔ)充安裝依賴與上一次不同,這次我們基于進(jìn)行改建,已經(jīng)有了很多依賴庫(kù)了,但我們?nèi)涡枰a(bǔ)充一個(gè)核心修改客戶端的配置修改文件,添加插件添加了這個(gè)配置以后,重新啟動(dòng)項(xiàng)目通過(guò)地址就可以訪問(wèn)到,頁(yè)面中出現(xiàn)的內(nèi)容就是所需要的。 從零開(kāi)始搭建一個(gè)vue-ssr 前言 上次我們已經(jīng)實(shí)現(xiàn)了從零開(kāi)始,搭建一個(gè)簡(jiǎn)單的vue-ssr的demo:從零開(kāi)始搭建一個(gè)vue-ssr(上)。那么這次呢,我們基于v...
摘要:無(wú)需使用服務(wù)器實(shí)時(shí)動(dòng)態(tài)編譯,而是使用預(yù)渲染方式,在構(gòu)建時(shí)簡(jiǎn)單地生成針對(duì)特定路由的靜態(tài)文件。與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁(yè)面應(yīng)用程序不同,服務(wù)器渲染應(yīng)用程序,需要處于運(yùn)行環(huán)境。更多的服務(wù)器端負(fù)載。 目錄結(jié)構(gòu) -no-ssr-demo 未做ssr之前的項(xiàng)目代碼用于對(duì)比 -vuecli2ssr 將vuecli生成的項(xiàng)目轉(zhuǎn)為ssr -prerender-demo 使用prer...
摘要:后端主要使用的框架,數(shù)據(jù)庫(kù)采用。后臺(tái)管理登錄采用與后端進(jìn)行登陸狀態(tài)的確認(rèn)。本文首發(fā)于小站,這是一個(gè)積累和分享知識(shí)的個(gè)人博客 這篇文章擱置了很長(zhǎng)時(shí)間,最終決定還是把它寫出來(lái),給剛開(kāi)始學(xué)習(xí)vue并且想用vue寫個(gè)人博客的同學(xué)一個(gè)參考。因?yàn)楫?dāng)初我也是參考了其他人分享的知識(shí),從一個(gè)vue小白變成了一個(gè)入門級(jí)選手,并最終完成了這個(gè)個(gè)人博客的搭建工作,代碼已托管在Github-justJokee。...
摘要:總算是今天成功把自己的孩子托付到阿里云的服務(wù)器上面了。中間還遇到很多很多坑最后看這自己所部署的三個(gè)網(wǎng)站安靜的躺在自己租的阿里云上。 一把桌子,一臺(tái)電腦,一瓶紅牛,一包紙巾,從白天到黑夜。歷經(jīng)一個(gè)多月的時(shí)間,從零到構(gòu)思到設(shè)計(jì),從設(shè)計(jì)到vue-ssr 的框架設(shè)計(jì),然后再?gòu)那岸说臉I(yè)務(wù)邏輯代碼的實(shí)現(xiàn),從 后臺(tái)nodejs 的 koa2框架到數(shù)據(jù)庫(kù)的設(shè)計(jì)到后端的業(yè)務(wù)邏輯的代碼實(shí)現(xiàn),從購(gòu)買阿里云服...
閱讀 1336·2023-04-25 23:47
閱讀 912·2021-11-23 09:51
閱讀 4432·2021-09-26 10:17
閱讀 3706·2021-09-10 11:19
閱讀 3254·2021-09-06 15:10
閱讀 3546·2019-08-30 12:49
閱讀 2421·2019-08-29 13:20
閱讀 1730·2019-08-28 18:14