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