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