mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
feat(vue-app): new fetch syntax (#6880)
This commit is contained in:
parent
e271aa0a0a
commit
6db325c321
5
examples/new-fetch/README.md
Normal file
5
examples/new-fetch/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# New fetch() with Nuxt.js
|
||||||
|
|
||||||
|
Nuxt.js `v2.12` introduces a new hook called `fetch` in any of your Vue components.
|
||||||
|
|
||||||
|
See [live demo](https://nuxt-new-fetch.surge.sh) and [documentation](https://nuxtjs.org/api/pages-fetch).
|
30
examples/new-fetch/components/Author.vue
Normal file
30
examples/new-fetch/components/Author.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<p v-if="$fetchState.error">
|
||||||
|
Could not fetch Author
|
||||||
|
</p>
|
||||||
|
<p v-else>
|
||||||
|
Written by {{ $fetchState.pending ? '...' : user.name }} <button @click="$fetch">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
userId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetch () {
|
||||||
|
this.user = await this.$http.$get(`https://jsonplaceholder.typicode.com/users/${this.userId}`)
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
user: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchOnServer: false
|
||||||
|
}
|
||||||
|
</script>
|
28
examples/new-fetch/layouts/default.vue
Normal file
28
examples/new-fetch/layouts/default.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- <p>Fetching: {{ $nuxt.isFetching }} ({{ $nuxt.nbFetching }})</p> -->
|
||||||
|
<nuxt />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
head: {
|
||||||
|
link: [
|
||||||
|
{ rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/gh/kognise/water.css@1.4.0/dist/light.min.css' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 20px 30px;
|
||||||
|
}
|
||||||
|
.page-enter-active, .page-leave-active {
|
||||||
|
transition: opacity .3s;
|
||||||
|
}
|
||||||
|
.page-enter, .page-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
18
examples/new-fetch/nuxt.config.js
Normal file
18
examples/new-fetch/nuxt.config.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const fetch = require('node-fetch')
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
'@/plugins/vue-placeholders.js'
|
||||||
|
],
|
||||||
|
modules: [
|
||||||
|
'@nuxt/http'
|
||||||
|
],
|
||||||
|
generate: {
|
||||||
|
async routes () {
|
||||||
|
const posts = await fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()).then(d => d.slice(0, 20))
|
||||||
|
const routes = posts.map(post => `/posts/${post.id}`)
|
||||||
|
|
||||||
|
return ['/'].concat(routes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
examples/new-fetch/package.json
Normal file
18
examples/new-fetch/package.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "example-hello-world",
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/http": "^0.3.8",
|
||||||
|
"nuxt-start": "latest",
|
||||||
|
"vue-content-placeholders": "^0.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start",
|
||||||
|
"generate": "nuxt generate",
|
||||||
|
"post-update": "yarn upgrade --latest"
|
||||||
|
}
|
||||||
|
}
|
43
examples/new-fetch/pages/index.vue
Normal file
43
examples/new-fetch/pages/index.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Blog posts</h1>
|
||||||
|
<template v-if="$fetchState.pending">
|
||||||
|
<content-placeholders>
|
||||||
|
<content-placeholders-text :lines="20" />
|
||||||
|
</content-placeholders>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="$fetchState.error">
|
||||||
|
<p>
|
||||||
|
Error while fetching posts: {{ error }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ul>
|
||||||
|
<li v-for="post of posts" :key="post.id">
|
||||||
|
<n-link :to="`/posts/${post.id}`">
|
||||||
|
{{ post.title }}
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/posts/404">
|
||||||
|
404 post
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
|
||||||
|
.then(posts => posts.slice(0, 20))
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
posts: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
54
examples/new-fetch/pages/posts/_id.vue
Normal file
54
examples/new-fetch/pages/posts/_id.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button @click="$fetch">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
<template v-if="$fetchState.pending">
|
||||||
|
<content-placeholders>
|
||||||
|
<content-placeholders-heading />
|
||||||
|
<content-placeholders-text :lines="10" />
|
||||||
|
</content-placeholders>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="$fetchState.error">
|
||||||
|
<h1>
|
||||||
|
Post #{{ $route.params.id }} not found
|
||||||
|
</h1>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<h1>{{ post.title }}</h1>
|
||||||
|
<author :user-id="post.userId" />
|
||||||
|
<pre>{{ post.body }}</pre>
|
||||||
|
<p>
|
||||||
|
<n-link :to="{ name: 'posts-id', params: { id: (post.id + 1) } }">
|
||||||
|
Next article
|
||||||
|
</n-link>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<p>
|
||||||
|
<n-link to="/">
|
||||||
|
Home
|
||||||
|
</n-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Author from '~/components/Author.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Author
|
||||||
|
},
|
||||||
|
async fetch () {
|
||||||
|
this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
post: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
head () {
|
||||||
|
return { title: this.post.title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
4
examples/new-fetch/plugins/vue-placeholders.js
Normal file
4
examples/new-fetch/plugins/vue-placeholders.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueContentPlaceholders from 'vue-content-placeholders'
|
||||||
|
|
||||||
|
Vue.use(VueContentPlaceholders)
|
@ -13,6 +13,8 @@ export const template = {
|
|||||||
'server.js',
|
'server.js',
|
||||||
'utils.js',
|
'utils.js',
|
||||||
'empty.js',
|
'empty.js',
|
||||||
|
'mixins/fetch.server.js',
|
||||||
|
'mixins/fetch.client.js',
|
||||||
'components/nuxt-error.vue',
|
'components/nuxt-error.vue',
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
'components/nuxt-link.server.js',
|
'components/nuxt-link.server.js',
|
||||||
|
@ -2,6 +2,7 @@ import Vue from 'vue'
|
|||||||
<% if (features.asyncData || features.fetch) { %>
|
<% if (features.asyncData || features.fetch) { %>
|
||||||
import {
|
import {
|
||||||
getMatchedComponentsInstances,
|
getMatchedComponentsInstances,
|
||||||
|
getChildrenComponentInstancesUsingFetch,
|
||||||
promisify,
|
promisify,
|
||||||
globalHandleError
|
globalHandleError
|
||||||
} from './utils'
|
} from './utils'
|
||||||
@ -88,7 +89,10 @@ export default {
|
|||||||
<% } %>
|
<% } %>
|
||||||
<% if (features.layouts) { %>
|
<% if (features.layouts) { %>
|
||||||
layout: null,
|
layout: null,
|
||||||
layoutName: ''
|
layoutName: '',
|
||||||
|
<% } %>
|
||||||
|
<% if (features.fetch) { %>
|
||||||
|
nbFetching: 0
|
||||||
<% } %>
|
<% } %>
|
||||||
}),
|
}),
|
||||||
<% } %>
|
<% } %>
|
||||||
@ -125,7 +129,12 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
isOffline () {
|
isOffline () {
|
||||||
return !this.isOnline
|
return !this.isOnline
|
||||||
|
},
|
||||||
|
<% if (features.fetch) { %>
|
||||||
|
isFetching() {
|
||||||
|
return this.nbFetching > 0
|
||||||
}
|
}
|
||||||
|
<% } %>
|
||||||
},
|
},
|
||||||
<% } %>
|
<% } %>
|
||||||
methods: {
|
methods: {
|
||||||
@ -157,9 +166,18 @@ export default {
|
|||||||
const p = []
|
const p = []
|
||||||
|
|
||||||
<% if (features.fetch) { %>
|
<% if (features.fetch) { %>
|
||||||
if (page.$options.fetch) {
|
// Old fetch
|
||||||
|
if (page.$options.fetch && page.$options.fetch.length) {
|
||||||
p.push(promisify(page.$options.fetch, this.context))
|
p.push(promisify(page.$options.fetch, this.context))
|
||||||
}
|
}
|
||||||
|
if (page.$fetch) {
|
||||||
|
p.push(page.$fetch())
|
||||||
|
} else {
|
||||||
|
// Get all component instance to call $fetch
|
||||||
|
for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) {
|
||||||
|
p.push(component.$fetch())
|
||||||
|
}
|
||||||
|
}
|
||||||
<% } %>
|
<% } %>
|
||||||
<% if (features.asyncData) { %>
|
<% if (features.asyncData) { %>
|
||||||
if (page.$options.asyncData) {
|
if (page.$options.asyncData) {
|
||||||
|
@ -17,8 +17,17 @@ import {
|
|||||||
globalHandleError
|
globalHandleError
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
||||||
|
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
||||||
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js' // should be included after ./index.js
|
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js' // should be included after ./index.js
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
|
// Fetch mixin
|
||||||
|
if (!Vue.__nuxt__fetch__mixin__) {
|
||||||
|
Vue.mixin(fetchMixin)
|
||||||
|
Vue.__nuxt__fetch__mixin__ = true
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// Component: <NuxtLink>
|
// Component: <NuxtLink>
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
Vue.component(NuxtLink.name, NuxtLink)
|
||||||
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
||||||
@ -458,7 +467,10 @@ async function render (to, from, next) {
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% if (features.fetch) { %>
|
<% if (features.fetch) { %>
|
||||||
const hasFetch = Boolean(Component.options.fetch)
|
const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length
|
||||||
|
if (hasFetch) {
|
||||||
|
console.warn('fetch(context) has been deprecated, please use middleware(context)')
|
||||||
|
}
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
const hasFetch = false
|
const hasFetch = false
|
||||||
<% } %>
|
<% } %>
|
||||||
@ -738,7 +750,7 @@ function addHotReload ($component, depth) {
|
|||||||
<% if (features.fetch) { %>
|
<% if (features.fetch) { %>
|
||||||
// Call fetch()
|
// Call fetch()
|
||||||
Component.options.fetch = Component.options.fetch || noopFetch
|
Component.options.fetch = Component.options.fetch || noopFetch
|
||||||
let pFetch = Component.options.fetch(context)
|
let pFetch = Component.options.fetch.length && Component.options.fetch(context)
|
||||||
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
||||||
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
<%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
||||||
promises.push(pFetch)
|
promises.push(pFetch)
|
||||||
|
@ -13,7 +13,8 @@ export default {
|
|||||||
default: undefined
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render (h, { parent, data, props }) {
|
render (_, { parent, data, props }) {
|
||||||
|
const h = parent.$createElement
|
||||||
<% if (features.transitions) { %>
|
<% if (features.transitions) { %>
|
||||||
data.nuxtChild = true
|
data.nuxtChild = true
|
||||||
const _parent = parent
|
const _parent = parent
|
||||||
@ -42,6 +43,7 @@ export default {
|
|||||||
listeners[key] = transition[key].bind(_parent)
|
listeners[key] = transition[key].bind(_parent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (process.client) {
|
||||||
// Add triggerScroll event on beforeEnter (fix #1376)
|
// Add triggerScroll event on beforeEnter (fix #1376)
|
||||||
const beforeEnter = listeners.beforeEnter
|
const beforeEnter = listeners.beforeEnter
|
||||||
listeners.beforeEnter = (el) => {
|
listeners.beforeEnter = (el) => {
|
||||||
@ -53,6 +55,7 @@ export default {
|
|||||||
return beforeEnter.call(_parent, el)
|
return beforeEnter.call(_parent, el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// make sure that leave is called asynchronous (fix #5703)
|
// make sure that leave is called asynchronous (fix #5703)
|
||||||
if (transition.css === false) {
|
if (transition.css === false) {
|
||||||
|
80
packages/vue-app/template/mixins/fetch.client.js
Normal file
80
packages/vue-app/template/mixins/fetch.client.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { hasFetch, normalizeError, addLifecycleHook } from '../utils'
|
||||||
|
|
||||||
|
const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.ssrKey
|
||||||
|
const nuxtState = window.<%= globals.context %>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
beforeCreate () {
|
||||||
|
if (!hasFetch(this)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200
|
||||||
|
|
||||||
|
Vue.util.defineReactive(this, '$fetchState', {
|
||||||
|
pending: false,
|
||||||
|
error: null,
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$fetch = $fetch.bind(this)
|
||||||
|
addLifecycleHook(this, 'created', created)
|
||||||
|
addLifecycleHook(this, 'beforeMount', beforeMount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeMount() {
|
||||||
|
if (!this._hydrated) {
|
||||||
|
return this.$fetch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function created() {
|
||||||
|
if (!isSsrHydration(this)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hydrate component
|
||||||
|
this._hydrated = true
|
||||||
|
this._ssrKey = +this.$vnode.elm.dataset.ssrKey
|
||||||
|
const data = nuxtState.fetch[this._ssrKey]
|
||||||
|
|
||||||
|
// If fetch error
|
||||||
|
if (data && data._error) {
|
||||||
|
this.$fetchState.error = data._error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge data
|
||||||
|
for (const key in data) {
|
||||||
|
Vue.set(this.$data, key, data[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function $fetch() {
|
||||||
|
this.$nuxt.nbFetching++
|
||||||
|
this.$fetchState.pending = true
|
||||||
|
this.$fetchState.error = null
|
||||||
|
this._hydrated = false
|
||||||
|
let error = null
|
||||||
|
const startTime = Date.now()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$options.fetch.call(this)
|
||||||
|
} catch (err) {
|
||||||
|
error = normalizeError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const delayLeft = this._fetchDelay - (Date.now() - startTime)
|
||||||
|
if (delayLeft > 0) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delayLeft))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$fetchState.error = error
|
||||||
|
this.$fetchState.pending = false
|
||||||
|
this.$fetchState.timestamp = Date.now()
|
||||||
|
|
||||||
|
this.$nextTick(() => this.$nuxt.nbFetching--)
|
||||||
|
}
|
||||||
|
|
49
packages/vue-app/template/mixins/fetch.server.js
Normal file
49
packages/vue-app/template/mixins/fetch.server.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { hasFetch, normalizeError, addLifecycleHook } from '../utils'
|
||||||
|
|
||||||
|
async function serverPrefetch() {
|
||||||
|
if (!this._fetchOnServer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call and await on $fetch
|
||||||
|
try {
|
||||||
|
await this.$options.fetch.call(this)
|
||||||
|
} catch (err) {
|
||||||
|
this.$fetchState.error = normalizeError(err)
|
||||||
|
}
|
||||||
|
this.$fetchState.pending = false
|
||||||
|
|
||||||
|
|
||||||
|
// Define an ssrKey for hydration
|
||||||
|
this._ssrKey = this.$ssrContext.nuxt.fetch.length
|
||||||
|
|
||||||
|
// Add data-ssr-key on parent element of Component
|
||||||
|
const attrs = this.$vnode.data.attrs = this.$vnode.data.attrs || {}
|
||||||
|
attrs['data-ssr-key'] = this._ssrKey
|
||||||
|
|
||||||
|
// Call asyncData & add to ssrContext for window.__NUXT__.fetch
|
||||||
|
this.$ssrContext.nuxt.fetch.push(this.$fetchState.error ? { _error: this.$fetchState.error } : this._data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
beforeCreate() {
|
||||||
|
if (!hasFetch(this)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.$options.fetchOnServer === 'function') {
|
||||||
|
this._fetchOnServer = this.$options.fetchOnServer.call(this) !== false
|
||||||
|
} else {
|
||||||
|
this._fetchOnServer = this.$options.fetchOnServer !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.util.defineReactive(this, '$fetchState', {
|
||||||
|
pending: true,
|
||||||
|
error: null,
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
addLifecycleHook(this, 'serverPrefetch', serverPrefetch)
|
||||||
|
}
|
||||||
|
}
|
@ -9,9 +9,21 @@ import {
|
|||||||
getMatchedComponents,
|
getMatchedComponents,
|
||||||
promisify
|
promisify
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
|
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.server'<% } %>
|
||||||
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
||||||
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
|
// Update serverPrefetch strategy
|
||||||
|
Vue.config.optionMergeStrategies.serverPrefetch = Vue.config.optionMergeStrategies.created
|
||||||
|
|
||||||
|
// Fetch mixin
|
||||||
|
if (!Vue.__nuxt__fetch__mixin__) {
|
||||||
|
Vue.mixin(fetchMixin)
|
||||||
|
Vue.__nuxt__fetch__mixin__ = true
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
|
|
||||||
// Component: <NuxtLink>
|
// Component: <NuxtLink>
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
Vue.component(NuxtLink.name, NuxtLink)
|
||||||
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
||||||
@ -60,7 +72,7 @@ export default async (ssrContext) => {
|
|||||||
// Used for beforeNuxtRender({ Components, nuxtState })
|
// Used for beforeNuxtRender({ Components, nuxtState })
|
||||||
ssrContext.beforeRenderFns = []
|
ssrContext.beforeRenderFns = []
|
||||||
// Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
|
// Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
|
||||||
ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], <% if (features.fetch) { %>fetch: [], <% } %>error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
||||||
// Create the app definition and the instance (created for each request)
|
// Create the app definition and the instance (created for each request)
|
||||||
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
|
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
@ -267,7 +279,8 @@ export default async (ssrContext) => {
|
|||||||
|
|
||||||
<% if (features.fetch) { %>
|
<% if (features.fetch) { %>
|
||||||
// Call fetch(context)
|
// Call fetch(context)
|
||||||
if (Component.options.fetch) {
|
if (Component.options.fetch && Component.options.fetch.length) {
|
||||||
|
console.warn('fetch(context) has been deprecated, please use middleware(context)')
|
||||||
promises.push(Component.options.fetch(app.context))
|
promises.push(Component.options.fetch(app.context))
|
||||||
} else {
|
} else {
|
||||||
promises.push(null)
|
promises.push(null)
|
||||||
|
@ -21,6 +21,24 @@ export function interopDefault (promise) {
|
|||||||
return promise.then(m => m.default || m)
|
return promise.then(m => m.default || m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if (features.fetch) { %>
|
||||||
|
export function hasFetch(vm) {
|
||||||
|
return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length
|
||||||
|
}
|
||||||
|
export function getChildrenComponentInstancesUsingFetch(vm, instances = []) {
|
||||||
|
const children = vm.$children || []
|
||||||
|
for (const child of children) {
|
||||||
|
if (child.$fetch) {
|
||||||
|
instances.push(child)
|
||||||
|
continue; // Don't get the children since it will reload the template
|
||||||
|
}
|
||||||
|
if (child.$children) {
|
||||||
|
getChildrenComponentInstancesUsingFetch(child, instances)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instances
|
||||||
|
}
|
||||||
|
<% } %>
|
||||||
<% if (features.asyncData) { %>
|
<% if (features.asyncData) { %>
|
||||||
export function applyAsyncData (Component, asyncData) {
|
export function applyAsyncData (Component, asyncData) {
|
||||||
if (
|
if (
|
||||||
@ -615,3 +633,10 @@ function formatQuery (query) {
|
|||||||
}).filter(Boolean).join('&')
|
}).filter(Boolean).join('&')
|
||||||
}
|
}
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
export function addLifecycleHook(vm, hook, fn) {
|
||||||
|
if (!vm.$options[hook]) {
|
||||||
|
vm.$options[hook] = []
|
||||||
|
}
|
||||||
|
vm.$options[hook].push(fn)
|
||||||
|
}
|
||||||
|
159
test/e2e/fetch.browser.test.js
Normal file
159
test/e2e/fetch.browser.test.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import Browser from '../utils/browser'
|
||||||
|
import { loadFixture, getPort, Nuxt } from '../utils'
|
||||||
|
|
||||||
|
let port
|
||||||
|
const browser = new Browser()
|
||||||
|
const url = route => 'http://localhost:' + port + route
|
||||||
|
|
||||||
|
let nuxt = null
|
||||||
|
let page = null
|
||||||
|
|
||||||
|
describe('basic browser', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
const config = await loadFixture('fetch')
|
||||||
|
nuxt = new Nuxt(config)
|
||||||
|
await nuxt.ready()
|
||||||
|
|
||||||
|
port = await getPort()
|
||||||
|
await nuxt.server.listen(port, 'localhost')
|
||||||
|
|
||||||
|
await browser.start({
|
||||||
|
// slowMo: 50,
|
||||||
|
// headless: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Open /', async () => {
|
||||||
|
page = await browser.page(url('/'))
|
||||||
|
expect(await page.$text('pre')).toContain('Atinux')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/fetch-client', async () => {
|
||||||
|
await page.nuxt.navigate('/fetch-client')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('pi0')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/fetch-error', async () => {
|
||||||
|
await page.nuxt.navigate('/fetch-error')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('#error')
|
||||||
|
expect(await page.$text('#error')).toContain('fetch-error')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/fetch-component', async () => {
|
||||||
|
await page.nuxt.navigate('/fetch-component')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('clarkdo')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/fetch-delay', async () => {
|
||||||
|
const now = Date.now()
|
||||||
|
await page.nuxt.navigate('/fetch-delay')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching for 1 second')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
const delay = Date.now() - now
|
||||||
|
expect(await page.$text('pre')).toContain('alexchopin')
|
||||||
|
expect(delay).toBeGreaterThanOrEqual(1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/fetch-button', async () => {
|
||||||
|
await page.nuxt.navigate('/fetch-button')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('kevinmarrec')
|
||||||
|
await page.click('button')
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('kevinmarrec')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/old-fetch', async () => {
|
||||||
|
const msg = new Promise(resolve =>
|
||||||
|
page.on('console', msg => resolve(msg.text()))
|
||||||
|
)
|
||||||
|
await page.nuxt.navigate('/old-fetch')
|
||||||
|
expect(await msg).toBe('fetch(context) has been deprecated, please use middleware(context)')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ssr: /fetch-client', async () => {
|
||||||
|
const page = await browser.page(url('/fetch-client'))
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('pi0')
|
||||||
|
page.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ssr: /fetch-conditional', async () => {
|
||||||
|
const page = await browser.page(url('/fetch-conditional'))
|
||||||
|
expect(await page.$text('pre')).toContain('galvez')
|
||||||
|
page.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ssr: /fetch-conditional?fetch_client=true', async () => {
|
||||||
|
const page = await browser.page(url('/fetch-conditional?fetch_client=true'))
|
||||||
|
expect(await page.$text('p')).toContain('Fetching...')
|
||||||
|
await page.waitForSelector('pre')
|
||||||
|
expect(await page.$text('pre')).toContain('pimlie')
|
||||||
|
page.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ssr: /fetch-error', async () => {
|
||||||
|
const page = await browser.page(url('/fetch-error'))
|
||||||
|
expect(await page.$text('#error')).toContain('fetch-error')
|
||||||
|
page.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ssr: /fetch-deep', async () => {
|
||||||
|
const page = await browser.page(url('/fetch-deep'))
|
||||||
|
const expectedState = {
|
||||||
|
foo: 'barbar',
|
||||||
|
user: {
|
||||||
|
name: 'Potato',
|
||||||
|
inventory: {
|
||||||
|
type: 'green',
|
||||||
|
items: ['A', 'B']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async: 'data',
|
||||||
|
async2: 'data2fetch'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hydrated HTML
|
||||||
|
const renderedData = await page.$text('#data').then(t => JSON.parse(t))
|
||||||
|
expect(renderedData).toMatchObject(expectedState)
|
||||||
|
|
||||||
|
// Fragments
|
||||||
|
const { data, fetch } = await page.evaluate(() => window.__NUXT__)
|
||||||
|
expect(data.length).toBe(1)
|
||||||
|
expect(fetch.length).toBe(1)
|
||||||
|
|
||||||
|
// asyncData mutations
|
||||||
|
expect(data[0]).toMatchObject({ async: 'data', async2: 'data2' })
|
||||||
|
|
||||||
|
// fetch mutations
|
||||||
|
expect(fetch[0]).toMatchObject({
|
||||||
|
user: {
|
||||||
|
inventory: { items: ['A', 'B'] },
|
||||||
|
name: 'Potato'
|
||||||
|
},
|
||||||
|
foo: 'barbar',
|
||||||
|
async2: 'data2fetch'
|
||||||
|
})
|
||||||
|
|
||||||
|
page.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close server and ask nuxt to stop listening to file changes
|
||||||
|
afterAll(async () => {
|
||||||
|
await nuxt.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop browser
|
||||||
|
afterAll(async () => {
|
||||||
|
await page.close()
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
})
|
2
test/fixtures/basic/pages/error-object.vue
vendored
2
test/fixtures/basic/pages/error-object.vue
vendored
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch () {
|
middleware (context) {
|
||||||
throw { error: 'fetch error!' } // eslint-disable-line
|
throw { error: 'fetch error!' } // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/error-string.vue
vendored
2
test/fixtures/basic/pages/error-string.vue
vendored
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch () {
|
middleware (context) {
|
||||||
throw 'fetch error!' // eslint-disable-line
|
throw 'fetch error!' // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
fetch ({ redirect }) {
|
middleware ({ redirect }) {
|
||||||
return redirect('https://nuxtjs.org/')
|
return redirect('https://nuxtjs.org/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/redirect-name.vue
vendored
2
test/fixtures/basic/pages/redirect-name.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
fetch ({ redirect }) {
|
middleware ({ redirect }) {
|
||||||
return redirect({ name: 'stateless' })
|
return redirect({ name: 'stateless' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/redirect.vue
vendored
2
test/fixtures/basic/pages/redirect.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
fetch ({ redirect }) {
|
middleware ({ redirect }) {
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/basic/pages/special-state.vue
vendored
2
test/fixtures/basic/pages/special-state.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
fetch ({ beforeNuxtRender }) {
|
middleware ({ beforeNuxtRender }) {
|
||||||
if (process.server) {
|
if (process.server) {
|
||||||
beforeNuxtRender(({ nuxtState }) => {
|
beforeNuxtRender(({ nuxtState }) => {
|
||||||
nuxtState.test = true
|
nuxtState.test = true
|
||||||
|
2
test/fixtures/basic/pages/store-module.vue
vendored
2
test/fixtures/basic/pages/store-module.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
fetch ({ store }) {
|
middleware ({ store }) {
|
||||||
store.dispatch('simpleModule/mutate')
|
store.dispatch('simpleModule/mutate')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
test/fixtures/fetch/components/Team.vue
vendored
Normal file
26
test/fixtures/fetch/components/Team.vue
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<p v-else-if="$fetchState.error">
|
||||||
|
{{ $fetchState.error }}
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
const url = (process.server ? `http://${this.$ssrContext.req.headers.host}` : '')
|
||||||
|
|
||||||
|
this.team = await fetch(`${url}/team.json`).then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
3
test/fixtures/fetch/fetch.test.js
vendored
Normal file
3
test/fixtures/fetch/fetch.test.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { buildFixture } from '../../utils/build'
|
||||||
|
|
||||||
|
buildFixture('fetch')
|
52
test/fixtures/fetch/layouts/default.vue
vendored
Normal file
52
test/fixtures/fetch/layouts/default.vue
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<n-link to="/">
|
||||||
|
Fetch
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-client">
|
||||||
|
Fetch on client
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-conditional">
|
||||||
|
Fetch conditional
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-error">
|
||||||
|
Fetch error
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-delay">
|
||||||
|
Fetch delay
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-button">
|
||||||
|
Fetch button
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-component">
|
||||||
|
Fetch in component
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/fetch-deep">
|
||||||
|
Fetch with deep update updates
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<n-link to="/old-fetch">
|
||||||
|
Deprecated fetch
|
||||||
|
</n-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<nuxt />
|
||||||
|
</div>
|
||||||
|
</template>
|
1
test/fixtures/fetch/nuxt.config.js
vendored
Normal file
1
test/fixtures/fetch/nuxt.config.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default {}
|
26
test/fixtures/fetch/pages/fetch-button.vue
vendored
Normal file
26
test/fixtures/fetch/pages/fetch-button.vue
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
<button @click="$fetch">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
const url = (process.server ? `http://${this.$ssrContext.req.headers.host}` : '')
|
||||||
|
|
||||||
|
this.team = await fetch(`${url}/team.json`).then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
22
test/fixtures/fetch/pages/fetch-client.vue
vendored
Normal file
22
test/fixtures/fetch/pages/fetch-client.vue
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
this.team = await fetch('/team.json').then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchOnServer: false
|
||||||
|
}
|
||||||
|
</script>
|
13
test/fixtures/fetch/pages/fetch-component.vue
vendored
Normal file
13
test/fixtures/fetch/pages/fetch-component.vue
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<team />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Team from '@/components/Team.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Team
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
27
test/fixtures/fetch/pages/fetch-conditional.vue
vendored
Normal file
27
test/fixtures/fetch/pages/fetch-conditional.vue
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nuxt-link to="/fetch-conditional?fetch_client=true">Fetch on client</nuxt-link>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
const url = (process.server ? `http://${this.$ssrContext.req.headers.host}` : '')
|
||||||
|
|
||||||
|
this.team = await fetch(`${url}/team.json`).then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchOnServer () {
|
||||||
|
return !this.$route.query.fetch_client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
35
test/fixtures/fetch/pages/fetch-deep.vue
vendored
Normal file
35
test/fixtures/fetch/pages/fetch-deep.vue
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
|
<pre id="data" v-html="JSON.stringify($data)" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
this.user.inventory.items.push('B')
|
||||||
|
this.user.name = 'Potato'
|
||||||
|
this.foo = 'barbar'
|
||||||
|
this.async2 = 'data2fetch'
|
||||||
|
},
|
||||||
|
async asyncData () {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
return {
|
||||||
|
async: 'data',
|
||||||
|
async2: 'data2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
foo: 'bar',
|
||||||
|
user: {
|
||||||
|
name: 'Baz',
|
||||||
|
inventory: {
|
||||||
|
type: 'green',
|
||||||
|
items: ['A']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
24
test/fixtures/fetch/pages/fetch-delay.vue
vendored
Normal file
24
test/fixtures/fetch/pages/fetch-delay.vue
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching for 1 second
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
const url = (process.server ? `http://${this.$ssrContext.req.headers.host}` : '')
|
||||||
|
|
||||||
|
this.team = await fetch(`${url}/team.json`).then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchDelay: 1000
|
||||||
|
}
|
||||||
|
</script>
|
24
test/fixtures/fetch/pages/fetch-error.vue
vendored
Normal file
24
test/fixtures/fetch/pages/fetch-error.vue
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<p v-else-if="$fetchState.error" id="error">
|
||||||
|
{{ $fetchState.error.message }}
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
await new Promise((resolve, reject) => reject(new Error('fetch-error')))
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
23
test/fixtures/fetch/pages/index.vue
vendored
Normal file
23
test/fixtures/fetch/pages/index.vue
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="$fetchState.pending">
|
||||||
|
Fetching...
|
||||||
|
</p>
|
||||||
|
<pre v-else>{{ team }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch () {
|
||||||
|
const url = (process.server ? `http://${this.$ssrContext.req.headers.host}` : '')
|
||||||
|
|
||||||
|
this.team = await fetch(`${url}/team.json`).then(res => res.json())
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
18
test/fixtures/fetch/pages/old-fetch.vue
vendored
Normal file
18
test/fixtures/fetch/pages/old-fetch.vue
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Display a warning in the console
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async fetch (context) {
|
||||||
|
// Should display a warning
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
team: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
11
test/fixtures/fetch/static/team.json
vendored
Normal file
11
test/fixtures/fetch/static/team.json
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
"Atinux",
|
||||||
|
"alexchopin",
|
||||||
|
"pi0",
|
||||||
|
"clarkdo",
|
||||||
|
"manniL",
|
||||||
|
"galvez",
|
||||||
|
"aldarund",
|
||||||
|
"kevinmarrec",
|
||||||
|
"pimlie"
|
||||||
|
]
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch () {
|
fetch (context) {
|
||||||
throw { message: 'fetch error!' } // eslint-disable-line
|
throw { message: 'fetch error!' } // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch () {
|
fetch (context) {
|
||||||
throw 'fetch error!' // eslint-disable-line
|
throw 'fetch error!' // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/spa/pages/error-handler.vue
vendored
2
test/fixtures/spa/pages/error-handler.vue
vendored
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch () {
|
fetch (context) {
|
||||||
throw new Error('fetch error!')
|
throw new Error('fetch error!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user