“學而不思則惘,思而不學則殆”
服務端渲染(SSR)
SSR意為 server-side rendering(服務端渲染),目的是為了解決單頁面應用的SEO的問題。
伺服器端渲染(SSR)和客戶端渲染(CSR)
伺服器端渲染(SSR)
瀏覽器先請求HTML文件,伺服器端先將html頁面(或頁面元件),生成為html字串,再返回給瀏覽器,最後直接渲染到頁面上。
客戶端渲染(CSR)
瀏覽器先請求HTML文件,在瀏覽器端載入html頁面中的JS指令碼。通過JS(vue/react)的能力,將虛擬DOM最終渲染填充到頁面中。
兩者本質的區別是什麼?
SSR
服務端生成好的html頁面內容,直接返回給瀏覽器渲染。
server
const express = require('express')
const app = express()
app.get('/ssr', (req, res) => {
res.send(`
<html>
<head>
<meta charset='utf-8'>
<title>SSR 服務端渲染</title>
</head>
<body>
<h3>SSR 服務端渲染</h3>
<p>SSR意為 Server Side Rendering(服務端渲染),目的是為了解決單頁面應用的SEO的問題。</p>
</body>
</html>
`)
})
app.listen(7200)
複製程式碼
客戶端訪問頁面localhost:7200/ssr
<html>
<head>
<meta charset='utf-8'>
<title>SSR 服務端渲染</title>
</head>
<body>
<h3>SSR 服務端渲染</h3>
<p>SSR意為 Server Side Rendering(服務端渲染),目的是為了解決單頁面應用的SEO的問題。</p>
</body>
</html>
複製程式碼
CSR
頁面首先直接輸出一個空的div#root,再由客戶端載入編譯打包好的react程式碼(bundle.js chunk.js等js指令碼),最終將頁面元件渲染到頁面中。
npx create-react-app my-app
cd my-app
yarn start
複製程式碼
瀏覽器訪問http://localhost:3000/
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script></body>
複製程式碼
客戶端渲染和伺服器端渲染的最重要的區別就是究竟是誰來完成html檔案的完整拼接,如果是在伺服器端完成的,然後返回給客戶端,就是伺服器端渲染,而如果是前端做了更多的工作完成了html的拼接,則就是客戶端渲染。
前後端拆分核心理念
前後端拆分,後端專注於資料介面服務,前端專注介面呼叫,頁面渲染,雙劍合璧,相得益彰。
伺服器端渲染的優缺點是怎樣的?
優點:
- 更好的SEO 首屏載入快 由於搜尋引擎爬蟲抓取工具可以直接檢視完全渲染的頁面。
- 首屏載入快 快速地看到完整渲染的頁面,從而提高使用者體驗。
- 後端生成靜態化檔案 即解析模板的工作完全交由後端來做,客戶端只要解析標準的html頁面即可。
缺點:
- 開發條件受限 在服務端渲染中,created和beforeCreate之外的生命週期鉤子不可用
- 佔用服務端資源
- 學習成本相對較高 除了對webpack、Vue要熟悉,還需要掌握node、Express相關技術。相對於客戶端渲染,專案構建、部署過程更加複雜。
客戶端渲染的優缺點是怎樣的?
優點:
- 前後端分離 前端專注於前端UI,後端專注於api開發,且前端有更多的選擇性,而不需要遵循後端特定的模板。
- 體驗更好
缺點:
- 首屏載入緩慢
- 不利於SEO 除了 Google 和 Bing 比較完美地實現了對於 SPA(Single-Page Application)的爬蟲渲染及內容抓取,大多數搜尋引擎包括百度都沒有支援。因而,包含豐富內容的產品並需要 SEO 流量的產品也就自然需要 SSR 實現。
是否應該使用服務端渲染
- 首屏載入慢 針對於首屏載入,可以做服務端渲染。但要有覺悟,一旦這樣做,後期維護是個很痛苦的事情。相比於做服務端渲染,更推薦通過應用拆分、code spliting 來完成優化首屏載入的過程(先前做過一次首屏優化,優化前首屏載入每次都在 5s+,code spliting 之後直接變成 2s+,價效比高)。
- SEO優化 如果是為了主頁網站被搜尋引擎收錄,可以使用服務端渲染。但更好的建議新開引導專案,在該專案上靜態資源或服務端渲染顯示頁面,作為主要網站的搜尋引擎引流作用。
Vue服務端渲染
這裡我們先從Vue的vue-server-renderer來聊聊服務端渲染,暫先不說那些ssr框架(Nuxt.js Next.js) vue-server-renderer 是官方提供給我們用來實現服務端渲染的npm包
基本用法
安裝
npm install vue vue-server-renderer --save
複製程式碼
渲染一個Vue例項
1.建立一個 Vue 例項
const Vue = require('vue')
const app = new Vue({
template: `<div>hello zhufeng</div>`
})
複製程式碼
2.建立一個 renderer物件
const renderer = require('vue-server-renderer').createRenderer()
複製程式碼
3.將 Vue 例項渲染為 HTML
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
// <div data-server-rendered="true">hello zhufeng</div>
})
// 在 2.5.0+,如果沒有傳入回撥函式,則會返回 Promise:
renderer.renderToString(app).then(html => {
console.log(html)
}).catch(err => {
console.error(err)
})
複製程式碼
Vue例項渲染 完整示例程式碼
Node.js 伺服器作為中間層
npm i express --save
複製程式碼
const Vue = require('vue')
const server = require('express')()
// 建立一個 renderer物件
const renderer = require('vue-server-renderer').createRenderer()
// 建立一個後端路由
server.get('/', (req, res) => {
// 建立一個 Vue 例項
const app = new Vue({
data: {
title: 'hello zhufeng'
},
template: `<h3>{{ title }}</h3>`
})
// 通過renderToString方法 將Vue例項轉換成HTML
renderer
.renderToString(app)
.then(html => {
console.log(html)
// '<h3 data-server-rendered="true">hello zhufeng</h3>'
// 最終將拼接好的html頁面內容 返回給瀏覽器
res.send(`
<!DOCTYPE html>
<html lang="en">
<head><title>Hello</title></head>
<body>${html}</body>
</html>
`)
})
.catch(err => {
res.status(500).end('Internal Server Error')
})
})
server.listen(7300)
複製程式碼
使用html頁面模板
建立一個html模板頁面,用一個額外的HTML頁面包裹容器,來包裹生成的HTML標記(markup)。
html模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>服務端渲染SSR</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
複製程式碼
注意 <!--vue-ssr-outlet--> 註釋 -- 這裡將是應用程式 HTML 標記注入的地方。
server端
const Vue = require('vue')
const fs = require('fs')
const server = require('express')()
const { createRenderer } = require('vue-server-renderer')
// 建立一個 renderer物件 並指定渲染模板
const renderer = createRenderer({
template: fs.readFileSync('./template/index1.html', 'utf-8')
})
// 建立一個後端路由
server.get('/', (req, res) => {
// 建立一個 Vue 例項
const app = new Vue({
data: {
title: 'hello zhufeng'
},
template: `<h3>{{ title }}</h3>`
})
// 通過renderToString方法 將Vue例項轉換成HTML
renderer
.renderToString(app)
.then(html => {
// 最終將拼接好的html頁面內容 返回給瀏覽器
res.send(html)
})
.catch(err => {
res.status(500).end('Internal Server Error')
})
})
server.listen(7300)
複製程式碼
動態注入title 和 meta標籤
html模板設定插值變數
<!DOCTYPE html>
<html lang="en">
<head>
{{{meta}}}
<title>{{title}}</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
複製程式碼
我們可以通過傳入一個"渲染上下文物件",作為 renderToString 函式的第二個引數,來提供插值資料:
// 動態注入title 和 meta標籤
const context = {
title: '珠峰前端培訓',
meta: `
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="keywords" content="HTML, CSS, Vue, React, Node, JavaScript" />
`
}
// 通過renderToString方法 將Vue例項轉換成HTML
renderer
.renderToString(app, context)
.then(html => {
// 最終將拼接好的html頁面內容 返回給瀏覽器
res.send(html)
})
.catch(err => {
res.status(500).end('Internal Server Error')
})
複製程式碼
參考原始碼
編寫通用程式碼
"通用"程式碼 - 即執行在伺服器和客戶端的程式碼。由於用例和平臺 API 的差異,當執行在不同環境中時,我們的程式碼將不會完全相同。所以這裡我們將會闡述你需要理解的關鍵事項。
SSR開發需要注意的問題
-
服務端渲染只會執行 vue 的兩個鉤子函式 beforeCreate 和 created
-
服務端渲染無法訪問 window 和 document等只有瀏覽器才有的全域性物件。
通用API 例如,axios 是一個 HTTP 客戶端,可以向伺服器和客戶端都暴露相同的 API。
webpack工程構建
服務端和客戶端各自都需要提供Vue應用程式。為了做到這一點,我們需要使用 webpack 來打包我們的 Vue 應用程式。事實上,我們可能需要在伺服器上使用 webpack 打包 Vue 應用程式,因為:
-
通常 Vue 應用程式是由 webpack 和 vue-loader 構建,並且許多 webpack 特定功能不能直接在 Node.js 中執行(例如通過 file-loader 匯入檔案,通過 css-loader 匯入 CSS)。
-
儘管 Node.js 最新版本能夠完全支援 ES2015 特性,我們還是需要轉譯客戶端程式碼以適應老版瀏覽器。這也會涉及到構建步驟
然後我們的服務端程式碼和客戶端程式碼通過webpack分別打包,生成Server Bundle和Client Bundle
-
伺服器需要「伺服器 bundle」然後用於伺服器端渲染(SSR)
-
客戶端 bundle」會傳送給瀏覽器,用於混合靜態標記。
從零搭建Vue開發環境(SSR)
利用vue-server-renderer 搭建Vue SSR開發環境
Git倉庫原始碼
從零大家vue ssr環境比較費勁
從零搭建vue環境 可以參考我這篇文章(從零搭建Vue開發環境:webpack4 + vue-loader + babel-loader v8 + Babel v7 + eslint + git hooks + editorconfig)
服務端渲染應用框架-Nuxt.js
Nuxt.js 是一個基於 Vue.js 的服務端渲染應用框架。 你可以基於它初始化新專案的基礎結構程式碼,或者在已有 Node.js 專案中使用 Nuxt.js。 Nuxt.js 預設了利用Vue.js開發服務端渲染的應用所需要的各種配置。
create-nuxt-app
Nuxt.js團隊建立的腳手架工具
建立一個nuxt工程
npx create-nuxt-app nuxt-app
複製程式碼
它會讓你進行一些整合選擇, 如伺服器端框架(express koa)和 UI框架。
啟動專案
npm run dev
複製程式碼
現在我們的應用執行在 http://localhost:3000 上執行。
注意:Nuxt.js 會監聽 pages 目錄中的檔案更改,因此在新增新頁面時無需重新啟動應用程式。
目錄結構
├── README.md # 說明文件
├── assets # 資源目錄 用於組織未編譯的靜態資源如 LESS、SASS 或 JavaScript
├── components # 元件目錄 用於組織應用的 Vue.js 元件
├── layouts # 佈局目錄 用於組織應用的佈局元件
├── middleware # 中介軟體目錄 用於存放應用的中介軟體。
├── nuxt.config.js # nuxt配置檔案 用於組織Nuxt.js 應用的個性化配置,以便覆蓋預設配置。
├── pages # 頁面目錄 用於組織應用的路由及檢視
├── plugins # 外掛目錄 用於組織那些需要 在根vue.js應用例項化之前需要執行的Javascript外掛
├── server # 服務端 用於組織node中間層服務程式碼
├── static # 靜態檔案目錄 用於存放應用的靜態檔案
├── store # store目錄 用於組織應用的 Vuex 狀態樹 檔案。
複製程式碼
目錄結構詳情說明 zh.nuxtjs.org/guide/direc…
非同步資料
asyncData
asyncData方法會在元件(限於頁面元件)每次載入之前被呼叫。它可以在服務端或路由更新之前被呼叫。你可以利用 asyncData方法來獲取資料,Nuxt.js 會將 asyncData 返回的資料融合元件 data 方法返回的資料一併返回給當前元件。
fetch
fetch 方法用於在渲染頁面前填充應用的狀態樹(store)資料, 與 asyncData 方法類似,不同的是它不會設定元件的資料。
Nuxt.js開發實戰(打造CNode社群)
路由建立
Nuxt.js 依據 pages 目錄結構自動生成 vue-router 模組的路由配置。
Vuex配置
Nuxt.js 會嘗試找到應用根目錄下的 store 目錄,如果該目錄存在,它將做以下的事情:
- 引用vuex模組
- 將vuex模組加到vendors構建配置中去
- 設定Vue根例項的store配置項
middleware中介軟體配置
每一箇中介軟體應放置在 middleware/ 目錄。檔名的名稱將成為中介軟體名稱(middleware/auth.js將成為 auth 中介軟體)。
node服務配置
nuxt.config.js
Nuxt.js 預設的配置涵蓋了大部分使用情形,可通過 nuxt.config.js 來覆蓋預設的配置。
zh.nuxtjs.org/api/configu… github.com/Lwenli1224/…
Nuxt.js CNode社群專案原始碼
更多詳情請看我倉庫原始碼 github.com/Lwenli1224/…
未完待續 持續更新。。。
論文件資料的重要性
越來越覺得平時學習工作 看文件很重要,特別是官方文件 一手的資料,英語好的話最好是看英文文件一手資料比較多。
-
看官方文件 如:
Nuxt.js zh.nuxtjs.org/
webpack webpack.js.org/
babel babeljs.io/ -
通過npm搜包瞭解
-
去github看原始碼
-
查問題去stackoverflow (科學上網)
-
逛技術社群 MDN 掘金 CSDN 部落格園等