Skip to content

Commit 05b070f

Browse files
committed
refactor ssr render context into separate class
1 parent e02fb12 commit 05b070f

File tree

2 files changed

+114
-68
lines changed

2 files changed

+114
-68
lines changed

src/server/render-context.js

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

src/server/render.js

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* @flow */
22

33
import { escape } from 'he'
4+
import { RenderContext } from './render-context'
45
import { compileToFunctions } from 'web/compiler/index'
56
import { createComponentInstanceForVnode } from 'core/vdom/create-component'
6-
import { noop } from 'shared/util'
77

88
let warned = Object.create(null)
99
const warnOnce = msg => {
@@ -13,17 +13,6 @@ const warnOnce = msg => {
1313
}
1414
}
1515

16-
const normalizeAsync = (cache, method) => {
17-
const fn = cache[method]
18-
if (!fn) {
19-
return
20-
} else if (fn.length > 1) {
21-
return (key, cb) => fn.call(cache, key, cb)
22-
} else {
23-
return (key, cb) => cb(fn.call(cache, key))
24-
}
25-
}
26-
2716
const compilationCache = Object.create(null)
2817
const normalizeRender = vm => {
2918
const { render, template } = vm.$options
@@ -202,76 +191,24 @@ function renderStartingTag (node: VNode, context) {
202191
return markup + '>'
203192
}
204193

205-
const nextFactory = context => function next () {
206-
const lastState = context.renderStates.pop()
207-
if (!lastState) {
208-
context.done()
209-
// cleanup context, avoid leakage
210-
context = (null: any)
211-
return
212-
}
213-
switch (lastState.type) {
214-
case 'Component':
215-
context.activeInstance = lastState.prevActive
216-
next()
217-
break
218-
case 'Element':
219-
const { children, total } = lastState
220-
const rendered = lastState.rendered++
221-
if (rendered < total) {
222-
context.renderStates.push(lastState)
223-
renderNode(children[rendered], false, context)
224-
} else {
225-
context.write(lastState.endTag, next)
226-
}
227-
break
228-
case 'ComponentWithCache':
229-
const { buffer, bufferIndex, key } = lastState
230-
const result = buffer[bufferIndex]
231-
context.cache.set(key, result)
232-
if (bufferIndex === 0) {
233-
// this is a top-level cached component,
234-
// exit caching mode.
235-
context.write.caching = false
236-
} else {
237-
// parent component is also being cached,
238-
// merge self into parent's result
239-
buffer[bufferIndex - 1] += result
240-
}
241-
buffer.length = bufferIndex
242-
next()
243-
break
244-
}
245-
}
246-
247194
export function createRenderFunction (
248195
modules: Array<Function>,
249196
directives: Object,
250197
isUnaryTag: Function,
251198
cache: any
252199
) {
253-
if (cache && (!cache.get || !cache.set)) {
254-
throw new Error('renderer cache must implement at least get & set.')
255-
}
256-
257-
const get = cache && normalizeAsync(cache, 'get')
258-
const has = cache && normalizeAsync(cache, 'has')
259-
260200
return function render (
261201
component: Component,
262202
write: (text: string, next: Function) => void,
263203
done: Function
264204
) {
265205
warned = Object.create(null)
266-
const context = {
206+
const context = new RenderContext({
267207
activeInstance: component,
268-
renderStates: [],
269-
next: noop, // for flow
270-
write, done,
208+
write, done, renderNode,
271209
isUnaryTag, modules, directives,
272-
cache, get, has
273-
}
274-
context.next = nextFactory(context)
210+
cache
211+
})
275212
normalizeRender(component)
276213
renderNode(component._render(), true, context)
277214
}

0 commit comments

Comments
 (0)