pexea12
pexea12

Reputation: 1209

Component in Vue.js server-side rendering

I am trying to make my Vue app have server-side rendering. I am using vue-server-renderer (https://www.npmjs.com/package/vue-server-renderer). Client-side rendering is working fine.

My app use vue-router and axios

Here is my server.js:

server.get('*', (request, response) => {
  bundleRenderer.renderToString({ url: request.url }, (error, htmlPromise) => {
    if (error) {
      // Log the error in the console
      console.error(error)
      // Tell the client something went wrong
      return response
        .status(500)
        .send(error)
    }
    response.send(layout.replace('<div id=app></div>', htmlPromise))
  })
})

getInfo() is the method to fetch server data.

Here is getInfo():

export default {
  methods: {
    getInfo(api) {
        return axios
          .get(api || this.$route.params.path)
          .then((data) => {
            this.data = data
            this.$set(this, 'isLoading', false)
          })
    },
  },
}

My server entry is:

import { app, router, store } from './index'

export default context => {

  let componentPromises = router.getMatchedComponents().filter((component) => {
    return component.methods && component.methods.getInfo
  }).map((component) => {
    return component.methods.getInfo()
  })

  return Promise.all(componentPromises).then(() => {
    return app
  })
}

However, I soon realize that all the components from router.getMatchedComponents() does not have $route or $set. Therefore, the method getInfo() stops working.

The document from https://router.vuejs.org/en/api/router-instance.html is very short and does not provide much information:

router.getMatchedComponents()

Returns an Array of the components (definition/constructor, not instances) matched by the current route. This is mostly used during server-side rendering to perform data prefetching.

How can I fix the problem?

Upvotes: 1

Views: 2955

Answers (1)

Davide
Davide

Reputation: 564

I have previously incurred into a similar problem and managed to successfully prefetch data by doing the following:

app.$router.onReady(() => {
   const matchedComponents = app.$router.getMatchedComponents()

   if (!matchedComponents.length) { /* ... */}

   Promise.all(matchedComponents.map((Component: any) => {
     if (Component.options.methods.asyncData) {
       return Component.options.methods.asyncData({
         store: app.$store,
         route: app.$router.currentRoute
       });
     }
   })).then(() => { /* your callback here ... */ });
}

According to vue ssr documentation (https://ssr.vuejs.org/en/data.html) the suggested way is to use a custom asyncData method in your component to perform data fetching rather than calling component methods directly:

export default {
   asyncData ({ store, route }) {
      // return the Promise from the action
      return store.dispatch('fetchItem', route.params.id)
   }
},

Upvotes: 0

Related Questions