Skip to content

Commit fc66cfb

Browse files
Akryumpikax
authored andcommitted
feat: add onServerPrefetch (vuejs#198)
* feat: add `onServerPrefetch` * test: onServerPrefetch * docs: note about SSR * test: ssrContext * feat: ssrContext in setup context * test: improved shared context test
1 parent 3433caa commit fc66cfb

File tree

6 files changed

+238
-2
lines changed

6 files changed

+238
-2
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,25 @@ declare module '@vue/composition-api/dist/component/component' {
311311
}
312312
}
313313
```
314+
315+
## SSR
316+
317+
Even if there is no definitive Vue 3 API for SSR yet, this plugin implements the `onServerPrefetch` lifecycle hook that allows you to use the `serverPrefetch` hook found in the classic API.
318+
319+
```js
320+
import { onServerPrefetch } from '@vue/composition-api';
321+
322+
export default {
323+
setup (props, { ssrContext }) {
324+
const result = ref();
325+
326+
onServerPrefetch(async () => {
327+
result.value = await callApi(ssrContext.someId);
328+
});
329+
330+
return {
331+
result,
332+
};
333+
},
334+
};
335+
```

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"ts-jest": "^24.0.2",
4747
"typescript": "^3.6.2",
4848
"vue": "^2.5.22",
49-
"vue-router": "^3.1.3"
49+
"vue-router": "^3.1.3",
50+
"vue-server-renderer": "^2.6.10"
5051
},
5152
"peerDependencies": {
5253
"vue": "^2.5.22"

src/apis/lifecycle.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ export const onUnmounted = createLifeCycles(['destroyed', 'deactivated'], genNam
3838
export const onErrorCaptured = createLifeCycle('errorCaptured');
3939
export const onActivated = createLifeCycle('activated');
4040
export const onDeactivated = createLifeCycle('deactivated');
41+
export const onServerPrefetch = createLifeCycle('serverPrefetch');

src/setup.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,14 @@ export function mixin(Vue: VueConstructor) {
216216
const ctx = {
217217
slots: {},
218218
} as SetupContext;
219-
const props: Array<string | [string, string]> = ['root', 'parent', 'refs', 'attrs', 'listeners'];
219+
const props: Array<string | [string, string]> = [
220+
'root',
221+
'parent',
222+
'refs',
223+
'attrs',
224+
'listeners',
225+
'ssrContext',
226+
];
220227
const methodReturnVoid = ['emit'];
221228
props.forEach(key => {
222229
let targetKey: string;

test/ssr/serverPrefetch.spec.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
const Vue = require('vue/dist/vue.common.js');
2+
const { createRenderer } = require('vue-server-renderer');
3+
const { ref, onServerPrefetch, getCurrentInstance, provide, inject } = require('../../src');
4+
5+
function fetch(result) {
6+
return new Promise(resolve => {
7+
setTimeout(() => {
8+
resolve(result);
9+
}, 10);
10+
});
11+
}
12+
13+
describe('serverPrefetch', () => {
14+
it('should prefetch async operations before rendering', async () => {
15+
const app = new Vue({
16+
setup() {
17+
const count = ref(0);
18+
19+
onServerPrefetch(async () => {
20+
count.value = await fetch(42);
21+
});
22+
23+
return {
24+
count,
25+
};
26+
},
27+
render(h) {
28+
return h('div', this.count);
29+
},
30+
});
31+
32+
const serverRenderer = createRenderer();
33+
const html = await serverRenderer.renderToString(app);
34+
expect(html).toBe('<div data-server-rendered="true">42</div>');
35+
});
36+
37+
it('should prefetch many async operations before rendering', async () => {
38+
const app = new Vue({
39+
setup() {
40+
const count = ref(0);
41+
const label = ref('');
42+
43+
onServerPrefetch(async () => {
44+
count.value = await fetch(42);
45+
});
46+
47+
onServerPrefetch(async () => {
48+
label.value = await fetch('meow');
49+
});
50+
51+
return {
52+
count,
53+
label,
54+
};
55+
},
56+
render(h) {
57+
return h('div', [this.count, this.label]);
58+
},
59+
});
60+
61+
const serverRenderer = createRenderer();
62+
const html = await serverRenderer.renderToString(app);
63+
expect(html).toBe('<div data-server-rendered="true">42meow</div>');
64+
});
65+
66+
it('should pass ssrContext', async () => {
67+
const child = {
68+
setup(props, { ssrContext }) {
69+
const content = ref();
70+
71+
expect(ssrContext.foo).toBe('bar');
72+
73+
onServerPrefetch(async () => {
74+
content.value = await fetch(ssrContext.foo);
75+
});
76+
77+
return {
78+
content,
79+
};
80+
},
81+
render(h) {
82+
return h('div', this.content);
83+
},
84+
};
85+
86+
const app = new Vue({
87+
components: {
88+
child,
89+
},
90+
render(h) {
91+
return h('child');
92+
},
93+
});
94+
95+
const serverRenderer = createRenderer();
96+
const html = await serverRenderer.renderToString(app, { foo: 'bar' });
97+
expect(html).toBe('<div data-server-rendered="true">bar</div>');
98+
});
99+
100+
it('should not share context', async () => {
101+
const instances = [];
102+
function createApp(context) {
103+
return new Vue({
104+
setup() {
105+
const count = ref(0);
106+
107+
onServerPrefetch(async () => {
108+
count.value = await fetch(context.result);
109+
});
110+
111+
instances.push(getCurrentInstance());
112+
113+
return {
114+
count,
115+
};
116+
},
117+
render(h) {
118+
return h('div', this.count);
119+
},
120+
});
121+
}
122+
123+
const serverRenderer = createRenderer();
124+
const promises = [];
125+
// Parallel requests
126+
for (let i = 1; i < 3; i++) {
127+
promises.push(
128+
new Promise(async resolve => {
129+
const app = createApp({ result: i });
130+
const html = await serverRenderer.renderToString(app);
131+
expect(html).toBe(`<div data-server-rendered="true">${i}</div>`);
132+
resolve();
133+
})
134+
);
135+
}
136+
await Promise.all(promises);
137+
expect((instances[0] === instances[1]) === instances[2]).toBe(false);
138+
});
139+
});

yarn.lock

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,16 @@ has@^1.0.1, has@^1.0.3:
15831583
dependencies:
15841584
function-bind "^1.1.1"
15851585

1586+
hash-sum@^1.0.2:
1587+
version "1.0.2"
1588+
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
1589+
integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
1590+
1591+
he@^1.1.0:
1592+
version "1.2.0"
1593+
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
1594+
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
1595+
15861596
hosted-git-info@^2.1.4:
15871597
version "2.7.1"
15881598
resolved "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
@@ -2579,11 +2589,36 @@ locate-path@^5.0.0:
25792589
dependencies:
25802590
p-locate "^4.1.0"
25812591

2592+
lodash._reinterpolate@^3.0.0:
2593+
version "3.0.0"
2594+
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
2595+
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
2596+
25822597
lodash.sortby@^4.7.0:
25832598
version "4.7.0"
25842599
resolved "https://registry.npm.taobao.org/lodash.sortby/download/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
25852600
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
25862601

2602+
lodash.template@^4.4.0:
2603+
version "4.5.0"
2604+
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
2605+
integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
2606+
dependencies:
2607+
lodash._reinterpolate "^3.0.0"
2608+
lodash.templatesettings "^4.0.0"
2609+
2610+
lodash.templatesettings@^4.0.0:
2611+
version "4.2.0"
2612+
resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
2613+
integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
2614+
dependencies:
2615+
lodash._reinterpolate "^3.0.0"
2616+
2617+
lodash.uniq@^4.5.0:
2618+
version "4.5.0"
2619+
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
2620+
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
2621+
25872622
lodash@^4.17.11:
25882623
version "4.17.11"
25892624
resolved "https://registry.npm.taobao.org/lodash/download/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
@@ -3506,6 +3541,13 @@ resolve@^1.10.0, resolve@^1.11.0, resolve@^1.3.2:
35063541
dependencies:
35073542
path-parse "^1.0.6"
35083543

3544+
resolve@^1.2.0:
3545+
version "1.13.1"
3546+
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16"
3547+
integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==
3548+
dependencies:
3549+
path-parse "^1.0.6"
3550+
35093551
restore-cursor@^2.0.0:
35103552
version "2.0.0"
35113553
resolved "https://registry.npm.taobao.org/restore-cursor/download/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -3658,6 +3700,11 @@ semver@^6.0.0:
36583700
resolved "https://registry.npm.taobao.org/semver/download/semver-6.1.1.tgz?cache=0&sync_timestamp=1559063729249&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
36593701
integrity sha1-U/U9qbMLIQPNTxXqs6GOy8shDJs=
36603702

3703+
serialize-javascript@^1.3.0:
3704+
version "1.9.1"
3705+
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
3706+
integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
3707+
36613708
serialize-javascript@^1.6.1:
36623709
version "1.7.0"
36633710
resolved "https://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65"
@@ -3791,6 +3838,11 @@ source-map-url@^0.4.0:
37913838
resolved "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
37923839
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
37933840

3841+
3842+
version "0.5.6"
3843+
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
3844+
integrity sha1-dc449SvwczxafwwRjYEzSiu19BI=
3845+
37943846
source-map@^0.5.0, source-map@^0.5.6:
37953847
version "0.5.7"
37963848
resolved "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -4245,6 +4297,20 @@ vue-router@^3.1.3:
42454297
resolved "https://registry.npm.taobao.org/vue-router/download/vue-router-3.1.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-3.1.3.tgz#e6b14fabc0c0ee9fda0e2cbbda74b350e28e412b"
42464298
integrity sha1-5rFPq8DA7p/aDiy72nSzUOKOQSs=
42474299

4300+
vue-server-renderer@^2.6.10:
4301+
version "2.6.10"
4302+
resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375"
4303+
integrity sha512-UYoCEutBpKzL2fKCwx8zlRtRtwxbPZXKTqbl2iIF4yRZUNO/ovrHyDAJDljft0kd+K0tZhN53XRHkgvCZoIhug==
4304+
dependencies:
4305+
chalk "^1.1.3"
4306+
hash-sum "^1.0.2"
4307+
he "^1.1.0"
4308+
lodash.template "^4.4.0"
4309+
lodash.uniq "^4.5.0"
4310+
resolve "^1.2.0"
4311+
serialize-javascript "^1.3.0"
4312+
source-map "0.5.6"
4313+
42484314
vue@^2.5.22:
42494315
version "2.6.10"
42504316
resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"

0 commit comments

Comments
 (0)