Skip to content

Commit 4e40666

Browse files
committed
wip
1 parent 37fb988 commit 4e40666

File tree

8 files changed

+143
-18
lines changed

8 files changed

+143
-18
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"karma-sourcemap-loader": "^0.3.0",
9494
"karma-webpack": "^2.0.1",
9595
"lodash": "^4.17.1",
96+
"lru-cache": "^4.0.2",
9697
"nightwatch": "^0.9.9",
9798
"nightwatch-helpers": "^1.2.0",
9899
"phantomjs-prebuilt": "^2.1.1",

src/server/create-renderer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function createRenderer ({
6363
return false
6464
}, done)
6565
try {
66-
render(component, write, () => {
66+
render(component, write, context, () => {
6767
if (template) {
6868
result = templateRenderer.renderSync(result, context)
6969
}
@@ -79,7 +79,7 @@ export function createRenderer ({
7979
context?: ?Object
8080
): stream$Readable {
8181
const renderStream = new RenderStream((write, done) => {
82-
render(component, write, done)
82+
render(component, write, context, done)
8383
})
8484
if (!template) {
8585
if (context && clientManifest) {

src/server/render-context.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ type RenderState = {
1515
type: 'ComponentWithCache';
1616
buffer: Array<string>;
1717
bufferIndex: number;
18+
componentBuffer: Array<Set<Class<Component>>>;
1819
key: string;
1920
};
2021

2122
export class RenderContext {
23+
userContext: ?Object;
2224
activeInstance: Component;
2325
renderStates: Array<RenderState>;
2426
write: (text: string, next: Function) => void;
@@ -35,6 +37,7 @@ export class RenderContext {
3537
has: ?(key: string, cb: Function) => void;
3638

3739
constructor (options: Object) {
40+
this.userContext = options.userContext
3841
this.activeInstance = options.activeInstance
3942
this.renderStates = []
4043

@@ -80,8 +83,11 @@ export class RenderContext {
8083
break
8184
case 'ComponentWithCache':
8285
this.renderStates.pop()
83-
const { buffer, bufferIndex, key } = lastState
84-
const result = buffer[bufferIndex]
86+
const { buffer, bufferIndex, componentBuffer, key } = lastState
87+
const result = {
88+
html: buffer[bufferIndex],
89+
components: componentBuffer[bufferIndex]
90+
}
8591
this.cache.set(key, result)
8692
if (bufferIndex === 0) {
8793
// this is a top-level cached component,
@@ -90,9 +96,12 @@ export class RenderContext {
9096
} else {
9197
// parent component is also being cached,
9298
// merge self into parent's result
93-
buffer[bufferIndex - 1] += result
99+
buffer[bufferIndex - 1] += result.html
100+
const prev = componentBuffer[bufferIndex - 1]
101+
result.components.forEach(c => prev.add(c))
94102
}
95103
buffer.length = bufferIndex
104+
componentBuffer.length = bufferIndex
96105
this.next()
97106
break
98107
}

src/server/render.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,20 @@ const normalizeRender = vm => {
3737
}
3838

3939
function renderNode (node, isRoot, context) {
40-
const { write, next } = context
40+
const { write, next, userContext } = context
4141
if (isDef(node.componentOptions)) {
4242
// check cache hit
4343
const Ctor = node.componentOptions.Ctor
4444
const getKey = Ctor.options.serverCacheKey
4545
const name = Ctor.options.name
46+
4647
// exposed by vue-loader, need to call this if cache hit because
4748
// component lifecycle hooks will not be called.
48-
const injectStyles = Ctor.options._injectStyles
49+
const registerComponent = Ctor.options._ssrRegister
50+
if (write.caching && isDef(registerComponent)) {
51+
write.componentBuffer[write.componentBuffer.length - 1].add(registerComponent)
52+
}
53+
4954
const cache = context.cache
5055
if (isDef(getKey) && isDef(cache) && isDef(name)) {
5156
const key = name + '::' + getKey(node.componentOptions.propsData)
@@ -54,8 +59,9 @@ function renderNode (node, isRoot, context) {
5459
(has: any)(key, hit => {
5560
if (hit === true && isDef(get)) {
5661
(get: any)(key, res => {
57-
injectStyles && injectStyles.call({})
58-
write(res, next)
62+
registerComponent && registerComponent(userContext)
63+
res.components.forEach(register => register(userContext))
64+
write(res.html, next)
5965
})
6066
} else {
6167
renderComponentWithCache(node, isRoot, key, context)
@@ -64,8 +70,9 @@ function renderNode (node, isRoot, context) {
6470
} else if (isDef(get)) {
6571
(get: any)(key, res => {
6672
if (isDef(res)) {
67-
injectStyles && injectStyles.call({})
68-
write(res, next)
73+
registerComponent && registerComponent(userContext)
74+
res.components.forEach(register => register(userContext))
75+
write(res.html, next)
6976
} else {
7077
renderComponentWithCache(node, isRoot, key, context)
7178
}
@@ -101,7 +108,10 @@ function renderNode (node, isRoot, context) {
101108

102109
function renderComponent (node, isRoot, context) {
103110
const prevActive = context.activeInstance
104-
const child = context.activeInstance = createComponentInstanceForVnode(node, context.activeInstance)
111+
const child = context.activeInstance = createComponentInstanceForVnode(
112+
node,
113+
context.activeInstance
114+
)
105115
normalizeRender(child)
106116
const childNode = child._render()
107117
childNode.parent = node
@@ -117,9 +127,14 @@ function renderComponentWithCache (node, isRoot, key, context) {
117127
write.caching = true
118128
const buffer = write.cacheBuffer
119129
const bufferIndex = buffer.push('') - 1
130+
const componentBuffer = write.componentBuffer
131+
componentBuffer.push(new Set())
120132
context.renderStates.push({
121133
type: 'ComponentWithCache',
122-
buffer, bufferIndex, key
134+
key,
135+
buffer,
136+
bufferIndex,
137+
componentBuffer
123138
})
124139
renderComponent(node, isRoot, context)
125140
}
@@ -234,11 +249,13 @@ export function createRenderFunction (
234249
return function render (
235250
component: Component,
236251
write: (text: string, next: Function) => void,
252+
userContext: ?Object,
237253
done: Function
238254
) {
239255
warned = Object.create(null)
240256
const context = new RenderContext({
241257
activeInstance: component,
258+
userContext,
242259
write, done, renderNode,
243260
isUnaryTag, modules, directives,
244261
cache

src/server/write.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ export function createWriteFunction (
2828
}
2929
cachedWrite.caching = false
3030
cachedWrite.cacheBuffer = []
31+
cachedWrite.componentBuffer = []
3132
return cachedWrite
3233
}

test/ssr/fixtures/nested-cache.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import Vue from '../../../dist/vue.runtime.common.js'
2+
3+
function register (id, context) {
4+
context = context || __VUE_SSR_CONTEXT__ // eslint-disable-line
5+
context.registered.push(id)
6+
}
7+
8+
const grandchild = {
9+
name: 'grandchild',
10+
props: ['id'],
11+
_ssrRegister: context => {
12+
register('grandchild', context)
13+
},
14+
beforeCreate () {
15+
register('grandchild')
16+
},
17+
serverCacheKey: props => props.id,
18+
render (h) {
19+
return h('div', '/test')
20+
}
21+
}
22+
23+
const child = {
24+
name: 'child',
25+
props: ['id'],
26+
_ssrRegister: context => {
27+
register('child', context)
28+
},
29+
beforeCreate () {
30+
register('child')
31+
},
32+
serverCacheKey: props => props.id,
33+
render (h) {
34+
return h(grandchild, { props: { id: this.id }})
35+
}
36+
}
37+
38+
const app = {
39+
name: 'app',
40+
props: ['id'],
41+
_ssrRegister: context => {
42+
register('app', context)
43+
},
44+
beforeCreate () {
45+
register('app')
46+
},
47+
serverCacheKey: props => props.id,
48+
render (h) {
49+
return h(child, { props: { id: this.id }})
50+
}
51+
}
52+
53+
export default () => {
54+
return Promise.resolve(new Vue({
55+
render: h => h(app, { props: { id: 1 }})
56+
}))
57+
}

test/ssr/ssr-bundle-render.spec.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import LRU from 'lru-cache'
12
import { VueSSRServerPlugin } from 'vue-ssr-webpack-plugin'
23
import { compileWithWebpack } from './compile-with-webpack'
34
import { createBundleRenderer } from '../../packages/vue-server-renderer'
@@ -105,8 +106,10 @@ describe('SSR: bundle renderer', () => {
105106
expect(err).toBeNull()
106107
expect(res).toBe(expected)
107108
expect(get).toHaveBeenCalledWith(key)
108-
expect(set).toHaveBeenCalledWith(key, expected)
109-
expect(cache[key]).toBe(expected)
109+
const setArgs = set.calls.argsFor(0)
110+
expect(setArgs[0]).toBe(key)
111+
expect(setArgs[1].html).toBe(expected)
112+
expect(cache[key].html).toBe(expected)
110113
renderer.renderToString((err, res) => {
111114
expect(err).toBeNull()
112115
expect(res).toBe(expected)
@@ -149,8 +152,10 @@ describe('SSR: bundle renderer', () => {
149152
expect(res).toBe(expected)
150153
expect(has).toHaveBeenCalledWith(key)
151154
expect(get).not.toHaveBeenCalled()
152-
expect(set).toHaveBeenCalledWith(key, expected)
153-
expect(cache[key]).toBe(expected)
155+
const setArgs = set.calls.argsFor(0)
156+
expect(setArgs[0]).toBe(key)
157+
expect(setArgs[1].html).toBe(expected)
158+
expect(cache[key].html).toBe(expected)
154159
renderer.renderToString((err, res) => {
155160
expect(err).toBeNull()
156161
expect(res).toBe(expected)
@@ -163,6 +168,41 @@ describe('SSR: bundle renderer', () => {
163168
})
164169
})
165170

171+
it('render with cache (nested)', done => {
172+
const cache = LRU({ maxAge: Infinity })
173+
spyOn(cache, 'get').and.callThrough()
174+
spyOn(cache, 'set').and.callThrough()
175+
const options = { cache }
176+
createRenderer('nested-cache.js', options, renderer => {
177+
const expected = '<div data-server-rendered="true">/test</div>'
178+
const key = 'app::1'
179+
const context1 = { registered: [] }
180+
const context2 = { registered: [] }
181+
renderer.renderToString(context1, (err, res) => {
182+
expect(err).toBeNull()
183+
expect(res).toBe(expected)
184+
expect(cache.set.calls.count()).toBe(3) // 3 nested components cached
185+
const cached = cache.get(key)
186+
expect(cached.html).toBe(expected)
187+
expect(cache.get.calls.count()).toBe(1)
188+
189+
// assert component usage registration for nested children
190+
expect(context1.registered).toEqual(['app', 'child', 'grandchild'])
191+
192+
renderer.renderToString(context2, (err, res) => {
193+
expect(err).toBeNull()
194+
expect(res).toBe(expected)
195+
expect(cache.set.calls.count()).toBe(3) // no new cache sets
196+
expect(cache.get.calls.count()).toBe(2) // 1 get for root
197+
198+
console.log(context1)
199+
console.log(context2)
200+
done()
201+
})
202+
})
203+
})
204+
})
205+
166206
it('renderToString (bundle format with code split)', done => {
167207
createRenderer('split.js', { asBundle: true }, renderer => {
168208
const context = { url: '/test' }

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3297,7 +3297,7 @@ lru-cache@2.2.x:
32973297
version "2.2.4"
32983298
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
32993299

3300-
lru-cache@^4.0.1:
3300+
lru-cache@^4.0.1, lru-cache@^4.0.2:
33013301
version "4.0.2"
33023302
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e"
33033303
dependencies:

0 commit comments

Comments
 (0)