基于Nuxt.js項目的服務(wù)端性能優(yōu)化與錯誤檢測(容錯處理)
nuxt.js 是一個基于 Vue.js 的服務(wù)端渲染應(yīng)用框架,使用nuxt.js在做同構(gòu)項目開發(fā)時,需要考慮的一些點總結(jié)如下:
一、node服務(wù)端性能優(yōu)化(提高node應(yīng)用程序處理高流量的能力)
基于nuxt.js的服務(wù)端渲染項目我們能做的服務(wù)端性能優(yōu)化有以下幾點(需要注意的是持久化緩存不應(yīng)該在本地開發(fā)環(huán)境去做,這樣在緩存期間不會暴露本地開發(fā)中代碼的問題)
| 優(yōu)化點 | 參考文檔及思路 | 優(yōu)化場景/條件 | 特別說明 | 檢測方法 |
|---|---|---|---|---|
| 1. 頁面緩存 | vue官方文檔 | 頁面內(nèi)容不是用戶特定(即對于相同的 URL,總是為所有用戶渲染相同的內(nèi)容) | 一般來說,一個頁面在服務(wù)端做了持久化緩存,那么對應(yīng)頁面的存在的api緩存,組件緩存也就沒有意義了,對于頁面緩存與api緩存同時存在的情況下(有可能存在),api緩存的時間應(yīng)該比頁面緩存的時間小,這樣是為了讓api響應(yīng)的內(nèi)容保持最新 | 1、代碼本地測試:在asyncData中打印測試日志,頁面緩存后,刷新頁面后服務(wù)端不會輸出測試日志;2、比較html頁面加載的DOMContentLoaded時間,刷新頁面可以看到緩存后的值比首次頁面加載(未緩存)的值要小 |
| 2. api緩存 | 在axios請求與響應(yīng)攔截器中去做 | 接口響應(yīng)內(nèi)容不是用戶特定(即對于相同的api接口URL,即總是為所有用戶響應(yīng)相同的內(nèi)容) | 一般請求方式為GET的api請求 | 比較首次請求與緩存后的api接口響應(yīng)的時間 |
| 3. 組件緩存 | nuxtjs官網(wǎng)文檔 vue 官網(wǎng)文檔 | 不依賴與全局狀態(tài),對渲染上下文不產(chǎn)生副作用的子組件 | 要緩存的組件name值必須唯一,serverCacheKey根據(jù)某個prop的值作為唯一key | 檢測方法同頁面緩存檢測方法一致,這個可能幾乎察覺不到 |
| 4. asyncData函數(shù)優(yōu)化 | Promise.all | 該函數(shù)中請求api接口數(shù)超過1個,多的甚至達到10,20多個,這種情況我們不能使用async await,請求完一個再接著請求下一個(同步請求接口);如果有10個接口需要請求,每個接口平均響應(yīng)1s,那么至少需要10s才會響應(yīng)html頁面;如果使用Promise.all異步請求10個接口,那么最快接近1s響應(yīng)html頁面; | asyncData函數(shù)會在服務(wù)端執(zhí)行代碼,因此一定要做好容錯處理;另外如果該函數(shù)代碼一直未執(zhí)行完,那么頁面首次響應(yīng)將會被掛起,一直處于加載中 | 對于頁面首次加載,該函數(shù)執(zhí)行耗時越短,頁面響應(yīng)時間就越短(頁面加載越快) |
1、頁面緩存功能模塊實現(xiàn)
我們在項目根目錄中創(chuàng)建一個文件 ~/serverMiddleware/page-cache.js
import LRUCache from 'lru-cache'
const cache = new LRUCache({
maxAge: 1000 * 60 * 2, // 有效期2分鐘
max: 1000 // 最大緩存數(shù)量
})
export default function(req, res, next) {
// 本地開發(fā)環(huán)境不做頁面緩存
if (process.env.NODE_ENV !== 'development') {
try {
const cacheKey = req.url
const cacheData = cache.get(cacheKey)
if (cacheData) {
return res.end(cacheData, 'utf8')
}
const originalEnd = res.end
res.end = function(data) {
cache.set(cacheKey, data)
originalEnd.call(res, ...arguments)
}
} catch(error) {
// console.log(`page-cache-middleware: ${error}`)
next()
}
}
next()
}
2、api緩存功能模塊實現(xiàn)
我們在項目根目錄中分別創(chuàng)建兩個文件 ~/plugins/axios/createCacheKey.js 與 ~/plugins/axios/cache.js ;特別坑的一點是nuxt.js開發(fā)環(huán)境cache.js插件代碼在頁面刷新,路由切換都相當于首次運行,因此你會發(fā)現(xiàn)緩存功能失效,只有在 process.env.NODE_ENV === 'production' 生產(chǎn)環(huán)境中測試有效
// ~/plugins/axios/createCacheKey.js
import md5 from 'md5'
/**
* 根據(jù)請求配置,是否是請求攔截器 創(chuàng)建緩存key
* @param {Object} config
* @param {Boolean} isRequest
*/
export default function createCacheKey(
config = {},
isRequest = false
) {
const {
url,
data,
params,
method,
baseURL,
} = config || {}
let commonUrl = url
/**
* request攔截器中config.url是未拼接baseURL的,response攔截器中response.config.url是拼接過baseURL的,
* 為了保持統(tǒng)一,使用統(tǒng)一拼接baseURL的commonUrl;注意下面的if條件判斷
*/
if (isRequest && !commonUrl.match(baseURL) && !commonUrl.match(/^https?/)) {
commonUrl = !!baseURL.match(/.+\/$/) ? `${baseURL.replace(/\/$/, '')}${url}` : `${baseURL}${url}`
}
// 根據(jù)請求指令,url,body體,參數(shù)生成規(guī)則
const rule = `method=${method}-url=${commonUrl}-data=${JSON.stringify(data || {})}-params=${JSON.stringify(params || {})}`
// md5加密
return md5(rule)
}
// ~/plugins/axios/cache.js
import LRUCache from 'lru-cache'
import axios from 'axios'
import globalConfig from '../../global-config'
import createCacheKey from './createCacheKey'
const cache = new LRUCache({
maxAge: 1000 * 60, // 有效期60秒,如果存在頁面緩存,api緩存的時間應(yīng)該比頁面緩存的時間小,這樣是為了讓api響應(yīng)的內(nèi)容保持最新
max: 1000 // 最大緩存數(shù)量
})
/**
* matchCacheCondition 是否滿足持久化緩存條件:服務(wù)端運行時 && 非本地開發(fā)環(huán)境 && api請求為get請求方式
* @param {Object} config 請求配置
*/
function matchCacheCondition(config = {}) {
return process.server && process.env.NODE_ENV !== 'development' && config.method.toLowerCase() === 'get'
}
/**
* 如果所有頁面都啟用了緩存,api緩存就沒有必要了
*/
export default function({ $axios, redirect }) {
$axios.interceptors.request.use(config => {
const { baseUrl } = globalConfig
config.baseURL = baseUrl[process.env.environment] || baseUrl['other']
// 不滿足緩存條件直接return config
if (!matchCacheCondition(config)) {
return config
}
const cacheKey = createCacheKey(config, true)
const cacheData = cache.get(cacheKey)
if (cacheData) {
const source = axios.CancelToken.source()
config.cancelToken = source.token
source.cancel({ cacheData, cacheKey, url: config.url })
return config
}
return config
})
$axios.interceptors.response.use(response => {
if (matchCacheCondition(response.config)) {
cache.set(createCacheKey(response.config), response)
}
return response
}, (error) => {
if (axios.isCancel(error) && matchCacheCondition(response.config)) {
// console.log(`當前頁面組件asyncData或者fetch函數(shù)中被緩存的接口url為:${error.message.url}`)
return Promise.resolve(error.message.cacheData)
}
// 服務(wù)端打印api接口請求錯誤日志
if (process.server) {
try {
const {
config: {
url
},
message
} = error || {}
console.log(`請求url:${url},錯誤消息:${message}`)
} catch(error) {
// console.log(error)
}
}
// 服務(wù)端,客戶端統(tǒng)一reject錯誤對象,因此頁面組件asyncData,fetch函數(shù)請求api接口一定要做catch處理
return Promise.reject(error)
})
}
3、組件緩存
vue官網(wǎng)文檔原話:如果 renderer 在組件渲染過程中進行緩存命中,那么它將直接重新使用整個子樹的緩存結(jié)果。這意味著在以下情況,你不應(yīng)該緩存組件:
- 它具有可能依賴于全局狀態(tài)的子組件。
- 它具有對渲染上下文產(chǎn)生副作用(side effect)的子組件。
因此,應(yīng)該小心使用組件緩存來解決性能瓶頸。在大多數(shù)情況下,你不應(yīng)該也不需要緩存單一實例組件。適用于緩存的最常見類型的組件,是在大的 v-for 列表中重復(fù)出現(xiàn)的組件。由于這些組件通常由數(shù)據(jù)庫集合(database collection)中的對象驅(qū)動,它們可以使用簡單的緩存策略:使用其唯一 id,再加上最后更新的時間戳,來生成其緩存鍵(cache key):
serverCacheKey: props => props.item.id + '::' + props.item.last_updated
4、頁面組件asyncData函數(shù)優(yōu)化
舉一個簡單的例子進行優(yōu)化
{
async asyncData({ $axios }) {
// 1、增加catch處理,是為了讓服務(wù)端,客戶端運行時不報錯,特別是防止服務(wù)端運行時不報錯,不然頁面就掛了
// 2、catch函數(shù)返回一個resolve空字面量對象的Promise,表明dataPromise1的狀態(tài)未來始終是resolved狀態(tài)
const dataPromise1 = $axios.get('/api/data1').catch(() => Promise.resolve({}))
const dataPromise2 = $axios.get('/api/data2').catch(() => Promise.resolve({}))
const dataPromise3 = $axios.get('/api/data3').catch(() => Promise.resolve({}))
const dataPromise4 = $axios.get('/api/data4').catch(() => Promise.resolve({}))
const dataPromise5 = $axios.get('/api/data5').catch(() => Promise.resolve({}))
const dataPromise6 = $axios.get('/api/data6').catch(() => Promise.resolve({}))
const dataPromise7 = $axios.get('/api/data7').catch(() => Promise.resolve({}))
const dataPromise8 = $axios.get('/api/data8').catch(() => Promise.resolve({}))
// 保證apiData有數(shù)據(jù)
const apiData = await new Promise(resolve => {
Promise.all([
dataPromise1, dataPromise2, dataPromise3, dataPromise4,
dataPromise5, dataPromise6, dataPromise7, dataPromise8,
])
.then(dataGather => {
resolve({
data1: dataGather[0],
data2: dataGather[1],
data3: dataGather[2],
data4: dataGather[3],
data5: dataGather[4],
data6: dataGather[5],
data7: dataGather[6],
data8: dataGather[7],
})
})
})
return apiData
}
}
二、node服務(wù)端錯誤檢測,容錯處理(提高node應(yīng)用程序處理容錯的能力)
首先確定使用nuxt.js框架,vue組件(頁面/非頁面組件)中以下函數(shù)都會在服務(wù)端執(zhí)行,因此代碼容錯非常重要,函數(shù)代碼執(zhí)行一旦出錯,頁面就掛了
- fetch
- asyncData
- beforeCreate
- created
1、看的見的錯誤
看的見的錯誤是指在開發(fā)環(huán)境中,你只要在fetch等以上函數(shù)中js執(zhí)行錯誤,本地就會有錯誤提示,便于你發(fā)現(xiàn)糾正錯誤代碼邏輯
2、未知/看不見的錯誤(讓未知錯誤暴露出來)
看不見的錯誤是指一些異步回調(diào)中的錯誤代碼不容易被發(fā)現(xiàn),如果異步行為一直沒有觸發(fā),那么處理該異步行為的回調(diào)代碼也不會執(zhí)行;但是對于處理所有頁面的api接口請求回調(diào)的錯誤排查(主要是做容錯處理,使代碼更加健壯,java接口請求404、接口數(shù)據(jù)字段/結(jié)構(gòu)的處理)我們能夠做好,很簡單,我們只需要在請求攔截器中把請求url更改就可以
$axios.interceptors.request.use(config => {
// TODO
// 檢測由于請求java接口失敗而導(dǎo)致的node應(yīng)用程序錯誤
config.url += '/xxxx'
return config
})
3、對于頁面刷新加載不需要渲染的數(shù)據(jù)的處理
只有頁面組件asyncData(函數(shù)返回的對象跟組件data融合),fetch(更新store操作)函數(shù)處理的數(shù)據(jù)跟頁面綁定后,頁面刷新加載服務(wù)端才會渲染;因此不建議組件在beforeCreate,created函數(shù)中通過請求api接口獲取頁面刷新加載不需要渲染的數(shù)據(jù),只需要在mounted函數(shù)中處理即可,防止由于代碼錯誤導(dǎo)致node應(yīng)用程序出錯
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
uniapp中實現(xiàn)canvas超出屏幕滾動查看功能
親愛的小伙伴,當你需要在uniapp中使用canvas繪制一個超長圖,就類似于橫向的流程圖時,這個canvas超出屏幕部分拖動屏幕查看會變得十分棘手,怎么解決這個問題呢,下面小編給大家介紹uniapp中實現(xiàn)canvas超出屏幕滾動查看功能,感興趣的朋友一起看看吧2024-03-03

