vue 服務(wù)端渲染(一):初探

概念

  • 客戶端渲染:在瀏覽器端進(jìn)行渲染,服務(wù)端返回的只是 <div id="app"></div> 的空標(biāo)簽文檔;
  • 服務(wù)端渲染:在服務(wù)器就做好數(shù)據(jù)的的拼接,請(qǐng)求返回的 html 是完整的文檔。

客戶端渲染,服務(wù)端渲染對(duì)比

  • 客戶端渲染不利于 SEO 搜索引擎優(yōu)化(返回的是空標(biāo)簽);
  • 服務(wù)端渲染出來的文檔可以被爬蟲抓取,客戶端異步渲染很難被爬蟲抓?。?/li>
  • 服務(wù)端渲染直接將 HTML 字符串傳遞給瀏覽器,大大加快了首屏加載時(shí)間;
  • 服務(wù)端渲染占用更多的 CPU 和內(nèi)存資源;
  • 在服務(wù)端渲染模式下,一些常用的瀏覽器 API 可能無法正常使用(服務(wù)端沒有 document,window);
  • 服務(wù)端渲染只支持 vue 中 beforeCreate 和 created 兩個(gè)生命周期(因?yàn)闆]有 dom,所以不涉及 mounted 等)

SSR 運(yùn)行過程:

  • 服務(wù)端渲染只是做首屏的渲染;
  • 后續(xù)在瀏覽器中的路由切換邏輯執(zhí)行的是客戶端渲染(前端路由切換頁面,每個(gè)路由返回的首屏是服務(wù)端來做)
  • 服務(wù)端渲染使用 Node 服務(wù)來實(shí)現(xiàn)(前后端分離模式)
  • 傳統(tǒng)的服務(wù)端渲染是 jsp asp php+smarty,不適合前后端分離開發(fā)模式

整個(gè)打包過程

  • 首先要保證客戶端渲染模式下可以跑起來,然后提供一個(gè)服務(wù)端入口
  • 通過將一份代碼打包出來兩份邏輯(客戶端和服務(wù)端)
  • 前端拿到打包出來的 bandle.js,后端通過打包的結(jié)果渲染出字符串返回給瀏覽器
  • 瀏覽器展示 = 前端 bundle.js + 服務(wù)端渲染的字符串
    如下圖:展示了打包過程:


安裝包

  • vue vue-server-renderer(通過vue-server-renderer來實(shí)現(xiàn)vue的服務(wù)端渲染)
  • koa koa-router(通過 node 來做服務(wù)端)
npm install vue vue-server-renderer koa @koa/router -D

開發(fā)中

本地新建一個(gè)文件夾 vue-ssr-demo,初始化項(xiàng)目npm init -f,生成一個(gè)package.json,npm install安裝需要用到的包,新建一個(gè)server.js來啟動(dòng)服務(wù):

// server.js
  const Vue = require('vue')
const VueServerRenderer = require('vue-server-renderer')

const vm = new Vue({
  data() {
    return {
      name: '小可愛',
      age: 3
    }
  },
  template: `<div>我是:{{name}},{{age}}歲</div>`
})

const Koa = require('koa')
const Router = require('@koa/router')
// 創(chuàng)建一個(gè)渲染器
const render = VueServerRenderer.createRenderer()// 創(chuàng)建一個(gè)渲染器

let app = new Koa() // app實(shí)例
let router = new Router() // 路由實(shí)例

router.get('/', async (ctx) => {
  ctx.body = await render.renderToString(vm) // render.renderToString 返回的是一個(gè)promise
})
app.use(router.routes())
app.listen(3500)

啟動(dòng)命令:nodemon server.js,VueServerRenderer.createRenderer()創(chuàng)建一個(gè)渲染器,調(diào)用渲染器 renderToString 方法 傳入vm實(shí)例,訪問http://localhost:3500/ 可以看到頁面展示:


頁面上顯示了data-server-rendered="true"表示服務(wù)端渲染。

通常我們都是有一個(gè) html 模板,然后將打包的內(nèi)容放進(jìn)去,新建一個(gè) html 文件,寫下vue server 的標(biāo)記

<!--vue-ssr-outlet-->
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue ssr template</title>
</head>
<body>
  <!--vue-ssr-outlet-->
</body>
</html>

server.js 中讀取本地的index.html文件,文件內(nèi)容作為渲染器模板即可:

const Vue = require('vue')
const VueServerRenderer = require('vue-server-renderer')

const vm = new Vue({
  data() {
    return {
      name: '小可愛',
      age: 3
    }
  },
  template: `<div>我是:{{name}},{{age}}歲</div>`
})

const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const path = require('path')

const htmlStr = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf8') // 同步讀取文件

// 創(chuàng)建一個(gè)渲染器
const render = VueServerRenderer.createRenderer({
  // 可以從本地讀取html文件當(dāng)作模板
  template: htmlStr, // 采用哪個(gè)模版去渲染,在html中加入這個(gè)<!--vue-ssr-outlet-->標(biāo)簽表示渲染到這個(gè)位置上
}) // 創(chuàng)建一個(gè)渲染器

let app = new Koa() // app實(shí)例
let router = new Router() // 路由實(shí)例

router.get('/', async (ctx) => {
  // ctx.body = 'hello world'
  ctx.body = await render.renderToString(vm) // render.renderToString 返回的是一個(gè)promise
  // // <div data-server-rendered="true">我是:hcj,20歲</div>
})

app.use(router.routes())
app.listen(3500)

監(jiān)聽文件改動(dòng),自動(dòng)重啟服務(wù)

npm install nodemon -g

目前實(shí)現(xiàn)了一個(gè)最簡單的 ssr,后續(xù)文章加入客戶端配置,服務(wù)端配置,vue-router, vuex 來實(shí)現(xiàn)完整的 ssr。

github:https://github.com/mxcz213/vue-ssr-demo/tree/part-one

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容