Nuxt.js是一個(gè)基于Vue.js的通用應(yīng)用架構(gòu), 它預(yù)設(shè)了服務(wù)端渲染(SSR, Server Side Render)應(yīng)用所需要的相關(guān)配置, 同時(shí)也支持生成靜態(tài)站點(diǎn).
1. 背景 & Nuxt簡(jiǎn)介
Nuxt
其一目的是為了解決單頁(yè)面應(yīng)用的SEO
問(wèn)題, 相比于我們平常的SPA
頁(yè)面. 在搜索引擎中由于無(wú)法從網(wǎng)頁(yè)中被抓取內(nèi)容信息(SPA頁(yè)面的信息都是被打包到JS文件中,動(dòng)態(tài)加載到頁(yè)面中), 從而無(wú)法被用戶(hù)所搜索到.
其二是服務(wù)端渲染相比于SPA頁(yè)面渲染,在網(wǎng)絡(luò)環(huán)境較差或者客戶(hù)端運(yùn)行在沒(méi)有JavaScript的引擎上, 這時(shí)基于SSR渲染
的頁(yè)面能更好的展現(xiàn)原有的頁(yè)面的內(nèi)容,而單頁(yè)面應(yīng)用可能就會(huì)有很長(zhǎng)的空白時(shí)間, 從而影響到用戶(hù)的體驗(yàn).
2. Nuxt應(yīng)用架構(gòu)
- 客戶(hù)端向服務(wù)器請(qǐng)求數(shù)據(jù)
- 服務(wù)器端獲取數(shù)據(jù)從API服務(wù)器
- 服務(wù)端返回完整HTML頁(yè)面給客戶(hù)端
- 客戶(hù)端頁(yè)面渲染使用SPA
- 客戶(hù)端直接請(qǐng)求API服務(wù)器
3. 項(xiàng)目創(chuàng)建
為了更加方便快速的創(chuàng)建項(xiàng)目, Nuxt.js 團(tuán)隊(duì)提供了一個(gè)腳手架工具 create-nuxt-app.確保你已經(jīng)安裝 npx
(npx 已經(jīng)被內(nèi)置自 NPM 5.2.0)
$ npx create-nuxt-app <project-name>
它會(huì)讓你進(jìn)行一些選擇:
- 在集成的服務(wù)器端框架之間進(jìn)行選擇:
- None(默認(rèn)服務(wù)器)
- Express
- Koa
- Hapi
- Feathers
- Micro
- Adonis
- 選擇您喜歡的UI框架:
- None
- Bootstrap
- Vuetify
- Bulma
- Tailwind
- Element UI
- Ant Design Vue
- Buefy
- 選擇你想要的Nuxt模式(universal or SPA)
- 選擇axios module
- 選擇 Eslint
- 選擇 Prettier
當(dāng)運(yùn)行完成時(shí),它將安裝所有依賴(lài)項(xiàng),完成后啟動(dòng)項(xiàng)目:
$ npm run dev
應(yīng)用現(xiàn)在會(huì)運(yùn)行在 http://localhost:3000
4. 項(xiàng)目開(kāi)發(fā)
4.1 目錄結(jié)構(gòu)
- api: api接口
- assets:靜態(tài)資源
- components:組件
- layouts: 布局目錄
- logs: 日志
- middleware:中間件
- pages: 頁(yè)面目錄
- plugins:插件
- nuxt.config.js: nuxt 配置文件
4.2 配置
Nuxt.js默認(rèn)的配置涵蓋了大部分的使用情形, 也可以通過(guò)修改 nuxt.config.js
來(lái)進(jìn)行自定義配置.
- plugins: 全局引入的插件
- css: 全局引入的css, scss 等
- head: 可以設(shè)置pages的頭部信息, 如titile, meta信息等
- loading:頁(yè)面切換的時(shí)候加載組件顯示的進(jìn)度條
- build: 自定義webpack的構(gòu)建配置
- env: 配置客戶(hù)端和服務(wù)端共享的環(huán)境變量
- cache:是否允許緩存
- router:自定義配置vue-router的信息
4.3 路由
Nuxt.js 會(huì)根據(jù)
pages
的目錄結(jié)構(gòu), 自動(dòng)生成vue-router
模塊的路由配置. 如上圖, 會(huì)生成 /dashboard/h5/:h5
, /dashboard/mws/:mws
, ...可以看出路徑根據(jù)目錄結(jié)構(gòu)自動(dòng)生成了, 動(dòng)態(tài)路徑需要在名字前添加下劃線(xiàn)( _ )
4.4 布局
上圖是Nuxt.js的布局架構(gòu). 最外層依舊是Document
, 往里一層是一個(gè)layout
層,在 Nuxt里面對(duì)應(yīng)目錄中的layouts文件夾,默認(rèn)的pages下的頁(yè)面都會(huì)套用 layouts/default.vue
的布局樣式. 其中 <nuxt/> 相當(dāng)于Vue
中的 slot
插槽的概念,
pages/**.vue的內(nèi)容都會(huì)被填入<nuxt/>,其他的內(nèi)容嵌套和平時(shí)的Vue單頁(yè)面應(yīng)用開(kāi)發(fā)是一樣的.
4.5 Vuex
在根目錄創(chuàng)建 store 目錄,就會(huì)默認(rèn)引用 vuex 模塊踩萎,除此之外雅倒,還進(jìn)行了以下的操作:1)將 vuex 模塊 加到 vendors 構(gòu)建配置中去绅络;2)設(shè)置 Vue 根實(shí)例的 store 配置項(xiàng).
Nuxt.js 支持兩種使用 store 的方式:
- 普通方式:
store/index.js
返回一個(gè)Vuex.Store
實(shí)例 - 模塊方式:store 目錄下的每個(gè) .js 文件會(huì)被轉(zhuǎn)換成為狀態(tài)樹(shù)指定命名的子模塊 (當(dāng)然别惦,index 是根模塊朗伶,相當(dāng)于設(shè)置了namespaced: true)
Nuxt.js提供了模塊方式的簡(jiǎn)單寫(xiě)法:使用狀態(tài)樹(shù)模塊化的方式,store/index.js 不需要返回 Vuex.Store
實(shí)例步咪,直接將 state
论皆、mutations
和 actions
暴露出來(lái)即可。示例如下:
export const state = () => ({
accesstoken: ''
})
export const mutations = {
setAccesstoken (state, accesstoken) {
state.accesstoken = accesstoken
}
}
4.6 異步數(shù)據(jù) asyncData
Nuxt.js 增加了一個(gè) asyncData
方法猾漫,用于 在設(shè)置組件數(shù)據(jù) 之前 能夠異步獲取 或 處理數(shù)據(jù)点晴。
由于asyncData
是在組件 初始化 之前被調(diào)用的,所以不能通過(guò) this
引用組件的實(shí)例對(duì)象悯周,可以使用上下文對(duì)象來(lái)實(shí)現(xiàn)某些功能,具體的上下文context
4.7 fetch 方法
與 asyncData
方法類(lèi)似粒督,不同的是它不會(huì)設(shè)置組件的數(shù)據(jù),作用是設(shè)置store
數(shù)據(jù)禽翼。
5. Nuxt 渲染流程
上圖是nuxt整個(gè)的渲染流程,在render
之前的幾個(gè)階段, 都可以拿到context
去做一些相應(yīng)操作.
-
nuxtServerInit
是Nuxt.js在服務(wù)端初始化的時(shí)候定義在store中.這個(gè)對(duì)我們想要直接傳遞值給服務(wù)端非常有用,比如session, user. -
middleware
是一個(gè)自定義的方法,在每次渲染頁(yè)面之前被調(diào)用.它可以注冊(cè)到全局下(nuxt.config.js
)也可以注冊(cè)在單個(gè)頁(yè)面或框架上. -
validate
允許在動(dòng)態(tài)路由組件中定義一個(gè)過(guò)濾器方法. -
asyncDate,fetch
asyncData可以讓我們?cè)陧?yè)面繪制前調(diào)用方法獲取需要的數(shù)據(jù)源;第一次時(shí)在服務(wù)端會(huì)被調(diào)用,之后客戶(hù)端也會(huì)在頁(yè)面之前被調(diào)用.fetch
和asyncData
非常相似,區(qū)別只在于fetch
只會(huì)用來(lái)改變store
的狀態(tài),不能填充數(shù)據(jù). *需要的一點(diǎn).如果在方法中調(diào)用this
會(huì)報(bào)錯(cuò).因?yàn)?code>asyncData & fetch在服務(wù)端會(huì)被調(diào)用所以this
的當(dāng)前組件并沒(méi)有實(shí)例化 -
Render
被渲染
6. 一些遇到的坑
-
Window 或 Document 對(duì)象未定義屠橄?
這是因?yàn)橐恍┲患嫒菘蛻?hù)端的腳本被打包進(jìn)了服務(wù)端的執(zhí)行腳本中去。 對(duì)于只適合在客戶(hù)端運(yùn)行的腳本闰挡,需要通過(guò)使用 process.browser 變量來(lái)判斷導(dǎo)入.
-
服務(wù)端渲染v-for 列表
當(dāng)頁(yè)面有列表內(nèi)容在客戶(hù)端渲染,刷新頁(yè)面服務(wù)端會(huì)重復(fù)渲染列表結(jié)構(gòu),生成兩份. 需要用
<no-ssr/>
組件進(jìn)行設(shè)置,從而不在無(wú)武器渲染中呈現(xiàn)