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()
会起作用吗?
我会尝试的其他事情:
- 在组件中硬编码 htmlData 值以检查它是否会呈现
- 如果可行,硬编码存储中的 htmlData 值并检查组件是否可以获取该存储数据并正确呈现
- 在那之后也可以工作,通过从组件调度从服务器获取 htmlData,看看它是否工作。
- 如果 (3) 不起作用,请尝试将 htmlData 推送到子组件
仅当您已经准备好 htmlData 时才创建,发送
htmlData 通过 props
传给子组件
好吧,我终于明白了。
我 运行 遇到的主要问题是乱序调用。当我修复顺序时,我 运行 陷入 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>
希望这可以帮助 运行 解决所有这些问题的任何人。
我正在尝试让服务器端渲染在 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()
会起作用吗?
我会尝试的其他事情:
- 在组件中硬编码 htmlData 值以检查它是否会呈现
- 如果可行,硬编码存储中的 htmlData 值并检查组件是否可以获取该存储数据并正确呈现
- 在那之后也可以工作,通过从组件调度从服务器获取 htmlData,看看它是否工作。
- 如果 (3) 不起作用,请尝试将 htmlData 推送到子组件 仅当您已经准备好 htmlData 时才创建,发送 htmlData 通过 props 传给子组件
好吧,我终于明白了。
我 运行 遇到的主要问题是乱序调用。当我修复顺序时,我 运行 陷入 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>
希望这可以帮助 运行 解决所有这些问题的任何人。