想要學(xué)習(xí)nuxt.js,首先要弄清楚客戶(hù)端渲染和服務(wù)端渲染這兩個(gè)概念。
一、客戶(hù)端渲染 VS 服務(wù)端渲染
1. 客戶(hù)端渲染
簡(jiǎn)單理解就是,在服務(wù)端放一個(gè)html 頁(yè)面,客戶(hù)端發(fā)起請(qǐng)求時(shí),服務(wù)端把頁(yè)面(響應(yīng)的是字符串)發(fā)送過(guò)去??蛻?hù)端從上到下依次解析,如果發(fā)現(xiàn)ajax請(qǐng)求就再發(fā)送新請(qǐng)求,拿到ajax 響應(yīng)結(jié)果以后渲染模板引擎。整個(gè)過(guò)程至少要發(fā)起兩次請(qǐng)求。如圖:

但是,這種渲染方式存在的弊端也日益顯露出來(lái),比如首屏渲染慢,不利于seo等問(wèn)題。想對(duì)應(yīng)的,服務(wù)端渲染恰好彌補(bǔ)了這些不足。
2. 服務(wù)端渲染:
也稱(chēng)SSR,即server side render的縮寫(xiě)。在服務(wù)端渲染出完整的首屏dom結(jié)構(gòu),直接發(fā)送到瀏覽器;前端拿到的內(nèi)容包括首屏及完整spa結(jié)構(gòu),應(yīng)用激活后依然按照spa方式運(yùn)行。整個(gè)過(guò)程只向服務(wù)端發(fā)起一次請(qǐng)求。如圖:

