vuecli項目構建SSR服務端渲染的實現(xiàn)
服務端渲染(SSR)
將一個 Vue 組件在服務端渲染成 HTML 字符串并發(fā)送到瀏覽器,最后將這些靜態(tài)標記“激活”為可交互應用程序的過程就叫服務端渲染(SSR)
服務器渲染的 Vue.js 應用程序也可以被認為是"同構"或"通用",因為應用程序的大部分代碼都可以在服務器和客戶端上運行
為什么使用 服務端渲染(SSR)
- 更好的 SEO:傳統(tǒng)的 spa 頁面數(shù)據(jù)都是異步加載,搜索引擎爬蟲無法抓取,服務端渲染(SSR)使搜索引擎爬蟲抓取工具可以直接查看完全渲染的頁面,解決 vue 項目的 seo 問題
- 更快的內容到達時間 (首屏加載更快):請求頁面時,服務端將渲染好的頁面直接發(fā)送給瀏覽器進行渲染,瀏覽器只需要解析渲染 HTML,無需等待所有的 JavaScript 都完成下載并執(zhí)行,才顯示服務器渲染的標記
服務端渲染(SSR)缺點
- 開發(fā)條件所限:瀏覽器特定的代碼,只能在某些生命周期鉤子函數(shù)中使用;一些外部擴展庫可能需要特殊處理,才能在服務器渲染應用程序中運行
- 涉及構建設置和部署的更多要求:與可以部署在任何靜態(tài)文件服務器上的完全靜態(tài)單頁面應用程序 (SPA) 不同,服務器渲染應用程序,需要處于 Node.js server 運行環(huán)境
- 更多的服務器端負載:在 Node.js 中渲染完整的應用程序,顯然會比僅僅提供靜態(tài)文件的 server 更加大量占用 CPU 資源,因此如果你預料在高流量環(huán)境下使用,需要準備相應的服務器負載,并采用緩存策略
服務端渲染(SSR)vs 預渲染(Prerendering)
如果你只是想改善少數(shù)營銷頁面(例如 /, /about, /contact 等)的 SEO,那么你可能需要預渲染,無需使用 web 服務器實時動態(tài)編譯 HTML,而是使用預渲染方式,在構建時簡單地生成針對特定路由的靜態(tài) HTML 文件,優(yōu)點是設置預渲染更簡單,并可以將你的前端作為一個完全靜態(tài)的站點
如果你使用 webpack,你可以使用 prerender-spa-plugin (npm地址) 插件輕松地添加預渲染
服務端渲染(SSR)原理
構建流程:所有的文件擁有一個公共入口 app.js,進入服務端入口 entry-server.js 和客戶端入口 entry-client.js ,項目完成后通過使用 webpack 打包生成服務端 server bundle(一個供服務端 SSR 使用的 json 文件)和客戶端 client bundle(用于瀏覽器),當請求頁面時,服務端將 vue 組件組裝成 HTML 字符串發(fā)送到瀏覽器,混入到客戶端訪問的 HTML 模板中,完成頁面渲染

