web-dev-qa-db-ja.com

VueJSサーバーサイドレンダリング:計算されたプロパティにストア内の変更が表示されない

サーバー側のレンダリングをVueJSで機能させようとしています。

私は公式ドキュメントに従っていて、この例をaxiosを使用して動作させるようにしています。エンドポイントは正しく、データはmutationに表示されます。

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

私もこのページを見つけて、getter、vuex mapState、mapGetterなどの使用を含むこれらの例のほとんどを試しました。

vue.js 2 vuexからストア値を監視する方法

こちらが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":{}}

はい、getter,でストアアイテムを返す方法について他の多くのバリエーションを試しましたが、何も機能しません。

上記のリンクにあるものの他に、設定ファイルのバリエーションを探して、store/actions、getData()などにasyncを追加しようとしました。

また、コンポーネントで直接axiosを呼び出そうとしましたが、成功しませんでした。

これはVueJSでほぼ完成したプロジェクトであり、SSRに変換しています。古いvueパッケージが競合を引き起こしていました。

また、公式ドキュメントから店舗コードを分割し、ルートの記述方法のバリエーションを試しました。何も動作しません。

現在のコードを実行するときに、printステートメントが示す内容を追加する必要があると思います。

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

コンポーネントの計算されたプロパティは、変異が設定される前に実行されますbefore。これにより、ミューテーションが更新される前にゲッターが呼び出されます。同様に、resへの変更をコンポーネントから直接呼び出そうとしている場合、ストアが呼び出されてレンダリングされているときの変更には何もありません。

これは私が実行しようとしているコードのリポジトリです: https://github.com/dt1/vue-ssr-attempt

(私は答えを見つけました。私は私のために働いているものでリポジトリを更新しました、そして私の解決策の説明は以下です)

7
dizzystar

さて、私は最終的にこれを理解しました。

私が遭遇した主な問題は、順不同の呼び出しでした。順序を修正すると、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呼び出しを行うとき、応答全体ではなく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>

うまくいけば、これがこれらの問題のすべてに遭遇している人を助けるでしょう。

1
dizzystar

Vue.jsにはSSRはありません そのまま 。あなたの例では、クライアントでデータを取得できます。

Storeで次の例を試してください。

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");
    }
  }
1
David Go

私の質問は、なぜthis.getData()がマウントされて呼び出されるのですか?

私がプロセスを正しく理解している場合、サーバー側のレンダリングを呼び出すときに、vueコンポーネントがマウントされる前にHTMLをレンダリングしようとしています。this.getData()を呼び出すと動作しますか?マウントする代わりに作成しましたか?

私が試す他のこと:

  1. コンポーネントのhtmlData値をハードコードして、レンダリングされるかどうかを確認します
  2. それが機能する場合は、ストアにhtmlData値をハードコードし、コンポーネントがそのストアデータをフェッチして適切にレンダリングできるかどうかを確認します
  3. それも機能した後、コンポーネントからのディスパッチを介してサーバーからhtmlDataをフェッチし、機能するかどうかを確認します。
  4. (3)が機能しない場合は、htmlDataの準備ができている場合にのみ作成された子コンポーネントにhtmlDataをプッシュしてみて、propsを介してhtmlDataを子コンポーネントに送信します
1
Stefani