服務(wù)端渲染有兩大優(yōu)點(diǎn):
一是更利于SEO。因?yàn)榕老x(chóng)只會(huì)爬取源碼,不會(huì)執(zhí)行腳本。使用了MVVM框架之后,頁(yè)面的大多數(shù)DOM元素是在客戶(hù)端根據(jù)js動(dòng)態(tài)生成的,可供爬蟲(chóng)抓取分析的內(nèi)容很少。而且瀏覽器爬蟲(chóng)不會(huì)等數(shù)據(jù)加載完成之后再去抓取。服務(wù)端渲染返回的是已經(jīng)獲取了異步數(shù)據(jù)并執(zhí)行JavaScript腳本的最終HTML,爬蟲(chóng)就可以抓取完整的頁(yè)面信息。
二是更利于首屏渲染。對(duì)服務(wù)端渲染而言,首屏渲染是node發(fā)送過(guò)來(lái)的html字符串,不依賴(lài)于js文件,這樣用戶(hù)就能更快地看到頁(yè)面內(nèi)容。尤其是大型單頁(yè)應(yīng)用,資源請(qǐng)求量大,造成首屏渲染加載緩慢,使用服務(wù)端渲染就可以在很大程度上解決首頁(yè)的白屏等待問(wèn)題。
Nuxt.js作為Vue.js的通用框架,就常被用來(lái)作SSR。
二、nuxt.js
nuxt是一個(gè)專(zhuān)注于ui渲染的應(yīng)用框架,可以快速搭建項(xiàng)目,還提供了服務(wù)端渲染的功能。
1. 安裝
直接用vue-cli安裝
vue init nuxt-community/starter-template <project-name>
2. nuxt推薦的項(xiàng)目結(jié)構(gòu)
assets——資源文件
components——組件
layouts——布局,默認(rèn)default。所有頁(yè)面都會(huì)加載在布局頁(yè)面中的<nuxt />標(biāo)簽中。如果要在普通頁(yè)面中使用下級(jí)路由,則要在頁(yè)面中添加<nuxt-child />
middleware——中間件:每個(gè)頁(yè)面加載前調(diào)用,在頁(yè)面中調(diào)用的方法是middleware: 'middlewareName'。
node_modules——依賴(lài)包
nuxt.config.js——個(gè)性化配置
package.json——
pages——頁(yè)面。根頁(yè)面是index.vue,二級(jí)頁(yè)面只要添加文件夾。動(dòng)態(tài)路由頁(yè)面的名稱(chēng)格式是:_變量.vue
plugins——插件
static——靜態(tài)文件(不需要webpack打包的)。
store——狀態(tài)管理
yarn.lock
3. 生命周期
Nuxt在vue的基礎(chǔ)上對(duì)生命周期做了擴(kuò)展:
export defualt {
middleware(){ }, // 服務(wù)端
validate(){ }, // 服務(wù)端
asyncData(){ }, // 服務(wù)端
fetch(){ }, // store數(shù)據(jù)加載
beforeCreate(){ }, // 服務(wù)端和客戶(hù)端都會(huì)執(zhí)行
created(){ }, // 服務(wù)端和客戶(hù)端都會(huì)執(zhí)行
beforeMount(){ }, //
mounted(){ } // 客戶(hù)端
}
4. asyncData(context)
如果需要服務(wù)端渲染,首次渲染時(shí)一定要使用這個(gè)方法。它可以在渲染組件前異步獲取數(shù)據(jù)。asyncData傳入context參數(shù),可以獲取一些信息,如:
export default {
asyncData(ctx){
ctx.app // 根實(shí)例
ctx.route // 路由實(shí)例
ctx.params // 路由參數(shù)
ctx.query // 路由問(wèn)號(hào)后的參數(shù)
ctx.error // 錯(cuò)誤處理方法
}
}
使用這個(gè)方法時(shí)要注意,如果由于服務(wù)器或api錯(cuò)誤導(dǎo)致無(wú)法渲染,就要做好容錯(cuò)機(jī)制,可以使用context.error方法。我們可以這樣做:
async asyncData(ctx){
try {
throw new Error()
} catch {
ctx.error( {statusCode: 500, message: '服務(wù)器開(kāi)小差了~'} ) // 這里的statusCode參數(shù)必須是http狀態(tài)碼
}
}
此時(shí),錯(cuò)誤頁(yè)可以通過(guò)/layout/error.vue自定義。
注意:該方法在服務(wù)端執(zhí)行,返回的數(shù)據(jù)與data()返回的數(shù)據(jù)合并。該方法在組件初始化前被調(diào)用,所以不能通過(guò)this引用實(shí)例對(duì)象。
5. head()
用于更新頭部信息title/descripe等,可以通過(guò)this獲取組件數(shù)據(jù)。
6. middleware()
在特定頁(yè)面實(shí)戰(zhàn)中間件使用axios請(qǐng)求數(shù)據(jù):
(1)nuxt項(xiàng)目默認(rèn)安裝axios,所以只要安裝proxy即可
npm install @nuxtjs/proxy
(2)在nuxt.config.js中加上:
export default {
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
proxy: {
'./api': {
target: 'http://www.xxx.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
(3)頁(yè)面中使用
import axios from 'axios'
export default {
data () {
return {
page: 0
}
},
async asyncData () {
let data = await axios.get('http://localhost:3000/api/admin/list')
return {
page: data.data.page
}
},
}
采用 import axios from 'axios' 方式引入axios時(shí),接口參數(shù)前須加baseURL。
5. 使用scss
(1)安裝sass
npm i node-sass sass-loader scss-loader --save-dev
(2)如果要全局使用某個(gè)scss文件,要借助sass-resources-loader,還需要在nuxt.config.js的build配置中調(diào)整導(dǎo)出的loader配置:
export default {
build: {
extend(config, { isDev, isClient }){
const sassResourcesLoader = {
loader: 'sass-resources-loader',
options: {
resources: [
// 填寫(xiě)需要全局注入scss的文件
'assets/styles/mixins.scss'
]
}
}
// 修改 scss sass 引用的 loader。
config.module.rules.forEach((rule) => {
if (rule.test.toString() === '/\\.vue$/') {
rule.options.loaders.sass.push(sassResourcesLoader)
rule.options.loaders.scss.push(sassResourcesLoader)
}
if (['/\\.sass$/', '/\\.scss$/'].indexOf(rule.test.toString()) !== -1) {
rule.use.push(sassResourcesLoader)
}
})
}
}
}
6. nuxt和vue的區(qū)別
(1)路由
nuxt按照 pages 文件夾的目錄結(jié)構(gòu)自動(dòng)生成路由
vue需在 src/router/index.js 手動(dòng)配置路由
(2)入口頁(yè)面
nuxt頁(yè)面入口為 layouts/default.vue
vue頁(yè)面入口為 src/App.vue
(3)webpack配置
nuxt內(nèi)置webpack,允許根據(jù)服務(wù)端需求,在 nuxt.config.js 中的build屬性自定義構(gòu)建webpack的配置,覆蓋默認(rèn)配置。
vue關(guān)于webpack的配置存放在build文件夾下。
7. 編譯過(guò)程
(1)加載nuxt.config.js;
(2)初始化nuxt,builder,開(kāi)始執(zhí)行構(gòu)建;
(3)準(zhǔn)備模板使用的參數(shù),然后根據(jù)模板生成真正的webpack編譯的js;
(4)分別執(zhí)行客戶(hù)端編譯和服務(wù)端編譯,生成最終的js腳本;
(5)編譯成功后,就需要啟動(dòng)服務(wù),監(jiān)聽(tīng)端口,這個(gè)是在npm run start中實(shí)現(xiàn)的。
關(guān)于nuxt.js先寫(xiě)這些了,更多內(nèi)容還是要去看官網(wǎng)文檔哦~
關(guān)注微信公眾號(hào)【CC前端手記】一起學(xué)更多前端小知識(shí)吧~