溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于Nuxt.js項目服務端性能優(yōu)化與錯誤檢測的示例分析

發(fā)布時間:2021-09-06 14:03:08 來源:億速云 閱讀:286 作者:小新 欄目:web開發(fā)

小編給大家分享一下基于Nuxt.js項目服務端性能優(yōu)化與錯誤檢測的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

nuxt.js 是一個基于 Vue.js 的服務端渲染應用框架,使用nuxt.js在做同構項目開發(fā)時,需要考慮的一些點總結如下:

一、node服務端性能優(yōu)化(提高node應用程序處理高流量的能力)

基于nuxt.js的服務端渲染項目我們能做的服務端性能優(yōu)化有以下幾點(需要注意的是持久化緩存不應該在本地開發(fā)環(huán)境去做,這樣在緩存期間不會暴露本地開發(fā)中代碼的問題)

優(yōu)化點參考文檔及思路優(yōu)化場景/條件特別說明檢測方法
1. 頁面緩存vue官方文檔頁面內容不是用戶特定(即對于相同的 URL,總是為所有用戶渲染相同的內容)一般來說,一個頁面在服務端做了持久化緩存,那么對應頁面的存在的api緩存,組件緩存也就沒有意義了,對于頁面緩存與api緩存同時存在的情況下(有可能存在),api緩存的時間應該比頁面緩存的時間小,這樣是為了讓api響應的內容保持最新1、代碼本地測試:在asyncData中打印測試日志,頁面緩存后,刷新頁面后服務端不會輸出測試日志;2、比較html頁面加載的DOMContentLoaded時間,刷新頁面可以看到緩存后的值比首次頁面加載(未緩存)的值要小
2. api緩存在axios請求與響應攔截器中去做接口響應內容不是用戶特定(即對于相同的api接口URL,即總是為所有用戶響應相同的內容)一般請求方式為GET的api請求比較首次請求與緩存后的api接口響應的時間
3. 組件緩存nuxtjs官網文檔 vue 官網文檔不依賴與全局狀態(tài),對渲染上下文不產生副作用的子組件要緩存的組件name值必須唯一,serverCacheKey根據某個prop的值作為唯一key檢測方法同頁面緩存檢測方法一致,這個可能幾乎察覺不到
4. asyncData函數優(yōu)化Promise.all該函數中請求api接口數超過1個,多的甚至達到10,20多個,這種情況我們不能使用async await,請求完一個再接著請求下一個(同步請求接口);如果有10個接口需要請求,每個接口平均響應1s,那么至少需要10s才會響應html頁面;如果使用Promise.all異步請求10個接口,那么最快接近1s響應html頁面;asyncData函數會在服務端執(zhí)行代碼,因此一定要做好容錯處理;另外如果該函數代碼一直未執(zhí)行完,那么頁面首次響應將會被掛起,一直處于加載中對于頁面首次加載,該函數執(zhí)行耗時越短,頁面響應時間就越短(頁面加載越快)

1、頁面緩存功能模塊實現

我們在項目根目錄中創(chuàng)建一個文件 ~/serverMiddleware/page-cache.js

import LRUCache from 'lru-cache'

const cache = new LRUCache({
 maxAge: 1000 * 60 * 2, // 有效期2分鐘
 max: 1000 // 最大緩存數量
})

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緩存功能模塊實現

我們在項目根目錄中分別創(chuàng)建兩個文件 ~/plugins/axios/createCacheKey.js 與 ~/plugins/axios/cache.js ;特別坑的一點是nuxt.js開發(fā)環(huán)境cache.js插件代碼在頁面刷新,路由切換都相當于首次運行,因此你會發(fā)現緩存功能失效,只有在 process.env.NODE_ENV === 'production' 生產環(huán)境中測試有效

// ~/plugins/axios/createCacheKey.js

import md5 from 'md5'

