Setup cache of generated HTML is an easy way to promote SSR server’s performance. To effectively use cache, app needs a way to communicate about the availability of cache, Vise uses CacheInfo.

Generate Unique Cache Key

Generate CacheInfo in beforeUseCache hook

There are multiple SSR cache related hooks in tapable-hooks:

  • beforeUseCache: Called before server trying to find cache. Generate CacheInfo, which could be used to update and reuse cached html, contains unique cache key and expire date based on the HTTP request.
  • findCache: Called when server trying to find usable cache with CacheInfo.
  • hitCache: Called when cache is hit. Mainly for statistic usage.
  • afterRender: page render successful, update cache with new HTML and CacheInfo in the context

Generate unique cache key with beforeUseCache

const serverHooks: ViseHooks = {
  // ...
  beforeUseCache: async (renderRequest) => {
    const { url, headers } = renderRequest.request;
    const userAgent = headers['user-agent'] || '';

    // suppose generated html depends on browser is chrome or not
    const browser = userAgent.indexOf('Chrome') > -1 ? 'chrome' : 'other';

    return {
      key: `${browser}_${url}`,
      expire: Date.now() + 3600 * 1000,
      stale: true,
      context: renderRequest,
    };
  },
  // ...
};

beforeUseCache receives an HTTPRequest parameter,including url and headers of the request. In the example code, cache key a generated from user-agent in the header and url of the request, that means as long as these 2 information do not change, the resulting HTML will be the same.

Typical elements affecting SSR HTML cache

Page URL

Different pages with different URL have different content.

User agent in header

If generated HTML depends on the kind or version of user’s browser (aka user agent), the cache key should include this information. Please be noted using the whole user agent as cache key is NOT advised. Only the feature the HTML relies on should be used, such as ‘iOS12+’ or ‘android8+’, or the cache will never be reused because of it’s so special.

Generally cookies are used to identify login users. If different people see different contents, and they falls in certain categories, the this information could be used as cache key. But if everyone sees his/her own content, then no cache could be used, you may want to move that module into the CSR generation.

Prevent Cache Error

In Server render process, errors like API timeout may happen. In such scenarios, the generate page will be pages with error or empty contents, CSR fallback or error handling will be needed.

Such pages should be prevented from being cached. Or an incidental error could affect thousands of people instead of one. Vise provide RenderContext.meta.cache switch to turn off cache for certain render.

// comePageOrComponent.vue
export default defineComponent({
  setup() {
    const ssrContext = useSSRContext();
    //... fetch & init the page, some error happens
    ssrContext.meta.cache = false;
  }
});

Except setting it in the UI libraries, other hooks with access to RenderContext can also set RenderContext.meta.cache to false to disable SSR cache.

Header and URL management suggestion

In a app, pages of UI libraries may read URL information from router, header information from SSRContext. The generation of CacheInfo logic is in server-hooks.ts. If manage poorly, the following may happen:

  • One page read URL or header, generate certain HTML, without added that dependency in the logic of beforeUseCache
  • Server using the generated CacheInfo saved the page
  • Next user send a request with different headers or URL, beforeUseCache return the same cache key, which hit the previous cache
  • In user’s browser, UI libraries’ Client Side hydration will fail, hydration mismatch detected, ant the whole page re-render.

So, it is vital to strictly manage the use of URL and header information in UI libraries, and make sure all such information affects the generation of HTML must be added to the CacheInfo generated by beforeUseCache.

Cache implementation in the Server

Different kind of cache such as Redis, databases, file cache could all be used with Vise. Generally findCache and afterRender hooks are where real cache operations happen. A Hook-plugin may wrap several hooks together to provide a cache implementation.