NuxtJS / Vuex | nuxtServerInit和fetchData操作未按状态填充用户

时间:2019-07-19 00:21:53

标签: javascript node.js vue.js vuex nuxt.js

带有NodeJS,Express和MongoDB的内置API,使用JWT和Cookies进行用户身份验证。 使用商店(vuex)从具有axios服务的API中获取用户数据。在商店文件夹中创建了auth.js,创建了fetchData操作,该操作从后端(axios.get(apiRoute))获取数据并将用户设置为状态。 想要使用nuxtServerInit做到这一点,所以我在存储文件夹中创建了index.js文件。添加了空状态和动作。动作包含nuxtServerInit,它使用dispatch()来调用auth.js中的fetchData方法。

毕竟,它根本不起作用。例如:用户已登录,但帐户页面未使用用户数据(名称,电子邮件,图像等)呈现。

我尝试从auth.js中的fetchData操作返回一个Promise,但它不起作用。 我也尝试在index.js文件的现场设置fetchData操作并直接在其上调用dispatch。

store / auth.js

// Importing Files
import axios from 'axios';

// State
export const state = () => ({
    user: null
});

// Mutations
export const mutations = {
    SET_USER (store, data) {
        store.user = data
    },
    RESET_USER (store) {
        store.user = null
    }
};

// Actions
export const actions = {
    // Fetch User Account
    async fetchData ({ commit }) {
        try {
           const response = await axios.get('http://localhost:3000/api/v1/users/account');
            commit('SET_USER', response.data.doc);
            return response;
        } catch (err) {
            commit('RESET_USER');
        }
    }
};

store / index.js

// State
export const state = () => ({

});

// Actions
export const actions = {
    async nuxtServerInit({ dispatch }) {
        console.log('Testing');
        const res = dispatch('auth/fetchData');
        return res;
    }
};

components / Settings.vue

<template>
  <section class="data-block-wrap" v-if="user">
     <BlockHeader :blockHeaderName="`Welcome Back, ${user.name.split(' ')[0]}`" btnText="More Details" />
     <img :src="getPhotoUrl(user.photo)" alt="User Photo" class="user-data__image">
     <p class="user-data__short-bio">{{ user.shortBio }}</p>
  </section>
</template>

<script>
 export default {
    // Computed
    computed: {
        user() {
            return this.$store.state.auth.user;
        }
    }
    ...
 };
</script>

我希望可以在Vue组件上正确呈现用户数据,但目前根本无法正常工作。渲染是静态的,没有显示来自数据库/ api的数据。

编辑/更新

在default.vue文件(所有组件的“父级”文件)的created()钩子上调用fetchData时,App会正确呈现用户数据。

default.vue

<template>
  <div class="container">
    <TopNav />
    <SideNav />
    <nuxt />
  </div>
</template>

// Importing Components
import TopNav from '@/components/navigation/TopNav';
import SideNav from '@/components/navigation/SideNav';
import axios from 'axios';

import { mapActions } from 'vuex';

export default {
  components: {
    TopNav,
    SideNav
  },
  methods: {
  // Map Actions
  ...mapActions('auth', ['fetchData']),
    async checkUser() {
      const user = await this.fetchData();
    },
  },
   // Lifecycle Method - Created
   created() {
    this.checkUser();
  }
}
</script>

2 个答案:

答案 0 :(得分:1)

这里似乎发生了一些非常有趣的事情。问题是从nuxtServerInit()内部调用axios.get('http://localhost:3000/api/v1/users/account')

这实际上导致了无限递归。 nuxtServerInit会对http://localhost:3000进行调用,该击中相同的服务器,再次运行nuxtServerInit,然后调用http://localhost:3000,依此类推,直到JavaScript堆内存不足为止。

为此,请使用fetch方法,而不是为此使用nuxtServerInit:

  

fetch方法用于在呈现页面之前填充商店,   就像asyncData方法一样,除了它没有设置组件   数据。

注意:您无法访问fetch中的Nuxt组件,因此必须使用context对象而不是“ this”

// inside your page component
export default {
  fetch (context) {
    return context.store.dispatch('auth/fetchData');
  }
}

一般规则:

  • 使用访存来填充服务器或客户端上的存储数据
  • 使用asyncData填充服务器或客户端上的组件数据
  • 使用nuxtServerInit进行诸如在请求对象上使用值设置商店的操作,例如会话,标头,Cookie等,这仅是服务器端所必需的

答案 1 :(得分:1)

    The solution to this question is to use the NuxtServerInt Action this way inside your store.js
    
    1. you will need to run  npm install cookieparser and npm install js-cookie
    
    const cookieparser = process.server ? require('cookieparser') : undefined
    
    export const state = () => {
      return {
        auth: null,
      }
    }
    export const mutations = {
      SET_AUTH(state, auth) {
        state.auth = auth
      },
     
    }
    export const actions = {
      nuxtServerInit({ commit }, { req }) {
        let auth = null
        if (req.headers.cookie) {
          try {
            const parsed = cookieparser.parse(req.headers.cookie)
            auth = parsed.auth
          } catch (err) {
            console.log('error', err)
          }
        }
        commit('SET_AUTH', auth)
      },
    }


Then in your login page component, you call your backend API, just like this 

import AuthServices from '@/ApiServices/AuthServices.js'
import swal from 'sweetalert'
const Cookie = process.client ? require('js-cookie') : undefined

 async onSubmit() {
      try {
        
        const body = {
          email: this.email,
          password: this.password,
        }

        const res = await AuthServices.loginUrl(body)
        console.log('res', res)
        console.log('res', res.data.message)
        setTimeout(() => {
          // we simulate the async request with timeout.
          const auth = {
            accessToken: res.data.payload.token, // from your api call, you get the user token 
            userData: res.data.payload.user,
          }
          swal('Logged in', `${res.data.message}`, 'success')

          this.email = this.password = ''

          this.$refs.loginForm.reset()
          this.$store.commit('setAuth', auth) // mutating to store for client rendering
          Cookie.set('auth', auth) // saving token in cookie for server rendering
          this.$router.push('/')
        }, 1000)
      } catch (error) {
        console.log('error', error)
        swal('Error!', `${error.message}`, 'error')
      }
    },


your AuthServices.js looks like this

import axios from 'axios'

const apiClient = axios.create({
  baseURL: `http://localhost:3000`,
})

export default {
  loginUrl(body) {
    return apiClient.post('/login', body, {
      headers: {
        'Content-Type': 'application/json',
      },
    })
  }
}




then you get the user data using computed in the navbar or say dashboard e.g to say Hi,Xavier

inside where you want place the user data, just add this
<template>
  <section>
     <p class="firtname_data">Hi, {{ user.firstnam }}</p>
  </section>
</template>

<script>
 export default {
    // Computed
    computed: {
    user() {
      return this.$store.state.auth.userData
    }
    ...
 };
</script>



Hope this help... it worked for me