ec4f055ab7d022622e894142c874903c3d46f0640a99ab317e4dfa73c867d87cd7cbdb658b6d01455915b0a442efe6d53af2843600d894579fb22c78e6a6f4 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /* @flow */
  2. import { isUndef } from 'shared/util'
  3. type RenderState = {
  4. type: 'Element';
  5. rendered: number;
  6. total: number;
  7. children: Array<VNode>;
  8. endTag: string;
  9. } | {
  10. type: 'Fragment';
  11. rendered: number;
  12. total: number;
  13. children: Array<VNode>;
  14. } | {
  15. type: 'Component';
  16. prevActive: Component;
  17. } | {
  18. type: 'ComponentWithCache';
  19. buffer: Array<string>;
  20. bufferIndex: number;
  21. componentBuffer: Array<Set<Class<Component>>>;
  22. key: string;
  23. };
  24. export class RenderContext {
  25. userContext: ?Object;
  26. activeInstance: Component;
  27. renderStates: Array<RenderState>;
  28. write: (text: string, next: Function) => void;
  29. renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void;
  30. next: () => void;
  31. done: (err: ?Error) => void;
  32. modules: Array<(node: VNode) => ?string>;
  33. directives: Object;
  34. isUnaryTag: (tag: string) => boolean;
  35. cache: any;
  36. get: ?(key: string, cb: Function) => void;
  37. has: ?(key: string, cb: Function) => void;
  38. constructor (options: Object) {
  39. this.userContext = options.userContext
  40. this.activeInstance = options.activeInstance
  41. this.renderStates = []
  42. this.write = options.write
  43. this.done = options.done
  44. this.renderNode = options.renderNode
  45. this.isUnaryTag = options.isUnaryTag
  46. this.modules = options.modules
  47. this.directives = options.directives
  48. const cache = options.cache
  49. if (cache && (!cache.get || !cache.set)) {
  50. throw new Error('renderer cache must implement at least get & set.')
  51. }
  52. this.cache = cache
  53. this.get = cache && normalizeAsync(cache, 'get')
  54. this.has = cache && normalizeAsync(cache, 'has')
  55. this.next = this.next.bind(this)
  56. }
  57. next () {
  58. // eslint-disable-next-line
  59. while (true) {
  60. const lastState = this.renderStates[this.renderStates.length - 1]
  61. if (isUndef(lastState)) {
  62. return this.done()
  63. }
  64. /* eslint-disable no-case-declarations */
  65. switch (lastState.type) {
  66. case 'Element':
  67. case 'Fragment':
  68. const { children, total } = lastState
  69. const rendered = lastState.rendered++
  70. if (rendered < total) {
  71. return this.renderNode(children[rendered], false, this)
  72. } else {
  73. this.renderStates.pop()
  74. if (lastState.type === 'Element') {
  75. return this.write(lastState.endTag, this.next)
  76. }
  77. }
  78. break
  79. case 'Component':
  80. this.renderStates.pop()
  81. this.activeInstance = lastState.prevActive
  82. break
  83. case 'ComponentWithCache':
  84. this.renderStates.pop()
  85. const { buffer, bufferIndex, componentBuffer, key } = lastState
  86. const result = {
  87. html: buffer[bufferIndex],
  88. components: componentBuffer[bufferIndex]
  89. }
  90. this.cache.set(key, result)
  91. if (bufferIndex === 0) {
  92. // this is a top-level cached component,
  93. // exit caching mode.
  94. this.write.caching = false
  95. } else {
  96. // parent component is also being cached,
  97. // merge self into parent's result
  98. buffer[bufferIndex - 1] += result.html
  99. const prev = componentBuffer[bufferIndex - 1]
  100. result.components.forEach(c => prev.add(c))
  101. }
  102. buffer.length = bufferIndex
  103. componentBuffer.length = bufferIndex
  104. break
  105. }
  106. }
  107. }
  108. }
  109. function normalizeAsync (cache, method) {
  110. const fn = cache[method]
  111. if (isUndef(fn)) {
  112. return
  113. } else if (fn.length > 1) {
  114. return (key, cb) => fn.call(cache, key, cb)
  115. } else {
  116. return (key, cb) => cb(fn.call(cache, key))
  117. }
  118. }