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

Posted

技术标签:

【中文标题】NuxtJS / Vuex | nuxtServerInit 和 fetchData 操作未填充用户状态【英文标题】:NuxtJS / Vuex | nuxtServerInit and fetchData action not filling user on state 【发布时间】:2019-11-27 22:54:12 【问题描述】:

使用 NodeJS、Express 和 MongoDB 构建 API,使用 JWT 和 Cookies 进行用户身份验证。 使用 store (vuex) 使用 axios 服务从 API 获取用户数据。在 store 文件夹中创建了 auth.js,创建了 fetchData 操作,该操作从后端 (axios.get(apiRoute)) 获取数据并将用户设置为状态。 想使用 nuxtServerInit 执行此操作,所以我在 store 文件夹中创建了 index.js 文件。添加了空状态和操作。 Action 包含 nuxtServerInit,它使用 dispatch() 调用 auth.js 中的 fetchData 方法。

然而,在这一切之后,它根本不起作用。例如:用户已登录,但帐户页面未使用用户数据(姓名、电子邮件、图像等)呈现。

我尝试从 auth.js 中的 fetchData 操作返回一个承诺,但没有成功。 我还尝试在 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)"  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 时,应用会正确呈现用户数据。

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>

【问题讨论】:

您是否检查过 fetchData 是否正在抛出,并调用了 commit('RESET_USER')?如果一切正常,您能否发布一个使用数据的组件示例? 是的,commit('RESET_USER') 被正确调用,因为每次在 index.js 上使用 dispatch 方法调用 fetchData 时都会抛出错误。使用用户数据使用组件更新帖子。 来自文档:'注意:异步 nuxtServerInit 操作必须返回一个 Promise 或利用 async/await 以允许 nuxt 服务器等待它们。 nuxtServerInit 中的await dispatch('auth/fetchData'); 有帮助吗? @c6p 已经尝试过异步等待方法,但没有运气。不会以任何方式影响它。 【参考方案1】:

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

这导致了本质上是无限递归。 nuxtServerInit 调用 http://localhost:3000,它会访问同一台服务器,再次运行 nuxtServerInit,然后调用 http://localhost:3000,依此类推,直到 javascript 堆内存不足。

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

fetch方法用于在渲染页面之前填充store, 它就像 asyncData 方法,只是它不设置组件 数据。

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

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

作为一般规则:

使用 fetch 填充存储在服务器或客户端上的数据 在服务器或客户端使用 asyncData 填充组件数据 将 nuxtServerInit 用于使用请求对象上的值(如会话、标头、cookie 等)设置存储,这仅在服务器端需要

【讨论】:

谢谢!这有点工作。现在的主要问题是,当我刷新页面时,数据消失了。是否有解决此问题的方法?虽然一旦我使用 nuxt-link 导航到其他页面,数据就会重新出现。 这是因为 Nuxt 默认将所有内容存储在内存中。要解决此问题,请使用 vuex-persistedstate 之类的包。不过这个包只会持久化 Vuex 数据,不会保存组件数据。【参考方案2】:
    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 

【讨论】:

【参考方案3】:

我想你在发送前忘了写await

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

【讨论】:

以上是关于NuxtJS / Vuex | nuxtServerInit 和 fetchData 操作未填充用户状态的主要内容,如果未能解决你的问题,请参考以下文章

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

Vuex 模块 CORS 错误中的 Nuxtjs Axios

nuxtjs中修改head及vuex的使用

nuxtServerInit 与 vuex-module-decorators

在从本地存储恢复 Vuex Store 之前执行的中间件

Vue/Nuxt:如何在 vuex 商店中访问 Nuxt 实例?