nuxt 是基於 vue 開發的一個應用框架,最常用的就是拿來做 ssr。那麼什麼是 ssr 呢,這就要提及一個相對的概念:csr。
CSR & SSR
web網頁開發從之前的 jsp,php 轉向現在的三大框架 angular react vue,其實就是從 csr 到 ssr 的轉變,即從服務端渲染轉而變為客戶端渲染。
有使用經驗的大概知道,例如之前的 php 開發,是將網頁內容和服務端程式碼邏輯寫在一起的,在客戶端請求時,服務端經過 php 引擎的渲染生成完整的 html 頁面再返回給客戶端,這種渲染出的頁面在瀏覽器中右鍵檢視原始碼都是能看見 頁面全部的 html 程式碼的。而客戶端渲染如 vue,返回的就只有一個掛在 app 節點的 html檔案和一個 js 檔案,頁面的內容都是在客戶端的 js 渲染生成的。所以渲染 html 文字所在的位置就是 CSR(客戶端渲染) 和 SSR(服務端渲染) 最本質的區分。
既然web開發從 ssr 過渡到了 csr,那我們為什麼又再去做 ssr 呢,這就要涉及到雙方的優缺了:
SSR:
優點:
便於 SEO,渲染完整的 html 更利於搜尋引擎的抓取。
頁面載入的白屏時間短(幾乎沒有)。
缺點:
每載入一個頁面都要對伺服器發起一次請求,伺服器的渲染負荷重,請求緩慢。
每次載入都會重新整理頁面,即使只是頁面中的小區域需要改變。
CSR:
優點:
- 頁面具有優秀的效能,更利於頁面互動。
缺點:
不利於SEO
首頁初始化載入白屏時間長。
在我們普遍使用三大框架的今天,如果我們的頁面需要對 SEO 的良好支援,這就需要我們做 ssr 了。
NUXT 專案的初始化
使用以下命令呼叫腳手架生成專案,腳手架選項按需選擇就可以了:
npx create-nuxt-app <專案名>
複製程式碼
生成的目錄結構如下:
.nuxt
執行快取目錄
assets
資源目錄,用於存放如 css 檔案,js 檔案,圖片
components
元件目錄,用於存放 vue 元件
layouts
佈局目錄,用於設定佈局,檔名即為佈局名
在 pages 目錄裡的元件可以通過 layout 屬性指定佈局元件,不指定預設為 default。
佈局元件中使用 <nuxt />
標籤指定應用佈局時,page 元件所在的位置。
middleware
中介軟體目錄,用於設定中介軟體函式,檔名即為中介軟體名
在 pages 目錄裡的元件可以通過 middleware 屬性指定中介軟體函式,中介軟體會在元件渲染前執行
pages
頁面目錄,用於設定路由頁面,目錄下的 vue 檔案會自動生成相應的路由配置
如以下目錄結構對應的配置:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
複製程式碼
nuxt 中使用 <nuxt-link>
代替了<router-link>
nuxt 會為 page 元件提供額外的配置項,常用到的如下:
layout 指定當前頁面使用的佈局(layouts 根目錄下的佈局檔案)。
validate 校驗方法用於校驗動態路由的引數。
middleware 指定頁面的中介軟體函式,中介軟體函式會在頁面渲染之前被呼叫,也可以指定為middleware資料夾內的中介軟體名。
asyncData 支援非同步資料處理,拿來為 page 元件請求非同步資料,返回物件中的 data 會覆蓋到元件的 data 中。
fetch 用於在渲染頁面之前獲取資料填充應用的狀態樹(store)。不同的是 fetch 方法不會設定元件的資料。
plugins
外掛目錄,用於引入第三方模組,
如 element-ui.js:
import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/en'
Vue.use(Element, { locale })
複製程式碼
然後可以在 nuxt.config.js 中的 plugins 中引入:
plugins: [
'@/plugins/element-ui'
],
複製程式碼
server
服務端目錄
static
靜態檔案目錄
store
vuex目錄,index.js 為主檔案,其他檔案預設會配置成 module 模組,預設啟用 namespace。
檔案形式為:
export const state = () => ({
// state
})
export const actions = {
// actions
}
export const mutations = {
// mutations
}
複製程式碼
在 index.js 的 actions 中可以配置 nuxtServerInit,可以用來將資料從服務端傳到客戶端。
具體使用
建立專案如下:
執行初始專案,效果如下:
使用佈局
在 layouts 下新建檔案 myLayout.vue
<template>
<div>
<header>
<h2>I am header</h2>
</header>
<main>
<nuxt />
</main>
<footer>
<h2>I am footer</h2>
</footer>
</div>
</template>
複製程式碼
然後我們在 pages 下新建檔案 layoutDemo.vue
<template>
<div>
<h1>I am layout demo</h1>
</div>
</template>
<script>
export default {
layout: 'myLayout'
}
</script>
複製程式碼
訪問 localhost:3000/layoutDemo,佈局效果如下:
使用中介軟體
例如我們實現一個未登入攔截跳轉的中介軟體:
在 middleware 中新建 auth.js
export default function (ctx) {
// eslint-disable-next-line no-constant-condition
if (true) { // ctx 判斷得到未登入
ctx.redirect({ path: '/' })
}
}
複製程式碼
再在 layoutDemo.vue 中加上 middleware: 'auth'
,這樣訪問該頁面就會攔截跳轉回主頁面。
用 asyncData 實現 ssr
我們再在 pages 中新建頁面 ssr1.vue
<template>
<div>
<ul>
<li v-for="item of list1" :key="item">
{{ item }}
</li>
</ul>
<ul style="margin-top: 50px">
<li v-for="item of list2" :key="item">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
layout: 'myLayout',
data () {
return {
list1: []
}
},
async created () {
const { status, data: { list } } = await this.$axios.get('/list')
if (status === 200) {
this.list = list
}
},
async asyncDate () {
const { status, data: { list } } = await this.$axios.get('/list')
if (status === 200) {
return {
list2: list
}
}
}
}
</script>
複製程式碼
這裡我們 list1 是用傳統的 vue 方式獲取非同步資料, list2 用 asyncData 獲取非同步資料
然後我們在 server 中寫介面,應為 node 還是採用 require 的方式,如果我們想使用 import 的方式,可以安裝 babel-cli 和 babel-preset-env , 新建配置檔案 .babelrc:
{
"presets": ["env"]
}
複製程式碼
然後在 package.json 裡將 dev 命令加上字尾 --exec babel-node
, server 中書寫 node 就可以使用 import 了。
然後安裝 koa-router,在 index.js 里加入以下程式碼:
import Router from 'koa-router'
// 以下程式碼加在 start 函式內的 原有的 app.use 之前
const router = new Router()
router.get('/list', (ctx) => {
ctx.body = {
list: ['a', 'b', 'c']
}
})
app.use(router.routes()).use(router.allowedMethods())
複製程式碼
然後訪問頁面,兩個列表都正常渲染
我們右鍵檢視原始碼:
list2 通過 asyncData 的方式實現了 ssr。
使用 nuxtServerInit 實現 ssr
我們新建頁面 ssr2.vue
<template>
<div>
<ul>
<li v-for="item of $store.state.list" :key="item">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
layout: 'myLayout'
}
</script>
複製程式碼
然後在 store 中新建 index.js
export const state = () => ({
list: []
})
export const mutations = {
setList (state, value) {
state.list = value
}
}
export const actions = {
async nuxtServerInit ({ commit }, { app }) {
const { status, data: { list } } = await this.$axios.get('/list')
if (status === 200) {
commit('setList', list)
}
}
}
複製程式碼
重新 npm run dev, 訪問 localhost:3000/ssr2
檢視原始碼,也正確渲染