/**
 * 根據請求配置,是否是請求攔截器 創(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}`
 }

 // 根據請求指令,url,body體,參數生成規(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緩存的時間應該比頁面緩存的時間小,這樣是為了讓api響應的內容保持最新
 max: 1000 // 最大緩存數量
})

/**
 * matchCacheCondition 是否滿足持久化緩存條件:服務端運行時 && 非本地開發(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函數中被緩存的接口url為:${error.message.url}`)
  return Promise.resolve(error.message.cacheData)
 }

 // 服務端打印api接口請求錯誤日志
 if (process.server) {
  try {
  const {
   config: {
   url
   },
   message
  } = error || {}
  console.log(`請求url:${url},錯誤消息:${message}`)
  } catch(error) {
  // console.log(error)
  }
 }

 // 服務端,客戶端統(tǒng)一reject錯誤對象,因此頁面組件asyncData,fetch函數請求api接口一定要做catch處理
 return Promise.reject(error)
 })
}

3、組件緩存

vue官網文檔原話:如果 renderer 在組件渲染過程中進行緩存命中,那么它將直接重新使用整個子樹的緩存結果。這意味著在以下情況,你不應該緩存組件:

  • 它具有可能依賴于全局狀態(tài)的子組件。

  • 它具有對渲染上下文產生副作用(side effect)的子組件。

因此,應該小心使用組件緩存來解決性能瓶頸。在大多數情況下,你不應該也不需要緩存單一實例組件。適用于緩存的最常見類型的組件,是在大的 v-for 列表中重復出現的組件。由于這些組件通常由數據庫集合(database collection)中的對象驅動,它們可以使用簡單的緩存策略:使用其唯一 id,再加上最后更新的時間戳,來生成其緩存鍵(cache key):

serverCacheKey: props => props.item.id + '::' + props.item.last_updated

4、頁面組件asyncData函數優(yōu)化

舉一個簡單的例子進行優(yōu)化

{
 async asyncData({ $axios }) {
 // 1、增加catch處理,是為了讓服務端,客戶端運行時不報錯,特別是防止服務端運行時不報錯,不然頁面就掛了
 // 2、catch函數返回一個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有數據
 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服務端錯誤檢測,容錯處理(提高node應用程序處理容錯的能力)

首先確定使用nuxt.js框架,vue組件(頁面/非頁面組件)中以下函數都會在服務端執(zhí)行,因此代碼容錯非常重要,函數代碼執(zhí)行一旦出錯,頁面就掛了

  • fetch

  • asyncData

  • beforeCreate

  • created

1、看的見的錯誤

看的見的錯誤是指在開發(fā)環(huán)境中,你只要在fetch等以上函數中js執(zhí)行錯誤,本地就會有錯誤提示,便于你發(fā)現糾正錯誤代碼邏輯

2、未知/看不見的錯誤(讓未知錯誤暴露出來)

看不見的錯誤是指一些異步回調中的錯誤代碼不容易被發(fā)現,如果異步行為一直沒有觸發(fā),那么處理該異步行為的回調代碼也不會執(zhí)行;但是對于處理所有頁面的api接口請求回調的錯誤排查(主要是做容錯處理,使代碼更加健壯,java接口請求404、接口數據字段/結構的處理)我們能夠做好,很簡單,我們只需要在請求攔截器中把請求url更改就可以

$axios.interceptors.request.use(config => {
 // TODO
 // 檢測由于請求java接口失敗而導致的node應用程序錯誤
 config.url += '/xxxx'

 return config
})

3、對于頁面刷新加載不需要渲染的數據的處理

只有頁面組件asyncData(函數返回的對象跟組件data融合),fetch(更新store操作)函數處理的數據跟頁面綁定后,頁面刷新加載服務端才會渲染;因此不建議組件在beforeCreate,created函數中通過請求api接口獲取頁面刷新加載不需要渲染的數據,只需要在mounted函數中處理即可,防止由于代碼錯誤導致node應用程序出錯。

以上是“基于Nuxt.js項目服務端性能優(yōu)化與錯誤檢測的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI