mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-19 01:45:53 +00:00
Add callback argument (optional) in data
This commit is contained in:
parent
e48f8250a0
commit
ae9b41f321
@ -2,17 +2,19 @@
|
||||
|
||||
## data (context)
|
||||
|
||||
> Nuxt.js *supercharges* the `data` method from vue.js to let you handle async operation before setting the real component data.
|
||||
> Nuxt.js *supercharges* the `data` method from vue.js to let you handle async operation before setting the component data.
|
||||
|
||||
`data` is called every time before loading the component (*only if attached to a route*). It can be called from the server-side or before navigating to the corresponding route.
|
||||
|
||||
The `data` method receives the context as the first argument, you can use it to fetch some data and return the component data. To make the data method asynchronous, **return a Promise**, nuxt.js will wait for the promise to be resolved before rendering the Component.
|
||||
The `data` method receives the context as the first argument, you can use it to fetch some data and return the component data. To make the data method asynchronous, Nuxt.js offers you 2 ways, choose the one you're the most familiar with:
|
||||
1. returning a `Promise`, Nuxt.js will wait for the promise to be resolved before rendering the Component
|
||||
2. Define a second argument which is a callback method to be called like this: `callback(err, data)`
|
||||
|
||||
For example:
|
||||
Example with returning a `Promise`:
|
||||
```js
|
||||
export default {
|
||||
data ({ params }) {
|
||||
return axios.get(`http://my-api/posts/${params.id}`)
|
||||
return axios.get(`https://my-api/posts/${params.id}`)
|
||||
.then((res) => {
|
||||
return { title: res.data.title }
|
||||
})
|
||||
@ -20,6 +22,18 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
Example with using the `callback` argument:
|
||||
```js
|
||||
export default {
|
||||
data ({ params }, callback) {
|
||||
axios.get(`https://my-api/posts/${params.id}`)
|
||||
.then((res) => {
|
||||
callback(null, { title: res.data.title })
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And then, you can display the data inside your template:
|
||||
|
||||
```html
|
||||
@ -36,12 +50,49 @@ List of all the available keys in `context`:
|
||||
|-----|------|--------------|-------------|
|
||||
| `isClient` | Boolean | Client & Server | Boolean to let you know if you're actually renderer from the client-side |
|
||||
| `isServer` | Boolean | Client & Server | Boolean to let you know if you're actually renderer from the server-side |
|
||||
| `isDev` | Boolean | Client & Server | Boolean to let you know if you're in dev mode, can be useful for caching some data in production |
|
||||
| `route` | [vue-router route](https://router.vuejs.org/en/api/route-object.html) | Client & Server | `vue-router` route instance [see documentation](https://router.vuejs.org/en/api/route-object.html) |
|
||||
| `store` | [vuex store](http://vuex.vuejs.org/en/api.html#vuexstore-instance-properties) | Client & Server | `Vuex.Store` instance. **Available only if `store: true` is set in `nuxt.config.js`** |
|
||||
| `params` | Object | Client & Server | Alias of route.params |
|
||||
| `query` | Object | Client & Server | Alias of route.query |
|
||||
| `req` | [http.Request](https://nodejs.org/api/http.html#http_class_http_incomingmessage) | Server | Request from the node.js server. If nuxt is used as a middleware, the req object might be different depending of the framework you're using. |
|
||||
| `res` | [http.Response](https://nodejs.org/api/http.html#http_class_http_serverresponse) | Server | Response from the node.js server. If nuxt is used as a middleware, the res object might be different depending of the framework you're using. |
|
||||
| `redirect` | Function | Client & Server | Use this method to redirect the user to another route, the status code is used on the server-side, default to 302. `redirect([status,] path [, query])` |
|
||||
| `error` | Function | Client & Server | Use this method to show the error page: `error(params)`. The `params` should have the fields `statusCode` and `message`. |
|
||||
|
||||
## Handling errors
|
||||
|
||||
Nuxt.js add the `error(params)` method in the `context`, you can call it to display the error page. `params.statusCode` will be also used to render the proper status code form the server-side.
|
||||
|
||||
Example with a `Promise`:
|
||||
```js
|
||||
export default {
|
||||
data ({ params, error }) {
|
||||
return axios.get(`https://my-api/posts/${params.id}`)
|
||||
.then((res) => {
|
||||
return { title: res.data.title }
|
||||
})
|
||||
.catch((e) => {
|
||||
error({ statusCode: 404, message: 'Post not found' })
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the `callback` argument, you can call it directly with the error, Nuxt.js will call the `error` method for you:
|
||||
```js
|
||||
export default {
|
||||
data ({ params }, callback) {
|
||||
axios.get(`https://my-api/posts/${params.id}`)
|
||||
.then((res) => {
|
||||
callback(null, { title: res.data.title })
|
||||
})
|
||||
.catch((e) => {
|
||||
callback({ statusCode: 404, message: 'Post not found' })
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
@ -50,4 +101,4 @@ npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Go to [http://localhost:3000](http://localhost:3000) and navigate inside the app.
|
||||
Go to [http://localhost:3000](http://localhost:3000) and navigate trough the app.
|
||||
|
@ -1,21 +1,20 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ userAgent }}!</p>
|
||||
<p>{{ userAgent }}</p>
|
||||
<p><router-link to="/post">See a post (http request / Ajax)</router-link></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data ({ req }) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(function () {
|
||||
resolve({
|
||||
userAgent: (req ? req.headers['user-agent'] : navigator.userAgent)
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
data ({ req }, callback) {
|
||||
setTimeout(function () {
|
||||
// callback(err, data)
|
||||
callback(null, {
|
||||
userAgent: (req ? req.headers['user-agent'] : navigator.userAgent)
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -11,10 +11,16 @@ const axios = require('axios')
|
||||
|
||||
export default {
|
||||
data ({ req }) {
|
||||
// We can return a Promise instead of calling the callback
|
||||
return axios.get('https://jsonplaceholder.typicode.com/posts/1')
|
||||
.then((res) => {
|
||||
return { post: res.data }
|
||||
})
|
||||
},
|
||||
head () {
|
||||
return {
|
||||
title: this.post.title
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
5
lib/app/App-new.vue
Normal file
5
lib/app/App-new.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<nuxt-container>
|
||||
<nuxt></nuxt>
|
||||
</nuxt-container>
|
||||
</template>
|
@ -29,8 +29,9 @@ export default {
|
||||
err = err || null
|
||||
this.err = err || null
|
||||
<% if (loading) { %>
|
||||
if (this.err && this.$loading && this.$loading.fail) {
|
||||
this.$loading.fail()
|
||||
if (this.err && this.$loading) {
|
||||
if (this.$loading.fail) this.$loading.fail()
|
||||
if (this.$loading.finish) this.$loading.finish()
|
||||
}
|
||||
<% } %>
|
||||
return this.err
|
||||
|
@ -4,7 +4,7 @@ require('es6-object-assign').polyfill()
|
||||
import 'es6-promise/auto'
|
||||
import Vue from 'vue'
|
||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
||||
import { getMatchedComponents, flatMapComponents, getContext, getLocation } from './utils'
|
||||
import { getMatchedComponents, flatMapComponents, getContext, promisify, getLocation } from './utils'
|
||||
const noopData = () => { return {} }
|
||||
const noopFetch = () => {}
|
||||
|
||||
@ -78,8 +78,7 @@ function render (to, ___, next) {
|
||||
}
|
||||
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
|
||||
if (Component._data && typeof Component._data === 'function') {
|
||||
var promise = Component._data(context)
|
||||
if (!(promise instanceof Promise)) promise = Promise.resolve(promise)
|
||||
var promise = promisify(Component._data, context)
|
||||
promise.then((data) => {
|
||||
Component.options.data = () => data || {}
|
||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
||||
@ -117,6 +116,11 @@ function hotReloadAPI (_app) {
|
||||
_app.$forceUpdate = function () {
|
||||
let Component = getMatchedComponents(router.currentRoute)[0]
|
||||
if (!Component) return _forceUpdate()
|
||||
if (typeof Component === 'object' && !Component.options) {
|
||||
// Updated via vue-router resolveAsyncComponents()
|
||||
Component = Vue.extend(Component)
|
||||
Component._Ctor = Component
|
||||
}
|
||||
<%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %>
|
||||
let promises = []
|
||||
const next = function (path) {
|
||||
@ -129,8 +133,7 @@ function hotReloadAPI (_app) {
|
||||
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '')
|
||||
if (originalDataFn !== newDataFn) {
|
||||
Component._data = Component._Ctor.options.data || noopData
|
||||
let p = Component._data(context)
|
||||
if (!(p instanceof Promise)) { p = Promise.resolve(p) }
|
||||
let p = promisify(Component._data, context)
|
||||
p.then((data) => {
|
||||
Component.options.data = () => data || {}
|
||||
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
|
||||
|
16
lib/app/components/nuxt-container.vue
Normal file
16
lib/app/components/nuxt-container.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div id="__nuxt">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'nuxt-container',
|
||||
head: <%= JSON.stringify(head) %>
|
||||
}
|
||||
</script>
|
||||
|
||||
<% css.forEach(function (c) { %>
|
||||
<style src="<%= (typeof c === 'string' ? c : c.src) %>" lang="<%= (c.lang ? c.lang : 'css') %>"></style>
|
||||
<% }) %>
|
@ -52,7 +52,7 @@ export default {
|
||||
this.percent = this.percent + Math.floor(num)
|
||||
return this
|
||||
},
|
||||
decrease () {
|
||||
decrease (num) {
|
||||
this.percent = this.percent - Math.floor(num)
|
||||
return this
|
||||
},
|
||||
@ -80,8 +80,6 @@ export default {
|
||||
},
|
||||
fail () {
|
||||
this.canSuccess = false
|
||||
this.percent = 100
|
||||
this.hide()
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
59
lib/app/components/nuxt.vue
Normal file
59
lib/app/components/nuxt.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<% if (loading) { %><nuxt-loading ref="loading"></nuxt-loading><% } %>
|
||||
<transition>
|
||||
<router-view v-if="!err"></router-view>
|
||||
<nuxt-error v-if="err" :error="err"></nuxt-error>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NuxtError from '<%= components.ErrorPage %>'
|
||||
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./nuxt-loading.vue") %>'<% } %>
|
||||
|
||||
export default {
|
||||
name: 'nuxt',
|
||||
data () {
|
||||
return {
|
||||
err: null
|
||||
}
|
||||
},
|
||||
<% if (loading) { %>
|
||||
created () {
|
||||
if (this.$root.$nuxt) {
|
||||
return console.error('Only one instance of Nuxt.js is possible, make sure to use <nuxt></nuxt> only once.')
|
||||
}
|
||||
// Add $nuxt in the root instance
|
||||
this.$root.$nuxt = this
|
||||
// add vm.$nuxt to child instances
|
||||
Vue.prototype.$nuxt = this
|
||||
// add to window so we can listen when ready
|
||||
if (typeof window !== 'undefined') {
|
||||
window.$nuxt = this
|
||||
}
|
||||
// for NUXT.serverRendered = false
|
||||
this.$loading = {}
|
||||
},
|
||||
mounted () {
|
||||
this.$loading = this.$refs.loading
|
||||
},
|
||||
<% } %>
|
||||
methods: {
|
||||
error (err) {
|
||||
err = err || null
|
||||
this.err = err || null
|
||||
<% if (loading) { %>
|
||||
if (this.err && this.$loading) {
|
||||
if (this.$loading.fail) this.$loading.fail()
|
||||
if (this.$loading.finish) this.$loading.finish()
|
||||
}
|
||||
<% } %>
|
||||
return this.err
|
||||
}
|
||||
},
|
||||
components: {
|
||||
NuxtError<%= (loading ? ',\n\t\tNuxtLoading' : '') %>
|
||||
}
|
||||
}
|
||||
</script>
|
@ -7,6 +7,12 @@ import Meta from 'vue-meta/lib/vue-meta.js' // require the ES2015 lib
|
||||
import router from './router.js'
|
||||
<% if (store) { %>import store from '~store/index.js'<% } %>
|
||||
|
||||
import NuxtContainer from './components/nuxt-container.vue'
|
||||
import Nuxt from './components/nuxt.vue'
|
||||
|
||||
Vue.component(NuxtContainer.name, NuxtContainer)
|
||||
Vue.component(Nuxt.name, Nuxt)
|
||||
|
||||
Vue.use(Meta, {
|
||||
keyName: 'head', // the component option name that vue-meta looks for meta info on.
|
||||
attribute: 'n-head', // the attribute name vue-meta adds to the tags it observes
|
||||
@ -18,7 +24,7 @@ Vue.use(Meta, {
|
||||
require('<%= pluginPath %>')
|
||||
<% }) %>
|
||||
|
||||
import App from './App.vue'
|
||||
import App from '<%= appPath %>'
|
||||
// create the app instance.
|
||||
// here we inject the router and store to all child components,
|
||||
// making them available everywhere as `this.$router` and `this.$store`.
|
||||
|
@ -5,7 +5,7 @@ import Vue from 'vue'
|
||||
import { stringify } from 'querystring'
|
||||
import { omit } from 'lodash'
|
||||
import { app, router<%= (store ? ', store' : '') %> } from './index'
|
||||
import { getMatchedComponents, getContext, urlJoin } from './utils'
|
||||
import { getMatchedComponents, getContext, promisify, urlJoin } from './utils'
|
||||
|
||||
const isDev = <%= isDev %>
|
||||
const _app = new Vue(app)
|
||||
@ -68,10 +68,10 @@ export default context => {
|
||||
Component._Ctor = Component
|
||||
Component.extendOptions = Component.options
|
||||
}
|
||||
const ctx = getContext(context)
|
||||
if (Component.options.data && typeof Component.options.data === 'function') {
|
||||
Component._data = Component.options.data
|
||||
var promise = Component._data(getContext(context))
|
||||
if (!(promise instanceof Promise)) promise = Promise.resolve(promise)
|
||||
let promise = promisify(Component._data, ctx)
|
||||
promise.then((data) => {
|
||||
Component.options.data = () => data
|
||||
Component._Ctor.options.data = Component.options.data
|
||||
@ -81,7 +81,7 @@ export default context => {
|
||||
promises.push(null)
|
||||
}
|
||||
if (Component.options.fetch) {
|
||||
promises.push(Component.options.fetch(getContext(context)))
|
||||
promises.push(Component.options.fetch(ctx))
|
||||
}
|
||||
return Promise.all(promises)
|
||||
}))
|
||||
|
@ -20,6 +20,7 @@ export function getContext (context) {
|
||||
let ctx = {
|
||||
isServer: !!context.isServer,
|
||||
isClient: !!context.isClient,
|
||||
isDev: <%= isDev %>,
|
||||
<%= (store ? 'store: context.store,' : '') %>
|
||||
route: (context.to ? context.to : context.route),
|
||||
error: context.error
|
||||
@ -45,6 +46,28 @@ export function getContext (context) {
|
||||
return ctx
|
||||
}
|
||||
|
||||
export function promisify (fn, context) {
|
||||
let promise
|
||||
if (fn.length === 2) {
|
||||
// fn(context, callback)
|
||||
promise = new Promise((resolve) => {
|
||||
fn(getContext(context), function (err, data) {
|
||||
if (err) {
|
||||
context.error(err)
|
||||
}
|
||||
data = data || {}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
promise = fn(getContext(context))
|
||||
}
|
||||
if (!(promise instanceof Promise)) {
|
||||
promise = Promise.resolve(promise)
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
||||
// Imported from vue-router
|
||||
export function getLocation (base) {
|
||||
var path = window.location.pathname
|
||||
|
@ -146,6 +146,8 @@ function * generateRoutesAndFiles () {
|
||||
'router.js',
|
||||
'server.js',
|
||||
'utils.js',
|
||||
'components/nuxt-container.vue',
|
||||
'components/nuxt.vue',
|
||||
'components/nuxt-loading.vue'
|
||||
]
|
||||
let templateVars = {
|
||||
@ -159,6 +161,7 @@ function * generateRoutesAndFiles () {
|
||||
store: this.options.store,
|
||||
css: this.options.css,
|
||||
plugins: this.options.plugins.map((p) => r(this.dir, p)),
|
||||
appPath: './App.vue',
|
||||
loading: (typeof this.options.loading === 'string' ? r(this.dir, this.options.loading) : this.options.loading),
|
||||
components: {
|
||||
Loading: r(__dirname, '..', 'app', 'components', 'nuxt-loading.vue'),
|
||||
@ -175,6 +178,9 @@ function * generateRoutesAndFiles () {
|
||||
route._name = '_' + hash(route._component)
|
||||
route.component = route._name
|
||||
})
|
||||
if (files.includes('pages/_app.vue')) {
|
||||
templateVars.appPath = r(this.dir, 'pages/_app.vue')
|
||||
}
|
||||
if (this.dev && files.includes('pages/_error-debug.vue')) {
|
||||
templateVars.components.ErrorPage = r(this.dir, 'pages/_error-debug.vue')
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user