Skip to content

Commit 6db325c

Browse files
authored
feat(vue-app): new fetch syntax (nuxt#6880)
1 parent e271aa0 commit 6db325c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+893
-27
lines changed

examples/new-fetch/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# New fetch() with Nuxt.js
2+
3+
Nuxt.js `v2.12` introduces a new hook called `fetch` in any of your Vue components.
4+
5+
See [live demo](https://nuxt-new-fetch.surge.sh) and [documentation](https://nuxtjs.org/api/pages-fetch).
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<template>
2+
<p v-if="$fetchState.error">
3+
Could not fetch Author
4+
</p>
5+
<p v-else>
6+
Written by {{ $fetchState.pending ? '...' : user.name }} <button @click="$fetch">
7+
Refresh
8+
</button>
9+
</p>
10+
</template>
11+
12+
<script>
13+
export default {
14+
props: {
15+
userId: {
16+
type: Number,
17+
required: true
18+
}
19+
},
20+
async fetch () {
21+
this.user = await this.$http.$get(`https://jsonplaceholder.typicode.com/users/${this.userId}`)
22+
},
23+
data () {
24+
return {
25+
user: {}
26+
}
27+
},
28+
fetchOnServer: false
29+
}
30+
</script>
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div>
3+
<!-- <p>Fetching: {{ $nuxt.isFetching }} ({{ $nuxt.nbFetching }})</p> -->
4+
<nuxt />
5+
</div>
6+
</template>
7+
8+
<script>
9+
export default {
10+
head: {
11+
link: [
12+
{ rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/gh/kognise/[email protected]/dist/light.min.css' }
13+
]
14+
}
15+
}
16+
</script>
17+
18+
<style>
19+
body {
20+
padding: 20px 30px;
21+
}
22+
.page-enter-active, .page-leave-active {
23+
transition: opacity .3s;
24+
}
25+
.page-enter, .page-leave-to {
26+
opacity: 0;
27+
}
28+
</style>

examples/new-fetch/nuxt.config.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const fetch = require('node-fetch')
2+
3+
export default {
4+
plugins: [
5+
'@/plugins/vue-placeholders.js'
6+
],
7+
modules: [
8+
'@nuxt/http'
9+
],
10+
generate: {
11+
async routes () {
12+
const posts = await fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()).then(d => d.slice(0, 20))
13+
const routes = posts.map(post => `/posts/${post.id}`)
14+
15+
return ['/'].concat(routes)
16+
}
17+
}
18+
}

examples/new-fetch/package.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "example-hello-world",
3+
"dependencies": {
4+
"@nuxt/http": "^0.3.8",
5+
"nuxt-start": "latest",
6+
"vue-content-placeholders": "^0.2.1"
7+
},
8+
"devDependencies": {
9+
"nuxt": "latest"
10+
},
11+
"scripts": {
12+
"dev": "nuxt",
13+
"build": "nuxt build",
14+
"start": "nuxt start",
15+
"generate": "nuxt generate",
16+
"post-update": "yarn upgrade --latest"
17+
}
18+
}

examples/new-fetch/pages/index.vue

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<div>
3+
<h1>Blog posts</h1>
4+
<template v-if="$fetchState.pending">
5+
<content-placeholders>
6+
<content-placeholders-text :lines="20" />
7+
</content-placeholders>
8+
</template>
9+
<template v-else-if="$fetchState.error">
10+
<p>
11+
Error while fetching posts: {{ error }}
12+
</p>
13+
</template>
14+
<template v-else>
15+
<ul>
16+
<li v-for="post of posts" :key="post.id">
17+
<n-link :to="`/posts/${post.id}`">
18+
{{ post.title }}
19+
</n-link>
20+
</li>
21+
<li>
22+
<n-link to="/posts/404">
23+
404 post
24+
</n-link>
25+
</li>
26+
</ul>
27+
</template>
28+
</div>
29+
</template>
30+
31+
<script>
32+
export default {
33+
async fetch () {
34+
this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
35+
.then(posts => posts.slice(0, 20))
36+
},
37+
data () {
38+
return {
39+
posts: null
40+
}
41+
}
42+
}
43+
</script>
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<template>
2+
<div>
3+
<button @click="$fetch">
4+
Refresh
5+
</button>
6+
<template v-if="$fetchState.pending">
7+
<content-placeholders>
8+
<content-placeholders-heading />
9+
<content-placeholders-text :lines="10" />
10+
</content-placeholders>
11+
</template>
12+
<template v-else-if="$fetchState.error">
13+
<h1>
14+
Post #{{ $route.params.id }} not found
15+
</h1>
16+
</template>
17+
<template v-else>
18+
<h1>{{ post.title }}</h1>
19+
<author :user-id="post.userId" />
20+
<pre>{{ post.body }}</pre>
21+
<p>
22+
<n-link :to="{ name: 'posts-id', params: { id: (post.id + 1) } }">
23+
Next article
24+
</n-link>
25+
</p>
26+
</template>
27+
<p>
28+
<n-link to="/">
29+
Home
30+
</n-link>
31+
</p>
32+
</div>
33+
</template>
34+
35+
<script>
36+
import Author from '~/components/Author.vue'
37+
38+
export default {
39+
components: {
40+
Author
41+
},
42+
async fetch () {
43+
this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
44+
},
45+
data () {
46+
return {
47+
post: {}
48+
}
49+
},
50+
head () {
51+
return { title: this.post.title }
52+
}
53+
}
54+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Vue from 'vue'
2+
import VueContentPlaceholders from 'vue-content-placeholders'
3+
4+
Vue.use(VueContentPlaceholders)

packages/vue-app/src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export const template = {
1313
'server.js',
1414
'utils.js',
1515
'empty.js',
16+
'mixins/fetch.server.js',
17+
'mixins/fetch.client.js',
1618
'components/nuxt-error.vue',
1719
'components/nuxt-child.js',
1820
'components/nuxt-link.server.js',

packages/vue-app/template/App.js

+21-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Vue from 'vue'
22
<% if (features.asyncData || features.fetch) { %>
33
import {
44
getMatchedComponentsInstances,
5+
getChildrenComponentInstancesUsingFetch,
56
promisify,
67
globalHandleError
78
} from './utils'
@@ -88,9 +89,12 @@ export default {
8889
<% } %>
8990
<% if (features.layouts) { %>
9091
layout: null,
91-
layoutName: ''
92+
layoutName: '',
9293
<% } %>
93-
}),
94+
<% if (features.fetch) { %>
95+
nbFetching: 0
96+
<% } %>
97+
}),
9498
<% } %>
9599
beforeCreate () {
96100
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
@@ -125,7 +129,12 @@ export default {
125129
computed: {
126130
isOffline () {
127131
return !this.isOnline
132+
},
133+
<% if (features.fetch) { %>
134+
isFetching() {
135+
return this.nbFetching > 0
128136
}
137+
<% } %>
129138
},
130139
<% } %>
131140
methods: {
@@ -157,9 +166,18 @@ export default {
157166
const p = []
158167

159168
<% if (features.fetch) { %>
160-
if (page.$options.fetch) {
169+
// Old fetch
170+
if (page.$options.fetch && page.$options.fetch.length) {
161171
p.push(promisify(page.$options.fetch, this.context))
162172
}
173+
if (page.$fetch) {
174+
p.push(page.$fetch())
175+
} else {
176+
// Get all component instance to call $fetch
177+
for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) {
178+
p.push(component.$fetch())
179+
}
180+
}
163181
<% } %>
164182
<% if (features.asyncData) { %>
165183
if (page.$options.asyncData) {

packages/vue-app/template/client.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@ import {
1717
globalHandleError
1818
} from './utils.js'
1919
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
20+
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
2021
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js' // should be included after ./index.js
2122

23+
<% if (features.fetch) { %>
24+
// Fetch mixin
25+
if (!Vue.__nuxt__fetch__mixin__) {
26+
Vue.mixin(fetchMixin)
27+
Vue.__nuxt__fetch__mixin__ = true
28+
}
29+
<% } %>
30+
2231
// Component: <NuxtLink>
2332
Vue.component(NuxtLink.name, NuxtLink)
2433
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
@@ -458,7 +467,10 @@ async function render (to, from, next) {
458467
<% } %>
459468

460469
<% if (features.fetch) { %>
461-
const hasFetch = Boolean(Component.options.fetch)
470+
const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length
471+
if (hasFetch) {
472+
console.warn('fetch(context) has been deprecated, please use middleware(context)')
473+
}
462474
<% } else { %>
463475
const hasFetch = false
464476
<% } %>
@@ -738,7 +750,7 @@ function addHotReload ($component, depth) {
738750
<% if (features.fetch) { %>
739751
// Call fetch()
740752
Component.options.fetch = Component.options.fetch || noopFetch
741-
let pFetch = Component.options.fetch(context)
753+
let pFetch = Component.options.fetch.length && Component.options.fetch(context)
742754
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
743755
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
744756
promises.push(pFetch)

packages/vue-app/template/components/nuxt-child.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export default {
1313
default: undefined
1414
}
1515
},
16-
render (h, { parent, data, props }) {
16+
render (_, { parent, data, props }) {
17+
const h = parent.$createElement
1718
<% if (features.transitions) { %>
1819
data.nuxtChild = true
1920
const _parent = parent
@@ -42,15 +43,17 @@ export default {
4243
listeners[key] = transition[key].bind(_parent)
4344
}
4445
})
45-
// Add triggerScroll event on beforeEnter (fix #1376)
46-
const beforeEnter = listeners.beforeEnter
47-
listeners.beforeEnter = (el) => {
48-
// Ensure to trigger scroll event after calling scrollBehavior
49-
window.<%= globals.nuxt %>.$nextTick(() => {
50-
window.<%= globals.nuxt %>.$emit('triggerScroll')
51-
})
52-
if (beforeEnter) {
53-
return beforeEnter.call(_parent, el)
46+
if (process.client) {
47+
// Add triggerScroll event on beforeEnter (fix #1376)
48+
const beforeEnter = listeners.beforeEnter
49+
listeners.beforeEnter = (el) => {
50+
// Ensure to trigger scroll event after calling scrollBehavior
51+
window.<%= globals.nuxt %>.$nextTick(() => {
52+
window.<%= globals.nuxt %>.$emit('triggerScroll')
53+
})
54+
if (beforeEnter) {
55+
return beforeEnter.call(_parent, el)
56+
}
5457
}
5558
}
5659

0 commit comments

Comments
 (0)