基于Nuxt.js項(xiàng)目的服務(wù)端性能優(yōu)化與錯(cuò)誤檢測(cè)(容錯(cuò)處理)
nuxt.js 是一個(gè)基于 Vue.js 的服務(wù)端渲染應(yīng)用框架,使用nuxt.js在做同構(gòu)項(xiàng)目開(kāi)發(fā)時(shí),需要考慮的一些點(diǎn)總結(jié)如下:
一、node服務(wù)端性能優(yōu)化(提高node應(yīng)用程序處理高流量的能力)
基于nuxt.js的服務(wù)端渲染項(xiàng)目我們能做的服務(wù)端性能優(yōu)化有以下幾點(diǎn)(需要注意的是持久化緩存不應(yīng)該在本地開(kāi)發(fā)環(huán)境去做,這樣在緩存期間不會(huì)暴露本地開(kāi)發(fā)中代碼的問(wèn)題)
優(yōu)化點(diǎn) | 參考文檔及思路 | 優(yōu)化場(chǎng)景/條件 | 特別說(shuō)明 | 檢測(cè)方法 |
---|---|---|---|---|
1. 頁(yè)面緩存 | vue官方文檔 | 頁(yè)面內(nèi)容不是用戶特定(即對(duì)于相同的 URL,總是為所有用戶渲染相同的內(nèi)容) | 一般來(lái)說(shuō),一個(gè)頁(yè)面在服務(wù)端做了持久化緩存,那么對(duì)應(yīng)頁(yè)面的存在的api緩存,組件緩存也就沒(méi)有意義了,對(duì)于頁(yè)面緩存與api緩存同時(shí)存在的情況下(有可能存在),api緩存的時(shí)間應(yīng)該比頁(yè)面緩存的時(shí)間小,這樣是為了讓api響應(yīng)的內(nèi)容保持最新 | 1、代碼本地測(cè)試:在asyncData中打印測(cè)試日志,頁(yè)面緩存后,刷新頁(yè)面后服務(wù)端不會(huì)輸出測(cè)試日志;2、比較html頁(yè)面加載的DOMContentLoaded時(shí)間,刷新頁(yè)面可以看到緩存后的值比首次頁(yè)面加載(未緩存)的值要小 |
2. api緩存 | 在axios請(qǐng)求與響應(yīng)攔截器中去做 | 接口響應(yīng)內(nèi)容不是用戶特定(即對(duì)于相同的api接口URL,即總是為所有用戶響應(yīng)相同的內(nèi)容) | 一般請(qǐng)求方式為GET的api請(qǐng)求 | 比較首次請(qǐng)求與緩存后的api接口響應(yīng)的時(shí)間 |
3. 組件緩存 | nuxtjs官網(wǎng)文檔 vue 官網(wǎng)文檔 | 不依賴與全局狀態(tài),對(duì)渲染上下文不產(chǎn)生副作用的子組件 | 要緩存的組件name值必須唯一,serverCacheKey根據(jù)某個(gè)prop的值作為唯一key | 檢測(cè)方法同頁(yè)面緩存檢測(cè)方法一致,這個(gè)可能幾乎察覺(jué)不到 |
4. asyncData函數(shù)優(yōu)化 | Promise.all | 該函數(shù)中請(qǐng)求api接口數(shù)超過(guò)1個(gè),多的甚至達(dá)到10,20多個(gè),這種情況我們不能使用async await,請(qǐng)求完一個(gè)再接著請(qǐng)求下一個(gè)(同步請(qǐng)求接口);如果有10個(gè)接口需要請(qǐng)求,每個(gè)接口平均響應(yīng)1s,那么至少需要10s才會(huì)響應(yīng)html頁(yè)面;如果使用Promise.all異步請(qǐng)求10個(gè)接口,那么最快接近1s響應(yīng)html頁(yè)面; | asyncData函數(shù)會(huì)在服務(wù)端執(zhí)行代碼,因此一定要做好容錯(cuò)處理;另外如果該函數(shù)代碼一直未執(zhí)行完,那么頁(yè)面首次響應(yīng)將會(huì)被掛起,一直處于加載中 | 對(duì)于頁(yè)面首次加載,該函數(shù)執(zhí)行耗時(shí)越短,頁(yè)面響應(yīng)時(shí)間就越短(頁(yè)面加載越快) |
1、頁(yè)面緩存功能模塊實(shí)現(xiàn)
我們?cè)陧?xiàng)目根目錄中創(chuàng)建一個(gè)文件 ~/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) { // 本地開(kāi)發(fā)環(huán)境不做頁(yè)面緩存 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緩存功能模塊實(shí)現(xiàn)
我們?cè)陧?xiàng)目根目錄中分別創(chuàng)建兩個(gè)文件 ~/plugins/axios/createCacheKey.js 與 ~/plugins/axios/cache.js ;特別坑的一點(diǎn)是nuxt.js開(kāi)發(fā)環(huán)境cache.js插件代碼在頁(yè)面刷新,路由切換都相當(dāng)于首次運(yùn)行,因此你會(huì)發(fā)現(xiàn)緩存功能失效,只有在 process.env.NODE_ENV === 'production' 生產(chǎn)環(huán)境中測(cè)試有效
// ~/plugins/axios/createCacheKey.js import md5 from 'md5' /** * 根據(jù)請(qǐng)求配置,是否是請(qǐng)求攔截器 創(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是拼接過(guò)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ù)請(qǐng)求指令,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秒,如果存在頁(yè)面緩存,api緩存的時(shí)間應(yīng)該比頁(yè)面緩存的時(shí)間小,這樣是為了讓api響應(yīng)的內(nèi)容保持最新 max: 1000 // 最大緩存數(shù)量 }) /** * matchCacheCondition 是否滿足持久化緩存條件:服務(wù)端運(yùn)行時(shí) && 非本地開(kāi)發(fā)環(huán)境 && api請(qǐng)求為get請(qǐng)求方式 * @param {Object} config 請(qǐng)求配置 */ function matchCacheCondition(config = {}) { return process.server && process.env.NODE_ENV !== 'development' && config.method.toLowerCase() === 'get' } /** * 如果所有頁(yè)面都啟用了緩存,api緩存就沒(méi)有必要了 */ 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(`當(dāng)前頁(yè)面組件asyncData或者fetch函數(shù)中被緩存的接口url為:${error.message.url}`) return Promise.resolve(error.message.cacheData) } // 服務(wù)端打印api接口請(qǐng)求錯(cuò)誤日志 if (process.server) { try { const { config: { url }, message } = error || {} console.log(`請(qǐng)求url:${url},錯(cuò)誤消息:${message}`) } catch(error) { // console.log(error) } } // 服務(wù)端,客戶端統(tǒng)一reject錯(cuò)誤對(duì)象,因此頁(yè)面組件asyncData,fetch函數(shù)請(qǐng)求api接口一定要做catch處理 return Promise.reject(error) }) }
3、組件緩存
vue官網(wǎng)文檔原話:如果 renderer 在組件渲染過(guò)程中進(jìn)行緩存命中,那么它將直接重新使用整個(gè)子樹(shù)的緩存結(jié)果。這意味著在以下情況,你不應(yīng)該緩存組件:
- 它具有可能依賴于全局狀態(tài)的子組件。
- 它具有對(duì)渲染上下文產(chǎn)生副作用(side effect)的子組件。
因此,應(yīng)該小心使用組件緩存來(lái)解決性能瓶頸。在大多數(shù)情況下,你不應(yīng)該也不需要緩存單一實(shí)例組件。適用于緩存的最常見(jiàn)類型的組件,是在大的 v-for 列表中重復(fù)出現(xiàn)的組件。由于這些組件通常由數(shù)據(jù)庫(kù)集合(database collection)中的對(duì)象驅(qū)動(dòng),它們可以使用簡(jiǎn)單的緩存策略:使用其唯一 id,再加上最后更新的時(shí)間戳,來(lái)生成其緩存鍵(cache key):
serverCacheKey: props => props.item.id + '::' + props.item.last_updated
4、頁(yè)面組件asyncData函數(shù)優(yōu)化
舉一個(gè)簡(jiǎn)單的例子進(jìn)行優(yōu)化
{ async asyncData({ $axios }) { // 1、增加catch處理,是為了讓服務(wù)端,客戶端運(yùn)行時(shí)不報(bào)錯(cuò),特別是防止服務(wù)端運(yùn)行時(shí)不報(bào)錯(cuò),不然頁(yè)面就掛了 // 2、catch函數(shù)返回一個(gè)resolve空字面量對(duì)象的Promise,表明dataPromise1的狀態(tài)未來(lá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ù)端錯(cuò)誤檢測(cè),容錯(cuò)處理(提高node應(yīng)用程序處理容錯(cuò)的能力)
首先確定使用nuxt.js框架,vue組件(頁(yè)面/非頁(yè)面組件)中以下函數(shù)都會(huì)在服務(wù)端執(zhí)行,因此代碼容錯(cuò)非常重要,函數(shù)代碼執(zhí)行一旦出錯(cuò),頁(yè)面就掛了
- fetch
- asyncData
- beforeCreate
- created
1、看的見(jiàn)的錯(cuò)誤
看的見(jiàn)的錯(cuò)誤是指在開(kāi)發(fā)環(huán)境中,你只要在fetch等以上函數(shù)中js執(zhí)行錯(cuò)誤,本地就會(huì)有錯(cuò)誤提示,便于你發(fā)現(xiàn)糾正錯(cuò)誤代碼邏輯
2、未知/看不見(jiàn)的錯(cuò)誤(讓未知錯(cuò)誤暴露出來(lái))
看不見(jiàn)的錯(cuò)誤是指一些異步回調(diào)中的錯(cuò)誤代碼不容易被發(fā)現(xiàn),如果異步行為一直沒(méi)有觸發(fā),那么處理該異步行為的回調(diào)代碼也不會(huì)執(zhí)行;但是對(duì)于處理所有頁(yè)面的api接口請(qǐng)求回調(diào)的錯(cuò)誤排查(主要是做容錯(cuò)處理,使代碼更加健壯,java接口請(qǐng)求404、接口數(shù)據(jù)字段/結(jié)構(gòu)的處理)我們能夠做好,很簡(jiǎn)單,我們只需要在請(qǐng)求攔截器中把請(qǐng)求url更改就可以
$axios.interceptors.request.use(config => { // TODO // 檢測(cè)由于請(qǐng)求java接口失敗而導(dǎo)致的node應(yīng)用程序錯(cuò)誤 config.url += '/xxxx' return config })
3、對(duì)于頁(yè)面刷新加載不需要渲染的數(shù)據(jù)的處理
只有頁(yè)面組件asyncData(函數(shù)返回的對(duì)象跟組件data融合),fetch(更新store操作)函數(shù)處理的數(shù)據(jù)跟頁(yè)面綁定后,頁(yè)面刷新加載服務(wù)端才會(huì)渲染;因此不建議組件在beforeCreate,created函數(shù)中通過(guò)請(qǐng)求api接口獲取頁(yè)面刷新加載不需要渲染的數(shù)據(jù),只需要在mounted函數(shù)中處理即可,防止由于代碼錯(cuò)誤導(dǎo)致node應(yīng)用程序出錯(cuò)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- JS性能優(yōu)化實(shí)現(xiàn)方法及優(yōu)點(diǎn)進(jìn)行
- js 函數(shù)性能比較方法
- JDK14性能管理工具之jstack使用介紹
- 高性能js數(shù)組去重(12種方法,史上最全)
- Vue.js 無(wú)限滾動(dòng)列表性能優(yōu)化方案
- 利用JavaScript的Map提升性能的方法詳解
- javascript for循環(huán)性能測(cè)試示例
- 監(jiān)控Nodejs的性能實(shí)例代碼
- 詳解如何提升JSON.stringify()的性能
- Javascript三種字符串連接方式及性能比較
- 實(shí)現(xiàn)高性能javascript的注意事項(xiàng)
- 如何使用gpu.js改善JavaScript的性能
相關(guān)文章
JavaScript實(shí)現(xiàn)的多種鼠標(biāo)拖放效果
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的多種鼠標(biāo)拖放效果,涉及JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)變換頁(yè)面元素屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11JS 實(shí)現(xiàn)請(qǐng)求調(diào)度器
這篇文章主要介紹了JS 實(shí)現(xiàn)請(qǐng)求調(diào)度器的方法,幫助大家更好的理解和學(xué)習(xí)使用js,感興趣的朋友可以了解下2021-03-03javaScript實(shí)現(xiàn)一個(gè)隊(duì)列的方法
這篇文章主要介紹了javaScript實(shí)現(xiàn)一個(gè)隊(duì)列的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07JavaScript 32位整型無(wú)符號(hào)操作示例
所有整數(shù)字變量默認(rèn)都是有符號(hào)整數(shù),JavaScript 進(jìn)行位操作時(shí),是采用32位有符號(hào)整型,這意味著其轉(zhuǎn)換的結(jié)果也是32位有符號(hào)整型2013-12-12微信小程序?qū)崿F(xiàn)簡(jiǎn)單計(jì)算器與秒表
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)簡(jiǎn)單計(jì)算器與秒表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09uniapp中實(shí)現(xiàn)canvas超出屏幕滾動(dòng)查看功能
親愛(ài)的小伙伴,當(dāng)你需要在uniapp中使用canvas繪制一個(gè)超長(zhǎng)圖,就類似于橫向的流程圖時(shí),這個(gè)canvas超出屏幕部分拖動(dòng)屏幕查看會(huì)變得十分棘手,怎么解決這個(gè)問(wèn)題呢,下面小編給大家介紹uniapp中實(shí)現(xiàn)canvas超出屏幕滾動(dòng)查看功能,感興趣的朋友一起看看吧2024-03-03