小仙男·言在前
關(guān)于框架:為了解決VUE的SPA單頁應(yīng)用對SEO搜索引擎優(yōu)化不友好的問題夹抗,這幾天一直在調(diào)研各種SSR框架凤优。比如https://doc.ssr-fc.com/ 和 https://fmfe.github.io/genesis-docs/ 都是比較不錯,且有自己理念和想法的框架癣朗。但是對于公司來說技術(shù)規(guī)范差異太大,團(tuán)隊學(xué)習(xí)成本比較高,思來想去它碎,還是基于NUXT.JS自己搭建一套SSR框架慢慢完善吧。
本文沒有附帶框架,大家直接進(jìn)入【NUXT官網(wǎng)】按官網(wǎng)提示初始化框架即可扳肛!
關(guān)于本文檔:本文檔是從官網(wǎng)文檔中摘錄的一些重點內(nèi)容傻挂,以及加入了自己的一些調(diào)整和對官網(wǎng)內(nèi)容的理解和解釋。
關(guān)于官網(wǎng):NUXT中文網(wǎng) 特別適合新手學(xué)習(xí)挖息,文檔及案例十分清楚詳盡金拒,可以說有手就行。但是套腹,中文網(wǎng)的更新不及時绪抛,有些章節(jié)(比如fetch鉤子中不能使用this)甚至存在明顯錯誤,所以有一定技術(shù)水平的寶子电禀,建議直接查看 NUXT英文官網(wǎng) 幢码。
【一、框架概述】
1尖飞、框架介紹
-
SSR
技術(shù)(即服務(wù)端渲染
技術(shù))症副,區(qū)別于原先純Vue框架的SPA
應(yīng)用(即單頁應(yīng)用
)。SPA
應(yīng)用只有一個index.html的入口文件政基,頁面顯示的所有內(nèi)容均靠客戶端JS進(jìn)行渲染贞铣,對于搜索引擎(SEO
)優(yōu)化來說,整個網(wǎng)站只有一個空頁面沮明,十分不友好辕坝。而服務(wù)端渲染
技術(shù),是借助node.js
作為框架服務(wù)端荐健,在初次訪問一個頁面的時候圣勒,先在服務(wù)端預(yù)請求接口,并在服務(wù)端組裝完成的html頁面后摧扇,返回給客戶端呈現(xiàn)圣贸。 -
Nuxt.js
是基于Vue
框架的一款服務(wù)端渲染
框架,提供了特有的框架結(jié)構(gòu)和服務(wù)端渲染生命周期扛稽。
2吁峻、開發(fā)環(huán)境
- 本框架基于
Node.js+Webpack+vue+Nuxt.js
進(jìn)行開發(fā),提供ElementUI
作為UI框架在张。開發(fā)前需全局安裝Node.js
與webpack
開發(fā)環(huán)境用含。 - 框架推薦
Node.js
版本為v16.15.0
,最低版本不得低于12
帮匾,推薦安裝nvm
或n
等node版本管理工具啄骇。
3、分支要求
- 遵循前端團(tuán)隊git倉庫及版本管理規(guī)范瘟斜,即
master
分支只用于拉取框架代碼缸夹,xxx_dev
為開發(fā)分支痪寻,xxx_test
為開發(fā)分支,xxx
為生產(chǎn)分支虽惭。
3橡类、關(guān)于本文檔
- <font color="#dd0000"><b>本文檔所述內(nèi)容,已經(jīng)是從官網(wǎng)中摘錄的【重中之重】芽唇,開發(fā)前請【詳盡】【仔細(xì)】【通讀】本文檔9嘶!匆笤!尤其是<a href='#five'>【五研侣、數(shù)據(jù)請求】</a> 與 <a href='#six-8'>【六-8、重要Q&A】</a>E谂酢R逶! </b></font>
- 文檔描述存在錯誤的地方寓盗,請以【NUXT英文官網(wǎng)】為準(zhǔn)。
4璧函、框架版本更新
- 詳見框架版本更新說明傀蚌。
【二、啟動與部署】
# 安裝框架以來
$ npm install
# 啟動本地開發(fā)環(huán)境蘸吓,默認(rèn)端口號:3000
$ npm run dev
# 編譯并在生產(chǎn)環(huán)境啟動
$ npm run build
$ npm run start
# 將網(wǎng)站打包成靜態(tài)化頁面
$ npm run generate
【三善炫、框架結(jié)構(gòu)】
- 詳細(xì)目錄結(jié)構(gòu)介紹及使用,參照<a href='#six'>【六库继、其他規(guī)范與Q&A】</a>
-- 框架根目錄
-- .nuxt Nuxt運營和編譯自動生成
-- dist 執(zhí)行Nuxt靜態(tài)化時生成
-- api 全局通用的Api請求函數(shù)(非Nuxt提供)
-- assets 靜態(tài)資源目錄箩艺,存放全局css、image等
-- components 自定義組件目錄宪萄,此目錄下組件無需引入艺谆,按需使用即可
-- layout 布局文件,參考https://www.nuxtjs.cn/guide/views
-- middleware 中間件拜英,類似于路由守衛(wèi)
-- modules 模塊静汤,用于設(shè)置全局監(jiān)聽等,參考https://www.nuxtjs.cn/guide/modules
-- pages 頁面目錄居凶,Nuxt會根據(jù)此目錄自動生成路由虫给,參考https://www.nuxtjs.cn/guide/routing
-- plugins 插件目錄,自定義各種插件侠碧,參考https://www.nuxtjs.cn/guide/plugins
> global.js (全局變量與全局方法)
> plugin.js (全局引入第三方組件)
> request.js (全局請求封裝)
> filter.js (全局過濾器封裝)
> util.js (全局工具函數(shù)封裝)
> all.client.js(僅在客戶端執(zhí)行插件抹估,暫時替代原app.vue)
-- static 不需要webpack編譯的靜態(tài)文件,一般存放ico等文件
-- store Vue狀態(tài)樹弄兜,與原寫法有所不同药蜻,參考https://www.nuxtjs.cn/guide/vuex-store
-- utils 工具類包 (非Nuxt提供)
.editorconfig
.gitignore
env.js 環(huán)境變量配置瓷式,分dev、test谷暮、pro三種環(huán)境
nux.config.js Nuxt的所有配置項蒿往,參考https://www.nuxtjs.cn/api/configuration-build
package-lock.json
package.json
README.md 框架使用文檔
ReleaseNote.md 版本更新說明
【四、生命周期】
-- Nuxt完整生命周期
【服務(wù)端渲染】
-- 全局
nuxtServerInit 第一個:nuxt中第一個運行的生命周期
RouteMiddleware 第二個:中間件湿弦,類似于原框架的路由導(dǎo)航守衛(wèi)
-- 組件
validate 是用來校驗url參數(shù)符不符合
asyncData Nuxt專屬生命周期瓤漏,可用于數(shù)據(jù)請求,只有page可用颊埃,子組件內(nèi)部不可用
beforeCreate Vue生命周期蔬充,但是服務(wù)端會執(zhí)行(不可用于數(shù)據(jù)請求,數(shù)據(jù)請求相關(guān)操作會在客戶端執(zhí)行)
created Vue生命周期班利,但是服務(wù)端會執(zhí)行(同上)
fetch Nuxt專屬生命周期饥漫,可用于數(shù)據(jù)請求, page和子組件都可用
【客戶端渲染】
-- 全局
* `@/plugins/all.client.js` (并非Nuxt生命周期罗标,是只在客戶端運行的插件庸队。此框架中用于暫時替代原框架中在App.vue中進(jìn)行的全局初始化操作。)
-- 組件
beforeCreate
created
beforeMount
mounted
... (其他Vue后續(xù)生命周期)
幾點說明:
-
beforeCreate/created
是Vue的生命周期闯割,但是會在服務(wù)端和客戶端各執(zhí)行一次彻消,但這兩個鉤子,僅供了解宙拉,不能用于數(shù)據(jù)請求宾尚。 -
asyncData
和fetch
都是Nuxt提供的生命周期,都可用于數(shù)據(jù)請求谢澈。只是寫法略有不同(參考后續(xù)章節(jié)<a href='#five'>【五煌贴、數(shù)據(jù)請求】</a>)。 -
@/plugins/all.client.js
并非Nuxt生命周期锥忿,是只在客戶端運行的插件牛郑。但是Nuxt
框架去掉了app.vue
,此插件的生命周期敬鬓,近似于原來的app.vue
井濒,故暫時用于替代原框架中在App.vue中進(jìn)行的全局初始化操作(是否恰當(dāng)暫時不知)。
<a name='five'>【五列林、數(shù)據(jù)請求】</a>
1. 數(shù)據(jù)請求鉤子
1.1 鉤子相關(guān)說明
-
asyncData
和fetch
都是Nuxt提供的生命周期瑞你,都可用于數(shù)據(jù)請求,都會在服務(wù)端預(yù)請求數(shù)據(jù)進(jìn)行組裝希痴; -
asyncData
只能在pages
級別的頁面中調(diào)用者甲,在子組件內(nèi)部不能調(diào)用;fetch
則可以同時在頁面和子組件中調(diào)用砌创; - 官方建議數(shù)據(jù)請求均采用
asyncData
虏缸,但為了保持與老框架寫法的一致鲫懒,本框架暫時建議采用fetch
(后果未知) -
fetch
請求相比于asyncData
的已知缺陷有:- ① 數(shù)據(jù)請求較慢,本框架Demo刽辙,從index頁進(jìn)入Detail頁窥岩,當(dāng)使用
fetch
請求時,可明顯看到瀏覽器選項卡的title出現(xiàn)一瞬間undefined
- ① 數(shù)據(jù)請求較慢,本框架Demo刽辙,從index頁進(jìn)入Detail頁窥岩,當(dāng)使用
- 盡管
beforeCreate/created
也可以在服務(wù)端渲染宰缤,但是這兩個鉤子的數(shù)據(jù)請求操作只會在客戶端執(zhí)行颂翼,原則上不用于頁面初始化。
1.2 asyncData
- asyncData 中不能訪問this慨灭,但是可以在第一參數(shù)中朦乏,拿到context上下文,使用context.app訪問Vue根示例氧骤;
- context上下文還包含store呻疹、route、params筹陵、query等數(shù)據(jù)刽锤,詳見context上下文
- asyncData中無法拿到組件實例(
this
),不能訪問組件實例中的data method等方法朦佩。 - 詳細(xì)介紹:asyncData
- 【請求示例】
// ① 使用return返回的對象并思,將直接初始化到組件`data`中
async asyncData({app, params}) {
const { code, data } = await app.$get('/policy/findById/'+params.id)
return {detail: data}
},
// ② return一個Promise,將在Promise執(zhí)行完成后吕粗,將數(shù)據(jù)初始化到組件`data`中
asyncData({app, params}) {
return app.$get('/policy/findById/'+params.id).then(res => {
return {detail: data}
})
},
// ③ 第二個參數(shù)為callback回調(diào)函數(shù),可直接傳入數(shù)據(jù)旭愧,初始化到組件`data`中
asyncData({app, params}, callback) {
app.$get('/policy/findById/'+params.id).then(res => {
callback(null, {detail: data})
})
},
1.3 fetch
- fetch 分兩種情況(新版本后支持第二種情況):
- ① 第一個參數(shù)接受context上下文颅筋,則與asyncData一樣,不能訪問this和組件實例输枯; (這種情況议泵,也不支持像asyncData一樣通過return或者回調(diào)函數(shù)修改data內(nèi)容)
- ② 不接受任何參數(shù)時,則可以正常訪問this桃熄。(可以近似的看成created的用法先口,區(qū)別是 必須要使用await 或者return一個primary)
- 詳細(xì)介紹:fetch英文文檔 (中文文檔嚴(yán)重延遲,存在錯誤)
- 【請求示例】
// ① 使用return返回一個Promise
fetch() {
return this.getDetail()
},
// ② 使用await/async
async fetch() {
await this.getDetail()
},
methods: {
// ① 使用await編寫methods方法
async getDetail(id){
const { code, data } = await this.$get('/policy/findById/'+this.$route.params.id)
this.detail = data
}
// ② 使用return Promise編寫methods方法
getDetail(id){
return this.$get('/policy/findById/'+this.$route.params.id).then(resw => {
this.detail = res.data
})
}
}
2. 數(shù)據(jù)請求方式
2.1 【框架推薦】 使用vue實例直接調(diào)用
- 本框架會將
$request/$get/$post
掛在到vue根示例瞳收,建議直接只用this
或上下文context.app
調(diào)用 - 【請求示例】
// 以this調(diào)用為例碉京,如果是在`asyncData`中,需要使用上下文`context.app`調(diào)用
// ① get
this.$get('/policy/findById/'+this.$route.params.id)
// ② post
this.$post('/policy/findAll/',{page:1,size:10,params:{}})
// ③ request
this.$request({
url: '/policy/findAll/',
method: 'post',
data: {page:1,size:10,params:{}}
})
2.2 兼容老框架的api分離式調(diào)用
- 本框架推薦使用
五 2.1
的方式調(diào)用螟深,但是也兼容了老框架的api分離式調(diào)用谐宙,用于提取可復(fù)用的公共請求
。 - 公共請求的api文件界弧,統(tǒng)一放在
@/api/*.js
管理凡蜻。 - 【請求示例】
/**
* @/api/index.js
*/
import request from '@/utils/request'
export function getPageList(data) {
return request.post('/policy/findAll', data)
}
/**
* @/pages/index.vue
*/
import { getPageList } from "@/api/index.js"
export default {
fetch() {
return this.getPageList(this.pageDto)
},
methods: {
getPageList(pageDto) {
return getPageList(pageDto).then(res => {
this.pageList = res.data.result
})
}
},
}
3. 其他注意事項
- 原則上搭综,所有初始化渲染數(shù)據(jù)的請求,都要在服務(wù)端渲染函數(shù)(
asyncData
或fetch
)中進(jìn)行划栓,極個別無法在服務(wù)端渲染的請求兑巾,可以在Vue的生命周期(created
或mounted
)中初始化; - 服務(wù)端渲染的生命周期(即
asyncData/fetch
)忠荞,不能使用任何瀏覽器專屬的對像(如DOM
對象)蒋歌,也就是document
和window
,以及window
的各種對象和方法钻洒,例如setTimeout
奋姿、setInterval
、localStorage
素标、sessionStorage
等称诗;
有上述需求的初始化邏輯,可以放到created
或mounted
中初始化头遭。
<a name='six'>【六寓免、其他規(guī)范與Q&A】</a>
1. 關(guān)于pages
- 本框架路由采用
約定式路由
,即不再使用route.js
進(jìn)行路由聲明计维,而是由框架根據(jù)pages
目錄自動生成路由袜香,詳見路由 - 文件夾或者文件,如果以
_
開頭鲫惶,表示此為動態(tài)路由蜈首,可以傳入不同參數(shù),在組件內(nèi)容欠母,可以使用上下文或者this.$router取到路由參數(shù)欢策;- 例如:
/pages/news/detail/_id.vue
、/pages/news/detail/_id/index.vue
- 訪問:
http://domain.com/pages/news/detail/12345
(上述兩種寫法均為這一路徑)
- 例如:
【注意】
- ① 使用
_id.vue
的寫法赏淌,表示id
為可選參數(shù)踩寇,即可以通過http://domain.com/pages/news/detail
訪問。如果要對id進(jìn)行限制或驗證六水,可以在組件內(nèi)使用validate()
驗證俺孙; - ② 使用
/_id/index.vue
的寫法,表示id
為必選參數(shù)掷贾,訪問http://domain.com/pages/news/detail
會報404睛榄。如果只要求id必填,而沒有其他格式限制想帅,可以使用此方式懈费。 - ③
validate()
驗證示例
// return true表示驗證通過,return false表示驗證失敗 404
validate({ params }) {
return /^\d+$/.test(params.id)
},
2. 關(guān)于plugins
- 用于自定義框架所需的各種插件博脑,聲明插件后在
nuxt.config.js
中引入插件即可憎乙,類似于原框架main.js
相關(guān)功能票罐。詳見插件 - 框架已有的插件包(具體用戶參照各插件的
頂部注釋
):-
plugin.js
用于全局引入各種npm包; -
global.js
用于聲明全局變量與全局方法泞边; -
request.js
實現(xiàn)了全局請求封裝(對應(yīng)@/utils/request.js
); -
filter.js
實現(xiàn)了全局請求封裝(對應(yīng)@/utils/filter.js
); -
util.js
實現(xiàn)了全局請求封裝(對應(yīng)@/utils/util.js
); -
all.client.js
只在客戶端引入该押,用于替代原框架中app.vue
中的各種初始化操作;
-
- 其他插件可根據(jù)需要自行定義阵谚,
*.js
表示服務(wù)端客戶端均導(dǎo)入蚕礼;*.client.js
表示僅在客戶端導(dǎo)入;*.server.js
表示只在服務(wù)端導(dǎo)入梢什;
3. 關(guān)于layout
- 用于定義框架中的各種布局文件奠蹬,可根據(jù)需要自行定義,詳見布局與視圖
- 默認(rèn)視圖為
default.vue
嗡午,默認(rèn)所有頁面都將調(diào)用囤躁;error.vue
是錯誤視圖,當(dāng)頁面出現(xiàn)問題時荔睹,自動調(diào)用狸演; - 其他視圖,可根據(jù)需要自行定義僻他,并在組件內(nèi)部聲明引用宵距。
- 【組件調(diào)用示例】
export default {
// 需要調(diào)用的視圖名稱,不寫默認(rèn)調(diào)用default.vue
layout: 'onlyBody',
data(){
return {}
}
}
4. 關(guān)于components
- 用于定義框架中的各種自定義組件吨拗,可根據(jù)需要自行定義满哪。
- 自定義組件中的數(shù)據(jù),一般應(yīng)從頁面?zhèn)魅肴芭瘢绻枰俳M件內(nèi)部獲取數(shù)據(jù)哨鸭,應(yīng)該使用
fetch
(子組件中不支持asyncData
)。 -
components
中聲明的各種組件携龟,在使用時兔跌,無需import
導(dǎo)入勘高。直接使用組件名按需調(diào)用即可峡蟋。 - 【使用示例】
<template>
<div>
// Header組件
<Header />
</div>
</template>
5. 關(guān)于store
-
store
文件夾為Nuxt提供用于定義Vuex狀態(tài)樹的文件夾,詳細(xì)文檔參照:Vuex狀態(tài)樹华望。 - 此文件夾下面的xxx.js蕊蝗,分別表示一個模塊,例如
index.js
對應(yīng)$store.state.xxx
赖舟,而user.js
對應(yīng)$store.state.user.xxx
- 本框架中
store
中模塊的定義與普通Vue框架大體相同蓬戚,只是Nuxt框架會自動引用Vuex并加載到構(gòu)建配置重,無需我們自己new Vuex()
- 【使用示例】
/**
* 【注意區(qū)別】
* state mutations action不再是包裹在一個對象中宾抓,并在new Vuex()的時候傳入子漩。 而是分別作為單獨模塊使用export導(dǎo)出即可豫喧。
*/
export const state = () => ({
counter: 0
})
export const mutations = {
increment(state) {
state.counter++
}
}
6. 關(guān)于middleware
-
middleware
是框架中用于聲明中間件的文件夾,聲明后在nuxt.config.js
中配置中間件即可幢泼,詳細(xì)文檔參照:中間件紧显。 -
@/middleware/router.js
為已經(jīng)升級聲明好的路由守衛(wèi)中間件
,可替代原框架中router.beforeEach
中的路由守衛(wèi)功能缕棵;
7. 關(guān)于modules
- 用于自定義模塊的文件夾孵班,可以在模塊中對Nuxt啟動部署的各種生命周期設(shè)置監(jiān)聽,詳細(xì)文檔參照:模塊招驴。
-
@/modules/generator.ts
實現(xiàn)了一個對靜態(tài)化結(jié)束generate:done
時進(jìn)行監(jiān)聽并處理的示例篙程。
const generator: any = function () {
this.nuxt.hook('generate:done', (context: any) => {
// TODO samething
})
}
export default generator
- 類似
this.nuxt.hook('generate:done',() => {})
的Nuxt框架hooks
還有很多,例如:ready
别厘、error
虱饿、render:before
、build:compile
等等……詳細(xì)參見INTERNALS
<a name='six-8'>8. 其他Q&A
</a>
1)每個頁面丹允,必須使用head
設(shè)置title
郭厌,必要時還需在詳情頁設(shè)置description
。(5癖巍U勰!切記EI仁邸!)
export default {
head() {
return {
// title必須設(shè)置OА3斜! 列表可以直接寫“xxx列表”食零,詳情頁等有不同標(biāo)題的困乒,要用新聞標(biāo)題、商品標(biāo)題等作為title前綴贰谣。
title: this.detail.title + '_新聞詳情',
meta: [
// 詳情頁娜搂,需要設(shè)置不同的description。 this.$getDesc 為全局封裝的從富文本中截取100字符的description
{ hid: 'description', name: 'description', content: this.$getDesc(this.detail.details) },
],
}
}
}
2)pages
目錄中的層級結(jié)構(gòu)吱抚,務(wù)必按照功能梳理清楚百宇,比如“news(新聞)”
的列表、詳情都要在一個文件夾中秘豹。
<font color="#dd0000"><b>(P!!目錄結(jié)構(gòu)一旦確定啄刹,原則上不可再調(diào)整d套!J木)</b></font>
3)框架中的其他重要文件之【CSS
篇】2采!
- 框架各種
css
文件谭企,位于@/assets/css/
中廓译。框架推薦使用scss
語言债查,使用"sass": "~1.32.13"
進(jìn)行編譯非区; -
common.scss
為全局公共CSS,請將全局樣式表聲明于此盹廷≌鞒瘢或自行定義CSS文件,并在此文件中import
導(dǎo)入俄占; -
font.scss
用于定義本框架各種字體管怠、圖標(biāo)庫等; -
variables.scss
聲明了框架的各種全局Scss變量缸榄,可以在所有頁面使用渤弛。- 注意:全局主題色,請用
$mainColor
表示甚带,不要在各自文件中自行聲明她肯!
- 注意:全局主題色,請用
-
element-variables.scss
是ElementUI的主題聲明文件,如需全局調(diào)整ElementUI的配色鹰贵,請在此調(diào)整晴氨;