App.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import Vue from 'vue'
  2. import { decode, parsePath, withoutBase, withoutTrailingSlash, normalizeURL } from 'ufo'
  3. import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError, urlJoin, sanitizeComponent } from './utils'
  4. import NuxtError from './components/nuxt-error.vue'
  5. import NuxtLoading from './components/nuxt-loading.vue'
  6. import '..\\..\\..\\assets\\css\\normalize.css'
  7. import '..\\..\\..\\assets\\css\\base.scss'
  8. import _6f6c098b from '..\\..\\..\\layouts\\default.vue'
  9. const layouts = { "_default": sanitizeComponent(_6f6c098b) }
  10. export default {
  11. render (h, props) {
  12. const loadingEl = h('NuxtLoading', { ref: 'loading' })
  13. const layoutEl = h(this.layout || 'nuxt')
  14. const templateEl = h('div', {
  15. domProps: {
  16. id: '__layout'
  17. },
  18. key: this.layoutName
  19. }, [layoutEl])
  20. const transitionEl = h('transition', {
  21. props: {
  22. name: 'layout',
  23. mode: 'out-in'
  24. },
  25. on: {
  26. beforeEnter (el) {
  27. // Ensure to trigger scroll event after calling scrollBehavior
  28. window.$nuxt.$nextTick(() => {
  29. window.$nuxt.$emit('triggerScroll')
  30. })
  31. }
  32. }
  33. }, [templateEl])
  34. return h('div', {
  35. domProps: {
  36. id: '__nuxt'
  37. }
  38. }, [
  39. loadingEl,
  40. transitionEl
  41. ])
  42. },
  43. data: () => ({
  44. isOnline: true,
  45. layout: null,
  46. layoutName: '',
  47. nbFetching: 0
  48. }),
  49. beforeCreate () {
  50. Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
  51. },
  52. created () {
  53. // Add this.$nuxt in child instances
  54. this.$root.$options.$nuxt = this
  55. if (process.client) {
  56. // add to window so we can listen when ready
  57. window.$nuxt = this
  58. this.refreshOnlineStatus()
  59. // Setup the listeners
  60. window.addEventListener('online', this.refreshOnlineStatus)
  61. window.addEventListener('offline', this.refreshOnlineStatus)
  62. }
  63. // Add $nuxt.error()
  64. this.error = this.nuxt.error
  65. // Add $nuxt.context
  66. this.context = this.$options.context
  67. },
  68. async mounted () {
  69. this.$loading = this.$refs.loading
  70. if (this.isPreview) {
  71. if (this.$store && this.$store._actions.nuxtServerInit) {
  72. this.$loading.start()
  73. await this.$store.dispatch('nuxtServerInit', this.context)
  74. }
  75. await this.refresh()
  76. this.$loading.finish()
  77. }
  78. },
  79. watch: {
  80. 'nuxt.err': 'errorChanged'
  81. },
  82. computed: {
  83. isOffline () {
  84. return !this.isOnline
  85. },
  86. isFetching () {
  87. return this.nbFetching > 0
  88. },
  89. isPreview () {
  90. return Boolean(this.$options.previewData)
  91. },
  92. },
  93. methods: {
  94. refreshOnlineStatus () {
  95. if (process.client) {
  96. if (typeof window.navigator.onLine === 'undefined') {
  97. // If the browser doesn't support connection status reports
  98. // assume that we are online because most apps' only react
  99. // when they now that the connection has been interrupted
  100. this.isOnline = true
  101. } else {
  102. this.isOnline = window.navigator.onLine
  103. }
  104. }
  105. },
  106. async refresh () {
  107. const pages = getMatchedComponentsInstances(this.$route)
  108. if (!pages.length) {
  109. return
  110. }
  111. this.$loading.start()
  112. const promises = pages.map((page) => {
  113. const p = []
  114. // Old fetch
  115. if (page.$options.fetch && page.$options.fetch.length) {
  116. p.push(promisify(page.$options.fetch, this.context))
  117. }
  118. if (page.$fetch) {
  119. p.push(page.$fetch())
  120. } else {
  121. // Get all component instance to call $fetch
  122. for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) {
  123. p.push(component.$fetch())
  124. }
  125. }
  126. if (page.$options.asyncData) {
  127. p.push(
  128. promisify(page.$options.asyncData, this.context)
  129. .then((newData) => {
  130. for (const key in newData) {
  131. Vue.set(page.$data, key, newData[key])
  132. }
  133. })
  134. )
  135. }
  136. return Promise.all(p)
  137. })
  138. try {
  139. await Promise.all(promises)
  140. } catch (error) {
  141. this.$loading.fail(error)
  142. globalHandleError(error)
  143. this.error(error)
  144. }
  145. this.$loading.finish()
  146. },
  147. errorChanged () {
  148. if (this.nuxt.err) {
  149. if (this.$loading) {
  150. if (this.$loading.fail) {
  151. this.$loading.fail(this.nuxt.err)
  152. }
  153. if (this.$loading.finish) {
  154. this.$loading.finish()
  155. }
  156. }
  157. let errorLayout = (NuxtError.options || NuxtError).layout;
  158. if (typeof errorLayout === 'function') {
  159. errorLayout = errorLayout(this.context)
  160. }
  161. this.setLayout(errorLayout)
  162. }
  163. },
  164. setLayout (layout) {
  165. if (!layout || !layouts['_' + layout]) {
  166. layout = 'default'
  167. }
  168. this.layoutName = layout
  169. this.layout = layouts['_' + layout]
  170. return this.layout
  171. },
  172. loadLayout (layout) {
  173. if (!layout || !layouts['_' + layout]) {
  174. layout = 'default'
  175. }
  176. return Promise.resolve(layouts['_' + layout])
  177. },
  178. getRouterBase() {
  179. return withoutTrailingSlash(this.$router.options.base)
  180. },
  181. getRoutePath(route = '/') {
  182. const base = this.getRouterBase()
  183. return withoutTrailingSlash(withoutBase(parsePath(route).pathname, base))
  184. },
  185. getStaticAssetsPath(route = '/') {
  186. const { staticAssetsBase } = window.__NUXT__
  187. return urlJoin(staticAssetsBase, this.getRoutePath(route))
  188. },
  189. async fetchStaticManifest() {
  190. return window.__NUXT_IMPORT__('manifest.js', normalizeURL(urlJoin(this.getStaticAssetsPath(), 'manifest.js')))
  191. },
  192. setPagePayload(payload) {
  193. this._pagePayload = payload
  194. this._fetchCounters = {}
  195. },
  196. async fetchPayload(route, prefetch) {
  197. const path = decode(this.getRoutePath(route))
  198. const manifest = await this.fetchStaticManifest()
  199. if (!manifest.routes.includes(path)) {
  200. if (!prefetch) { this.setPagePayload(false) }
  201. throw new Error(`Route ${path} is not pre-rendered`)
  202. }
  203. const src = urlJoin(this.getStaticAssetsPath(route), 'payload.js')
  204. try {
  205. const payload = await window.__NUXT_IMPORT__(path, normalizeURL(src))
  206. if (!prefetch) { this.setPagePayload(payload) }
  207. return payload
  208. } catch (err) {
  209. if (!prefetch) { this.setPagePayload(false) }
  210. throw err
  211. }
  212. }
  213. },
  214. components: {
  215. NuxtLoading
  216. }
  217. }