nuxt緩存實踐

nuxt是基於vuessr解決方案,能夠是使用vue語法完成先後端的同構。javascript

然而在與傳統純字符串拼接的ssr方案相比,性能就沒那麼好了,nuxt須要在服務端生成虛擬dom,而後再序列化出HTML字符串,咱們常說nodejs的高性能指的是異步IO操做頻繁的場景而非CPU操做密集的場景,畢竟nodejs是運行在單線程下的,在涉及到高併發的場景下,性能就會有所降低,能夠考慮採用合理的緩存策略html

nuxt的緩存能夠分爲組件級別緩存, API級別緩存以及頁面級別緩存vue

組件級別的緩存

配置項nuxt.config.js的配置大概長這樣子:java

const LRU = require('lru-cache')
module.exports = {
  render: {
    bundleRenderer: {
      cache: LRU({
        max: 1000,                     // 最大的緩存個數
        maxAge: 1000 * 60 * 15        // 緩存15分鐘
      })
    }
  }
}
複製代碼

並非說配了該項就實現了組件級別的緩存,還須要在需作緩存的vue組件上增長name以及serverCacheKey字段,以肯定緩存的惟一鍵值,好比:node

export default {
  name: 'AppHeader',
  props: ['type'],
  serverCacheKey: props => props.type
}
複製代碼

上述組件會根據父組件傳下來的type值去作緩存,鍵值是:AppHeader::${props.type},由此,新的請求到來時,只要父組件傳下來的type屬性以前處理過,就能夠複用以前的渲染緩存結果,以增進性能ios

從該例子能夠看出,若是該組件除了依賴父組件的type屬性,還依賴於別的屬性,serverCacheKey這裏也要作出相應的改變,所以,若是組件依賴於不少的全局狀態,或者,依賴的狀態取值很是多,意味須要緩存會被頻繁被設置而致使溢出,其實就沒有多大意義了,在lru-cache的配置中,設置的最大緩存個數是1000,超出部分就會被清掉git

其次,不該該緩存可能對渲染上下文產生反作用的子組件,好比,組件的createdbeforeCreated的鉤子在服務端也會走,組件被緩存後就不會執行了,這些可能影響到渲染上下文的地方也要當心,更多內容請參考:組件級別緩存github

通常來講,比較適合的場景是v-for大量數據的渲染,由於循環操做比較耗cpuaxios

API級別的緩存

在服務端渲染的場景中,每每會將請求放在服務端去作,渲染完頁面再返回給瀏覽器,而有些接口是能夠去作緩存的,好比,不依賴登陸態且不依賴過多參數的接口或者是單純獲取配置數據的接口等,接口的處理也是須要時間的,對接口的緩存能夠加快每一個請求的處理速度,更快地釋放掉請求,從而增進性能後端

api的請求使用axiosaxios便可以在服務端使用也但是在瀏覽器使用,代碼大概長這樣子

import axios from 'axios'
import md5 from 'md5'
import LRU from 'lru-cache'

// 給api加3秒緩存
const CACHED = LRU({
  max: 1000,
  maxAge: 1000 * 3
})

function request (config) {
  let key
  // 服務端才加緩存,瀏覽器端就無論了
  if (config.cache && !process.browser) {
    const { params = {}, data = {} } = config
    key = md5(config.url + JSON.stringify(params) + JSON.stringify(data))
    if (CACHED.has(key)) {
      // 緩存命中
      return Promise.resolve(CACHED.get(key))
    }
  }
  return axios(config)
    .then(rsp => {
      if (config.cache && !process.browser) {
        // 返回結果前先設置緩存
        CACHED.set(key, rsp.data)
      }
      return rsp.data
    })
}
複製代碼

使用上跟平時使用axios仍是同樣的,就多加了個cache的屬性標識是否須要在服務端作緩存

const api = {
  getGames: params => request({
    url: '/gameInfo/gatGames',
    params,
    cache: true
  })
}
複製代碼

頁面級別的緩存

在不依賴於登陸態以及過多參數的狀況下,若是併發量很大,能夠考慮使用頁面級別的緩存, 在nuxt.config.js增長serverMiddleware屬性

const nuxtPageCache = require('nuxt-page-cache')

module.exports = {
  serverMiddleware: [
    nuxtPageCache.cacheSeconds(1, req => {
      if (req.query && req.query.pageType) {
        return req.query.pageType
      }
      return false
    })
  ]
}
複製代碼

上面的例子根據連接後面的參數pageType去作緩存,若是連接後面有pageType參數,就作緩存,緩存時間爲1秒,也就是說在1秒內相同的pageType請求,服務端只會執行一次完整的渲染

nuxt-page-cache參考了route-cache,寫得比較簡陋,你也能夠從新封裝下,nuxt最終返回數據是使用res.end(html, 'utf-8'),頁面級別的緩存大概的思想以下:

const LRU = require('lru-cache')

let cacheStore = new LRU({
  max: 100,         // 設置最大的緩存個數
  maxAge: 200
})

module.exports.cacheSeconds = function (secondsTTL, cacheKey) {
  // 設置緩存的時間
  const ttl = secondsTTL * 1000
  return function (req, res, next) {
    // 獲取緩存的key值
    let key = req.originalUrl
    if (typeof cacheKey === 'function') {
      key = cacheKey(req, res)
      if (!key) { return next() }
    } else if (typeof cacheKey === 'string') {
      key = cacheKey
    }

    // 若是緩存命中,直接返回
    const value = cacheStore.get(key)
    if (value) {
      return res.end(value, 'utf-8')
    }

    // 緩存原先的end方案
    res.original_end = res.end

    // 重寫res.end方案,由此nuxt調用res.end其實是調用該方法,
    res.end = function () {
      if (res.statusCode === 200) {
        // 設置緩存
        cacheStore.set(key, data, ttl)
      }
      // 最終返回結果
      res.original_end(data, 'utf-8')
    }
  }
}
複製代碼

若是緩存命中,直接將原先的計算結果返回,大大提供了性能

總結

在高併發的狀況下能夠考慮使用緩存,而緩存策略的使用須要視場景而定,這裏再也不贅述,還能夠考慮使用pm2開啓集羣模式去管理咱們的進程,從而知足更高的併發。

相關文章
相關標籤/搜索