You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

87 lines
1.9 KiB

4 weeks ago
  1. import Vue from 'vue'
  2. import { hasFetch, normalizeError, addLifecycleHook } from '../utils'
  3. const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey
  4. const nuxtState = window.__NUXT__
  5. export default {
  6. beforeCreate () {
  7. if (!hasFetch(this)) {
  8. return
  9. }
  10. this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200
  11. Vue.util.defineReactive(this, '$fetchState', {
  12. pending: false,
  13. error: null,
  14. timestamp: Date.now()
  15. })
  16. this.$fetch = $fetch.bind(this)
  17. addLifecycleHook(this, 'created', created)
  18. addLifecycleHook(this, 'beforeMount', beforeMount)
  19. }
  20. }
  21. function beforeMount() {
  22. if (!this._hydrated) {
  23. return this.$fetch()
  24. }
  25. }
  26. function created() {
  27. if (!isSsrHydration(this)) {
  28. return
  29. }
  30. // Hydrate component
  31. this._hydrated = true
  32. this._fetchKey = +this.$vnode.elm.dataset.fetchKey
  33. const data = nuxtState.fetch[this._fetchKey]
  34. // If fetch error
  35. if (data && data._error) {
  36. this.$fetchState.error = data._error
  37. return
  38. }
  39. // Merge data
  40. for (const key in data) {
  41. Vue.set(this.$data, key, data[key])
  42. }
  43. }
  44. function $fetch() {
  45. if (!this._fetchPromise) {
  46. this._fetchPromise = $_fetch.call(this)
  47. .then(() => { delete this._fetchPromise })
  48. }
  49. return this._fetchPromise
  50. }
  51. async function $_fetch() {
  52. this.$nuxt.nbFetching++
  53. this.$fetchState.pending = true
  54. this.$fetchState.error = null
  55. this._hydrated = false
  56. let error = null
  57. const startTime = Date.now()
  58. try {
  59. await this.$options.fetch.call(this)
  60. } catch (err) {
  61. error = normalizeError(err)
  62. }
  63. const delayLeft = this._fetchDelay - (Date.now() - startTime)
  64. if (delayLeft > 0) {
  65. await new Promise(resolve => setTimeout(resolve, delayLeft))
  66. }
  67. this.$fetchState.error = error
  68. this.$fetchState.pending = false
  69. this.$fetchState.timestamp = Date.now()
  70. this.$nextTick(() => this.$nuxt.nbFetching--)
  71. }