Vue JS 服务器端渲染:计算 属性 未在商店中看到更改

VueJS sever-side-rendering: computed property not seeing changes in store

我正在尝试让服务器端渲染在 VueJS 中工作。

我一直在关注官方文档,并试图让这个示例使用 axios 工作。端点正确,数据确实显示在 mutation.

https://ssr.vuejs.org/guide/data.html

我也找到了这个页面并尝试了大部分示例,包括使用 getters、vuex mapState、mapGetter 等:

这里是store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import { fetchPage } from './api'

export function createStore () {
    return new Vuex.Store({
        strict: true,

        state: () => ({
            res: {}
        }),

        actions: {
            actXY ({ commit }, page) {
                fetchPage('homepage').then((item) => {
                    commit('mutXY', { page, item });
                }).catch(error => {
                    throw new Error(`Store ${error}`);

                })
            }
        },

        mutations: {
            mutXY (state, { page, item }) {
                Vue.set(state.res, page, item);
                console.log(state.res.homepage.data)

            }
        },

        getters: {
            getXY (state) {
                return state.res
            }
        }
    })
}

控制台日志显示正确的信息,这意味着 axios 端点正在工作并更新状态对象。最初,我试图调用 mutation,但后来我尝试调用 getter。不幸的是,getter 中没有更新新数据,因此组件中当然不会显示任何新数据。

这是HomePage.vue:

<template>
  <div v-if="htmlData">{{ JSON.stringify(htmlData) }}</div>
  <div v-else>loading?</div>
</template>

<script>
export default ({
    name: 'homepage',

    computed: {
        htmlData () {
            console.log("computed = " + JSON.stringify(this.$store.state.res));
            console.log(this.$store.getters.getXY);
            return this.$store.getters
            // return this.getQuery()
        }
    },

    serverPrefetch () {
        return this.getData()
    },

    mounted () {
        console.log("in mounted")
        if (!this.item) {
            this.getData()
        }
    },

    methods: {
        getData () {
            return this.$store.dispatch('actXY', 'homepage')
        }
    }
})

</script>

如所写,htmlData 将显示:

{"getXY":{}}

是的,我确实尝试了很多关于如何 return getter, 中的商店项目的其他变体,但没有任何效果。

除了上述链接中的内容外,我还四处寻找配置文件的变体,我尝试将 async 添加到 store/actions、getData() 等

我也尝试过直接在组件中调用axios,但是没有成功。

由于这是一个已经在 VueJS 中或多或少完成的项目,我正在将其转换为 SSR,因此我从 package.json 中删除了所有内容并重新安装了每个包,希望其中之一旧的 vue 包引起了冲突。

我还尝试了从官方文档中拆分商店代码,并尝试了如何编写路由的变体。什么都没用。

我想我应该添加当我 运行 当前代码时打印语句显示的内容:

computed = undefined
computed mm  = undefined
getter = {}
computed get  = {"getXY":{}}
{
  title: 'Home'...}

组件 运行s 中的计算 属性 在 设置突变之前。这导致 getter 在突变更新之前被调用。同样,如果我试图直接从组件调用对 res 的更改,则在调用和呈现商店时不会发生任何变化。

这是我正在尝试 运行 的代码的回购:https://github.com/dt1/vue-ssr-attempt

(我找到了答案。我已经用适合我的方法更新了存储库,下面是对我的解决方案的解释)

第一个storeData()=>storeData(state)

然后 return this.$store.state.items.homepage.data => return this.$store.state.items.homepage && return this.$store.state.items.homepage.data(或用空主页初始化状态项)

Vue.jsout of the box中没有SSR。 在您的示例中,您可以在客户端获取数据。

试试这个商店的例子:

export default new Vuex.Store({
  strict: true,
  state: {
    res: null
  },
  getters: {
    getXY(state) {
      return state.res;
    }
  },
  mutations: {
    mutXY(state, payload) {
      state.res = payload;
    }
  },
  actions: {
    actXY: function(store) {
      fetchPage("homepage")
        .then(res => {
          store.commit("mutXY", res);
        })
        .catch(error => {
          throw new Error(`Store ${error}`);
        });
    }
  }
});

并且您可以这样在组件中获取数据:

  computed: {
    htmlData() {
      return this.$store.getters.getXY;
    }
  },
  created() {
    this.getData();
  },
  methods: {
    getData() {
      return this.$store.dispatch("actXY", "homepage");
    }
  }

我的问题是为什么this.getData()在mounted中被调用?

如果我正确理解了这个过程,当您调用服务器端渲染时,您试图在安装 vue 组件之前渲染 HTML。如果您在 created 而不是 mounted 中调用 this.getData() 会起作用吗?

我会尝试的其他事情:

  1. 在组件中硬编码 htmlData 值以检查它是否会呈现
  2. 如果可行,硬编码存储中的 htmlData 值并检查组件是否可以获取该存储数据并正确呈现
  3. 在那之后也可以工作,通过从组件调度从服务器获取 htmlData,看看它是否工作。
  4. 如果 (3) 不起作用,请尝试将 htmlData 推送到子组件 仅当您已经准备好 htmlData 时才创建,发送 htmlData 通过 props
  5. 传给子组件

好吧,我终于明白了。

我 运行 遇到的主要问题是乱序调用。当我修复顺序时,我 运行 陷入 Converting circular structure to JSON 错误。接下来是尝试了解 VueJS、Vuex 商店和 axios 之间的回调函数是如何工作的。

我想出的最终解决方案是:

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import * as util from 'util'

Vue.use(Vuex)

import { fetchPage } from './api'

export function createStore () {
    return new Vuex.Store({
        strict: true,

        state: () => ({
            res: {}
        }),

        actions: {
            actXY ({ commit }, page) {
                return fetchPage(page).then(item => {
                    commit('mutXY', { page , item } );
                })
            }
        },

        mutations: {
            mutXY (state, {page, item }) {
                Vue.set(state.res, page, item);
            }
        }
    })
}

然后在进行 axios 调用时,我必须确保 return 仅 response.data 而不是整个响应。在此之前,我收到 unhandled promise 错误。

api.js

import Vue from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios);

Vue.axios.defaults.baseURL = '<url goes here>';

export function fetchPage(page){
    return Vue.axios.get(page).then((resp => resp.data))
}

最后但同样重要的是,这是 Homepage.vue

为了触发更新,我必须使 getData() 异步:

<template>
<div v-if="htmlData">
  <div>{{ htmlData.title }}</div>
  <div>{{ htmlData.description }}</div>
  <div>{{ htmlData.html }}</div>
</div>
</template>

<script>
export default ({
    name: 'homepage',
    computed: {
        htmlData () {
            return this.$store.state.res.homepage
        }
    },

    serverPrefetch () {
        return this.getData()
    },
    mounted () {
        if (!this.item) {
            this.getData()
        }
    },
    methods: {
        async getData () {
            await this.$store.dispatch('actXY', 'homepage')
        }
    }
})
</script>

希望这可以帮助 运行 解决所有这些问题的任何人。