通過 vuecli 創(chuàng)建 vue 項目
vue create vue-ssr-demo
vue-server-renderer
vue-server-renderer 是 SSR 渲染的核心,提供 createRenderer 方法,這個方法的 renderToString 可以把 app 渲染成字符串。createBundleRenderer 方法可以通過預打包應用程序代碼創(chuàng)建 bundleRenderer 實例,來渲染 bundle 和 HTML 模板
安裝 vue-server-renderer
npm install vue-server-renderer --save
注意:
- vue-server-renderer 和 vue 必須匹配版本
- vue-server-renderer 依賴一些 Node.js 原生模塊,因此只能在 Node.js 中使用
避免狀態(tài)單例
Node.js 服務器是一個長期運行的進程,當我們的代碼進入該進程時,它將進行一次取值并留存在內存中,這意味著如果創(chuàng)建一個單例對象,它將在每個傳入的請求之間共享,所以我們應該暴露一個可以重復執(zhí)行的工廠函數(shù),為每個請求創(chuàng)建一個新的根 Vue 實例,如果我們在多個請求之間使用一個共享的實例,很容易導致交叉請求狀態(tài)污染(同樣的規(guī)則也適用于 router、store 和 event bus 實例)
創(chuàng)建 路由 router
安裝 vue-router
npm install vue-router --save
在 src 目錄下創(chuàng)建 router 文件夾和 index.js
在 components 目錄下創(chuàng)建 Home.vue 和 About.vue 頁面(根據(jù)項目需求自定義創(chuàng)建)
router/index.js:
import Vue from "vue"
import Router from "vue-router"
import Home from "@/components/Home"
import About from "@/components/About"
Vue.use(Router)
//每次用戶請求都需要創(chuàng)建一個新的router實例
//創(chuàng)建createRouter工廠函數(shù)
export default function createRouter() {
//創(chuàng)建router實例
return new Router({
mode: "history",
routes: [
{
path: "/",
name: 'home',
component: Home
},
{
path: "/about",
name: 'about',
component: About
}
]
})
}
修改 App.vue
修改 App.vue 頁面,進行頁面布局(根據(jù)項目需求自定義布局)
App.vue:
<template> <div id="app"> <nav> <router-link to="/">首頁</router-link> <router-link to="/about">關于</router-link> </nav> <router-view></router-view> </div> </template>
創(chuàng)建 公共入口 app.js
在 src 目錄下創(chuàng)建 公共入口 app.js ,用于創(chuàng)建 vue 實例
app.js:
import Vue from "vue"
import App from "./App.vue"
import createRouter from "./router"
//創(chuàng)建createApp工廠函數(shù)
export default function createApp() {
const router = createRouter()
//創(chuàng)建vue實例
const app = new Vue({
router,
render: h => h(App),
})
return { app, router }
}
創(chuàng)建 服務端入口 entry-server.js
在 src 目錄下創(chuàng)建 服務端入口 entry-server.js ,用于渲染首屏
entry-server.js:
import createApp from "./app"
export default context => {
return new Promise((resolve, reject) => {
const { app, router } = createApp()
//渲染首屏
router.push(context.url)
router.onReady(() => {
resolve(app)
}, reject)
})
}
創(chuàng)建 客戶端入口 entry-client.js
在 src 目錄下創(chuàng)建 客戶端入口 entry-client.js ,用于掛載激活 app
entry-client.js:
import createApp from "./app"
const { app, router } = createApp()
router.onReady(() => {
//掛載激活app
app.$mount("#app")
})
創(chuàng)建 頁面模板 index.temp.html
在 public 目錄下創(chuàng)建 index.temp.html ,作為渲染 Vue 應用程序時,renderer 生成 HTML 頁面包裹容器,來包裹生成的 HTML 標記
<!--vue-ssr-outlet--> 注釋將是應用程序 HTML 標記注入的地方
index.temp.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</title> </head> <body> <!--vue-ssr-outlet--> </body> </html>
創(chuàng)建 Node.js 服務器
服務端渲染(SSR)需要使用 Node.js 服務器,這里使用 express 框架搭建
安裝 express
npm install express --save
根目錄下創(chuàng)建 server.js 文件,用于搭建 Node.js 服務器
server.js:
//nodejs服務器
const express = require("express")
const Vue = require("vue")
const fs = require("fs")
//創(chuàng)建express實例
const app = express()
//創(chuàng)建渲染器
const { createBundleRenderer } = require("vue-server-renderer")
const serverBundle = require("./dist/server/vue-ssr-server-bundle.json")
const clientManifest = require("./dist/client/vue-ssr-client-manifest.json")
const renderer = createBundleRenderer(serverBundle, {
runInNewContext: false,
template: fs.readFileSync("./public/index.temp.html", "utf-8"), //頁面模板
clientManifest
})
//中間件處理靜態(tài)文件請求
app.use(express.static("./dist/client", {index: false}))
//將路由的處理交給vue
app.get("*", async (req, res) => {
try {
const context = {
url: req.url,
title: ""
}
const html = await renderer.renderToString(context)
res.send(html)
}catch {
res.status(500).send("服務器內部錯誤!")
}
})
app.listen(9999, () => {
console.log("服務器渲染成功!")
})
webpack 打包配置
根目錄下創(chuàng)建 vue 配置文件 vue.config.js 進行 webpack 配置,該配置會覆蓋 vue-cli 中 webpack 的默認配置
vue.config.js:
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin")
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin")
//環(huán)境變量,決定入口是客戶端還是服務端
const TARGRT_NODE = process.env.WEBPACK_TARGET === "node"
const target = TARGRT_NODE ? "server" : "client"
module.exports = {
css: {
extract: false
},
outputDir: "./dist/" + target,
configureWebpack: () => ({
//將 entry 指向應用程序的 server entry 文件
entry: `./src/entry-${target}.js`,
//對 bundle renderer 提供 source map 支持
devtool: "source-map",
//這允許 webpack 以 Node 適用方式(Node-appropriate fashion)處理動態(tài)導入(dynamic import)
//并且還會在編譯 Vue 組件時,告知 `vue-loader` 輸送面向服務器代碼(server-oriented code)
target: TARGRT_NODE ? "node" : "web",
node: TARGRT_NODE ? undefined : false,
output: {
//此處告知 server bundle 使用 Node 風格導出模塊(Node-style exports)
libraryTarget: TARGRT_NODE ? "commonjs2" : undefined
},
optimization: { splitChunks: TARGRT_NODE ? false : undefined },
//將服務器的整個輸出構建為單個 JOSN 文件的插件
//服務端默認文件名為 vue-ssr-server-bundle.json
plugins: [TARGRT_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
})
}
打包腳本配置
cross-env 插件:運行跨平臺設置和使用環(huán)境變量的腳本
安裝 cross-env 插件
npm install cross-env --save
在 package.json 文件中定義項目運行打包腳本
package.json:
{
......
"scripts": {
"server": "node server",
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build": "npm run build:server && npm run build:client"
},
......
}
終端執(zhí)行命令打包項目:
npm run build
打包完成后,終端執(zhí)行命令啟動 Node.js 服務器
npm run server
服務器啟動后,瀏覽器打開 localhost:9999 即可訪問 SSR 項目
查看網頁源代碼發(fā)現(xiàn)根元素上添加了一個特殊的屬性:data-server-rendered,該屬性讓客戶端 Vue 知道這部分 HTML 是由 Vue 在服務端渲染的,并且應該以激活模式進行掛載
<div id="app" data-server-rendered="true">......</div>

項目目錄:

打包后 dist 目錄:

到此這篇關于vuecli項目構建SSR服務端渲染的實現(xiàn)的文章就介紹到這了,更多相關vuecli構建SSR服務端渲染內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn)
這篇文章主要介紹了基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05
基于Vue和Firebase實現(xiàn)一個實時聊天應用
在本文中,我們將學習如何使用Vue.js和Firebase來構建一個實時聊天應用,Vue.js是一種流行的JavaScript前端框架,而Firebase是Google提供的實時數(shù)據(jù)庫和后端服務,結合這兩者,我們可以快速搭建一個功能強大的實時聊天應用,需要的朋友可以參考下2023-08-08
詳解vue-cli 本地開發(fā)mock數(shù)據(jù)使用方法
這篇文章主要介紹了詳解vue-cli 本地開發(fā)mock數(shù)據(jù)使用方法,如果后端接口尚未開發(fā)完成,前端開發(fā)一般使用mock數(shù)據(jù)。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
vue給input file綁定函數(shù)獲取當前上傳的對象完美實現(xiàn)方法
這篇文章主要介紹了vue給input file綁定函數(shù)獲取當前上傳的對象完美實現(xiàn)方法,需要的朋友可以參考下2017-12-12

