深入學習Vue SSR服務端渲染 用Nuxt.js打造CNode社群

Brolly發表於2019-01-22

“學而不思則惘,思而不學則殆”

服務端渲染(SSR)

SSR意為 server-side rendering(服務端渲染),目的是為了解決單頁面應用的SEO的問題。

伺服器端渲染(SSR)和客戶端渲染(CSR)

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')
    })
複製程式碼

參考原始碼

github.com/Lwenli1224/…

編寫通用程式碼

"通用"程式碼 - 即執行在伺服器和客戶端的程式碼。由於用例和平臺 API 的差異,當執行在不同環境中時,我們的程式碼將不會完全相同。所以這裡我們將會闡述你需要理解的關鍵事項。

SSR開發需要注意的問題

  • 服務端渲染只會執行 vue 的兩個鉤子函式 beforeCreate 和 created

  • 服務端渲染無法訪問 window 和 document等只有瀏覽器才有的全域性物件。

通用API 例如,axios 是一個 HTTP 客戶端,可以向伺服器和客戶端都暴露相同的 API。

webpack工程構建

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環境比較費勁

github.com/Lwenli1224/…

從零搭建vue環境 可以參考我這篇文章(從零搭建Vue開發環境:webpack4 + vue-loader + babel-loader v8 + Babel v7 + eslint + git hooks + editorconfig)

juejin.im/post/5c4898…

服務端渲染應用框架-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社群)

CNode

路由建立

Nuxt.js 依據 pages 目錄結構自動生成 vue-router 模組的路由配置。

router

Vuex配置

Nuxt.js 會嘗試找到應用根目錄下的 store 目錄,如果該目錄存在,它將做以下的事情:

  • 引用vuex模組
  • 將vuex模組加到vendors構建配置中去
  • 設定Vue根例項的store配置項

Vuex

middleware中介軟體配置

每一箇中介軟體應放置在 middleware/ 目錄。檔名的名稱將成為中介軟體名稱(middleware/auth.js將成為 auth 中介軟體)。

middleware

node服務配置

server

nuxt.config.js

Nuxt.js 預設的配置涵蓋了大部分使用情形,可通過 nuxt.config.js 來覆蓋預設的配置。

zh.nuxtjs.org/api/configu… github.com/Lwenli1224/…

Nuxt.js CNode社群專案原始碼

更多詳情請看我倉庫原始碼 github.com/Lwenli1224/…

未完待續 持續更新。。。

論文件資料的重要性

越來越覺得平時學習工作 看文件很重要,特別是官方文件 一手的資料,英語好的話最好是看英文文件一手資料比較多。

珠峰公眾號

相關文章