ssrComputed.spec.ts 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { computed, createSSRApp, defineComponent, h, reactive, ref } from 'vue'
  2. import { renderToString } from '../src/renderToString'
  3. // #5208 reported memory leak of keeping computed alive during SSR
  4. // so we made computed properties created during SSR non-reactive in
  5. // https://github.com/vuejs/core/commit/f4f0966b33863ac0fca6a20cf9e8ddfbb311ae87
  6. // However, the default caching leads to #5300 which is tested below.
  7. // In Vue 2, computed properties are simple getters during SSR - this can be
  8. // inefficient if an expensive computed is accessed multiple times during render,
  9. // but because of potential mutations, we cannot cache it until we enter the
  10. // render phase (where no mutations can happen anymore)
  11. test('computed reactivity during SSR', async () => {
  12. const store = {
  13. // initial state could be hydrated
  14. state: reactive({ items: null }) as any,
  15. // pretend to fetch some data from an api
  16. async fetchData() {
  17. this.state.items = ['hello', 'world']
  18. },
  19. }
  20. const getterSpy = vi.fn()
  21. const App = defineComponent(async () => {
  22. const msg = computed(() => {
  23. getterSpy()
  24. return store.state.items?.join(' ')
  25. })
  26. // If msg value is falsy then we are either in ssr context or on the client
  27. // and the initial state was not modified/hydrated.
  28. // In both cases we need to fetch data.
  29. if (!msg.value) await store.fetchData()
  30. return () => h('div', null, msg.value + msg.value + msg.value)
  31. })
  32. const app = createSSRApp(App)
  33. const html = await renderToString(app)
  34. expect(html).toMatch('hello world')
  35. // should only be called twice since access should be cached
  36. // during the render phase
  37. expect(getterSpy).toHaveBeenCalledTimes(2)
  38. })
  39. // although we technically shouldn't allow state mutation during render,
  40. // it does sometimes happen
  41. test('computed mutation during render', async () => {
  42. const App = defineComponent(async () => {
  43. const n = ref(0)
  44. const m = computed(() => n.value + 1)
  45. m.value // force non-dirty
  46. return () => {
  47. n.value++
  48. return h('div', null, `value: ${m.value}`)
  49. }
  50. })
  51. const app = createSSRApp(App)
  52. const html = await renderToString(app)
  53. expect(html).toMatch('value: 2')
  54. })