什麼是SSR?以下這篇文章跟大家介紹一下在vue中實作SSR服務端渲染的方法,希望對大家有幫助!
一、SSR是什麼
#Server-Side Rendering
我們稱之為SSR
,意為服務端渲染
指由服務側完成頁面的 HTML
結構拼接的頁面處理技術,傳送到瀏覽器,然後為其綁定狀態與事件,成為完全可互動頁面的過程。 【相關推薦:vuejs影片教學】
先來看看Web
3個階段的發展史:
- 傳統服務端渲染SSR
- 單頁應用SPA
- 服務端渲染SSR
#傳統web開發
##網頁內容在服務端渲染完成,⼀次性傳輸到瀏覽器dom#結構
單頁應用SPA
單頁應用優秀的使用者體驗,使其逐漸成為主流,頁面內容由JS渲染出來,這種方式稱為客戶端渲染
#app,並沒有內容
服務端渲染SSR
SSR解決方案,後端渲染出完整的首屏的
dom結構返回,前端拿到的內容包含首屏及完整
spa結構,應用激活後依然按照
spa方式運行
##看完前端發展,我們再看看
官方對SSR
的解釋:
Vue SSR伺服器渲染的Vue.js 應用程式也可以被認為是"同構"或"通用",因為應用程式的大部分程式碼都可以在伺服器和客戶端上運行
##我們從上門解釋得到以下結論:
- 是一個在
- SPA
上改良的服務端渲染
透過
Vue SSR 渲染的頁面,需要在客戶端啟動才能實現互動 -
Vue SSR
將包含兩部分:服務端渲染的首屏,包含互動的 - SPA
SSR主要解決了以下兩種問題:
seo:搜尋引擎優先爬取頁面
HTML- 結構,使用
- ssr
時,服務端已經產生了和業務想關聯的
HTML,有利於
seo首屏呈現渲染:使用者不需要等待頁面所有
js 載入完成就可以看到頁面視圖(壓力來到了伺服器,所以需要權衡哪些用服務端渲染,哪些交給客戶端) -
但使用
SSR
- #函式庫的支持性,程式碼相容
- 效能問題
-
#每個請求都是
n- 個實例的創建,不然會污染,消耗會變得很大
-
快取
node serve 、 - ngin
x判斷目前使用者有沒有過期,如果沒過期的話就緩存,用剛剛的結果。
降級:監控
cpu 、記憶體佔用過多,就 - spa
,回傳單一的殼
-
- 所以在我們選擇是否使用 SSR
- SEO
- 的頁面是否只是少數幾個,這些是否可以使用預渲染( Prerender SPA Plugin)實作
首屏的請求回應邏輯是否複雜,資料回傳是否大量且緩慢 - ##三、如何實作
對於同構開發,我們依然使用webpack打包,我們要解決兩個問題:服務端首屏渲染和客戶端啟動
這裡需要產生一個伺服器bundle
檔案用於服務端首屏渲染和一個客戶端bundle
檔案用於客戶端啟動
程式碼結構除了兩個不同入口之外,其他結構和之前vue
應用完全相同
src ├── router ├────── index.js # 路由声明 ├── store ├────── index.js # 全局状态 ├── main.js # ⽤于创建vue实例 ├── entry-client.js # 客户端⼊⼝,⽤于静态内容“激活” └── entry-server.js # 服务端⼊⼝,⽤于⾸屏内容渲染
路由配置
import Vue from "vue"; import Router from "vue-router"; Vue.use(Router); //导出⼯⼚函数 export function createRouter(){ return new Router({ mode: 'history', routes: [ // 客户端没有编译器,这⾥要写成渲染函数 { path: "/", component: { render: h => h('div', 'index page') } }, { path: "/detail", component: { render: h => h('div', 'detail page') }} ] }); }
主檔案main.js
跟之前不同,主檔案是負責建立vue
實例的工廠,每次請求都會有獨立的vue
實例建立
import Vue from "vue"; import App from "./App.vue"; import { createRouter } from "./router"; // 导出Vue实例⼯⼚函数,为每次请求创建独⽴实例 // 上下⽂⽤于给vue实例传递参数 export function createApp(context) { const router = createRouter(); const app = new Vue({ router, context, render: h => h(App) }); return { app, router }; }
編寫服務端入口src/entry-server.js
它的任務是建立Vue
實例並根據傳入url
指定首屏
import { createApp } from "./main"; // 返回⼀个函数,接收请求上下⽂,返回创建的vue实例 export default context => { // 这⾥返回⼀个Promise,确保路由或组件准备就绪 return new Promise((resolve, reject) => { const { app, router } = createApp(context); // 跳转到⾸屏的地址 router.push(context.url); // 路由就绪,返回结果 router.onReady(() => { resolve(app); }, reject); }); };
寫客戶端入口entry-client.js
#客戶端入口只需建立vue
實例並執行掛載,這⼀步稱為啟動
import { createApp } from "./main"; // 创建vue、router实例 const { app, router } = createApp(); // 路由就绪,执⾏挂载 router.onReady(() => { app.$mount("#app"); });
對webpack
進行設定
安裝依賴
npm install webpack-node-externals lodash.merge -D
對vue.config.js
進行配置
// 两个插件分别负责打包客户端和服务端 const VueSSRServerPlugin = require("vue-server-renderer/server-plugin"); const VueSSRClientPlugin = require("vue-server-renderer/client-plugin"); const nodeExternals = require("webpack-node-externals"); const merge = require("lodash.merge"); // 根据传⼊环境变量决定⼊⼝⽂件和相应配置项 const TARGET_NODE = process.env.WEBPACK_TARGET === "node"; const target = TARGET_NODE ? "server" : "client"; module.exports = { css: { extract: false }, outputDir: './dist/'+target, configureWebpack: () => ({ // 将 entry 指向应⽤程序的 server / client ⽂件 entry: `./src/entry-${target}.js`, // 对 bundle renderer 提供 source map ⽀持 devtool: 'source-map', // target设置为node使webpack以Node适⽤的⽅式处理动态导⼊, // 并且还会在编译Vue组件时告知`vue-loader`输出⾯向服务器代码。 target: TARGET_NODE ? "node" : "web", // 是否模拟node全局变量 node: TARGET_NODE ? undefined : false, output: { // 此处使⽤Node⻛格导出模块 libraryTarget: TARGET_NODE ? "commonjs2" : undefined }, // https://webpack.js.org/configuration/externals/#function // https://github.com/liady/webpack-node-externals // 外置化应⽤程序依赖模块。可以使服务器构建速度更快,并⽣成较⼩的打包⽂件。 externals: TARGET_NODE ? nodeExternals({ // 不要外置化webpack需要处理的依赖模块。 // 可以在这⾥添加更多的⽂件类型。例如,未处理 *.vue 原始⽂件, // 还应该将修改`global`(例如polyfill)的依赖模块列⼊⽩名单 whitelist: [/\.css$/] }) : undefined, optimization: { splitChunks: undefined }, // 这是将服务器的整个输出构建为单个 JSON ⽂件的插件。 // 服务端默认⽂件名为 `vue-ssr-server-bundle.json` // 客户端默认⽂件名为 `vue-ssr-client-manifest.json`。 plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()] }), chainWebpack: config => { // cli4项⽬添加 if (TARGET_NODE) { config.optimization.delete('splitChunks') } config.module .rule("vue") .use("vue-loader") .tap(options => { merge(options, { optimizeSSR: false }); }); } };
對腳本進行配置,安裝依賴
npm i cross-env - D
"scripts": { "build:client": "vue-cli-service build", "build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build", "build": "npm run build:server && npm run build:client" }
執行打包:npm run build
#最後修改宿主檔案/public/index.html
##
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Document</title> </head> <body> <!--vue-ssr-outlet--> </body> </html>
是服務端渲染入口位置,注意不能為了好看而在前後加空格安裝
vuex
npm install -S vuex
vuex工廠函數
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export function createStore () { return new Vuex.Store({ state: { count:108 }, mutations: { add(state){ state.count += 1; } } }) }#在
main.js檔案中掛載
store
import { createStore } from './store' export function createApp (context) { // 创建实例 const store = createStore() const app = new Vue({ store, // 挂载 render: h => h(App) }) return { app, router, store } }伺服器端渲染的是應用程式的"快照",如果應用依賴於⼀些異步數據,那麼在開始渲染之前,需要先預取和解析好這些數據在
store進行一步資料取得
export function createStore() { return new Vuex.Store({ mutations: { // 加⼀个初始化 init(state, count) { state.count = count; }, }, actions: { // 加⼀个异步请求count的action getCount({ commit }) { return new Promise(resolve => { setTimeout(() => { commit("init", Math.random() * 100); resolve(); }, 1000); }); }, }, }); }元件中的資料預取邏輯
export default { asyncData({ store, route }) { // 约定预取逻辑编写在预取钩⼦asyncData中 // 触发 action 后,返回 Promise 以便确定请求结果 return store.dispatch("getCount"); } };服務端資料預取,
entry-server.js
import { createApp } from "./app"; export default context => { return new Promise((resolve, reject) => { // 拿出store和router实例 const { app, router, store } = createApp(context); router.push(context.url); router.onReady(() => { // 获取匹配的路由组件数组 const matchedComponents = router.getMatchedComponents(); // 若⽆匹配则抛出异常 if (!matchedComponents.length) { return reject({ code: 404 }); } // 对所有匹配的路由组件调⽤可能存在的`asyncData()` Promise.all( matchedComponents.map(Component => { if (Component.asyncData) { return Component.asyncData({ store, route: router.currentRoute, }); } }), ) .then(() => { // 所有预取钩⼦ resolve 后, // store 已经填充⼊渲染应⽤所需状态 // 将状态附加到上下⽂,且 `template` 选项⽤于 renderer 时, // 状态将⾃动序列化为 `window.__INITIAL_STATE__`,并注⼊ HTML context.state = store.state; resolve(app); }) .catch(reject); }, reject); }); };客戶端在掛載到應用程式之前,
store 就應該取得到狀態,
entry-client.js
// 导出store const { app, router, store } = createApp(); // 当使⽤ template 时,context.state 将作为 window.__INITIAL_STATE__ 状态⾃动嵌⼊到最终的 HTML // 在客户端挂载到应⽤程序之前,store 就应该获取到状态: if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__); }客戶端資料預先取處理,
main.js
Vue.mixin({ beforeMount() { const { asyncData } = this.$options; if (asyncData) { // 将获取数据操作分配给 promise // 以便在组件中,我们可以在数据准备就绪后 // 通过运⾏ `this.dataPromise.then(...)` 来执⾏其他任务 this.dataPromise = asyncData({ store: this.$store, route: this.$route, }); } }, });修改伺服器啟動檔案
// 获取⽂件路径 const resolve = dir => require('path').resolve(__dirname, dir) // 第 1 步:开放dist/client⽬录,关闭默认下载index⻚的选项,不然到不了后⾯路由 app.use(express.static(resolve('../dist/client'), {index: false})) // 第 2 步:获得⼀个createBundleRenderer const { createBundleRenderer } = require("vue-server-renderer"); // 第 3 步:服务端打包⽂件地址 const bundle = resolve("../dist/server/vue-ssr-server-bundle.json"); // 第 4 步:创建渲染器 const renderer = createBundleRenderer(bundle, { runInNewContext: false, // https://ssr.vuejs.org/zh/api/#runinnewcontext template: require('fs').readFileSync(resolve("../public/index.html"), "utf8"), // 宿主⽂件 clientManifest: require(resolve("../dist/client/vue-ssr-clientmanifest.json")) // 客户端清单 }); app.get('*', async (req,res)=>{ // 设置url和title两个重要参数 const context = { title:'ssr test', url:req.url } const html = await renderer.renderToString(context); res.send(html) })
小結
- ##使用
- ssr
不存在單例模式,每次使用者要求都會建立一個新的
vue
實例 實作 - ssr
需要實作服務端首屏渲染與用戶端啟動
服務端異步擷取資料 - asyncData
可以分為首屏非同步取得和切換元件取得
#首屏異步獲取數據,在服務端預渲染的時候就應該已經完成- 切換組件通過 mixin
- 混入,在
beforeMount
鉤子完成數據獲取
(學習影片分享:
、web前端)
以上是什麼是SSR? vue中怎麼實作SSR服務端渲染?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本篇文章带大家聊聊vue指令中的修饰符,对比一下vue中的指令修饰符和dom事件中的event对象,介绍一下常用的事件修饰符,希望对大家有所帮助!

如何覆盖组件库样式?下面本篇文章给大家介绍一下React和Vue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!

Vue3中的SSR技术应用实践,提升应用的SEO效果随着前端开发的快速发展,SPA(单页面应用)已经成为了主流。SPA的好处不言而喻,可以提供流畅的用户体验,但却在SEO(搜索引擎优化)方面有一些挑战。由于SPA在前端渲染阶段只返回一个HTML模板,大部分内容是通过JavaScript动态加载的,导致搜索引擎在抓取、索引和排名方面的困难。为了解决这个问题,

react与vue的虚拟dom没有区别;react和vue的虚拟dom都是用js对象来模拟真实DOM,用虚拟DOM的diff来最小化更新真实DOM,可以减小不必要的性能损耗,按颗粒度分为不同的类型比较同层级dom节点,进行增、删、移的操作。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

禪工作室 13.0.1
強大的PHP整合開發環境

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),