そのようなコンポーネントの場合
<template>
<div>
<router-link :to="{name:'section', params: { sectionId: firstSectionId }}">Start</router-link>
</div>
</template>
<script lang="ts">
import { mapActions } from "vuex"
export default {
mounted() {
this.getSectionId()
},
computed: {
firstSectionId() {
return this.$store.state.firstSectionId
}
},
methods: mapActions(["getSectionId"])
}
</script>
格納:
const store: any = new Vuex.Store({
state: {
firstSectionId: null
},
// actions,
// mutations
})
getSectionId
アクションにWebリクエストがあり、非同期でデータをフェッチし、firstSectionId
のstate
を埋める突然変異を呼び出します。最初のレンダリング中にfirstSectionId
はnull
であり、router-link
のレンダリング中に必要なパラメーターが欠落しているという警告が表示されます。
ここでv-if="firstSectionId"
を追加しても問題ありません。しかし、一般に、表示するサーバーからデータを取得する方法は何ですか?現在、私のすべてのコンポーネントは、レンダリングの前にストアにデータが存在するかどうかをチェックしています。それは通常ですか、またはレンダリングする前にデータがロードされるのを待つより良い方法がありますか?
データを非同期でフェッチするための1つの方法は、vuexストアでpromiseを使用することですactions 。
Vue.http.get(API_URL)
.then((response) => {
//use response object
})
.catch((error) => {
console.log(error.statusText)
});
this route にリクエストすることを示すため。応答がどのように見えるかを確認できます。 state.users配列に応答オブジェクトを保存しましょう。
store.js
const store = new Vuex.Store({
state: {
users: []
},
mutations: {
FETCH_USERS(state, users) {
state.users = users
}
},
actions: {
fetchUsers({ commit }, { self }) {
Vue.http.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
commit("FETCH_USERS", response.body);
self.filterUsers();
})
.catch((error) => {
console.log(error.statusText)
});
}
}
})
export default store
コミット後にself.filteruser()
メソッドがあることに気付きました。それは重要な瞬間です。その前に、私たちは突然変異をコミットしています。これは同期操作であり、filterUsers()
メソッド(自己パラメーターを渡すことを忘れないでください)
Users.vue
import store from "../store/store"
export default {
name: 'users',
created() {
this.$store.dispatch("fetchUsers", { self: this })
},
methods:{
filterUsers() {
//do something with users
console.log("Users--->",this.$store.state.users)
}
}
}
ES6非同期プログラミングの約束
//User.vue
created() {
this.$store.dispatch("fetchUser").then(() => {
console.log("This would be printed after dispatch!!")
})
}
//store.js
actions: {
fetchUser({ commit }) {
return new Promise((resolve, reject) => {
Vue.http.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
commit("FETCH_USERS", response.body);
resolve();
})
.catch((error) => {
console.log(error.statusText);
});
});
}
}
ES7:async/await
コールバック地獄から逃れ、非同期プログラミングを改善するには、async
関数を使用します。約束でawait
を使用できます。コードは従うのがはるかに簡単に見えます(同期のように)が、コードをブラウザーで読み取ることができないため、実行するにはBabelトランスパイラーが必要です。
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // wait for actionA to finish
commit('gotOtherData', await getOtherData())
}
}
私の経験では、期待される結果と同じタイプの空の値で状態を事前設定する場合、いくつかのチェックをスキップできます(もちろん、何を期待するかがわかっている場合)。アイテムの配列がある場合は、[]
ディレクティブ、v-for
チェック、および同様のデータアクセス試行を壊さないため、null
ではなく.length
で開始します。
しかし一般的に、v-if
を追加するのは非常に普通のことです。 vue-router
ドキュメントのこれに関するセクション があり、プロパティが存在するかどうかを確認することがまさに提案されています。それが言及している別の可能な解決策は、beforeRouteEnter
ガード内のデータをフェッチすることです。これにより、データが既に利用可能な状態で常にコンポーネントに到達することが保証されます。
最終的に、両方のソリューションは正しいものであり、それらの間の決定はUX/UIの問題です。
場所とGoogleマップAPIについても同様の要件がありました。 APIから場所を取得し、リストにロードしてから、マップコンポーネントでそれらを使用してマーカーを作成する必要がありました。 Vuexアクションでaxiosを使用してデータをフェッチし、状態を変更してロードし、ゲッターを使用して、マウントされたライフサイクルフックで結果の配列を取得しました。これにより、非同期アクションが解決される前にマウントされた空のアレイが起動されました。
Store.subscribeを使用して、この方法で解決しました。
<template>
<div class="google-map" :id="mapName"></div>
</template>
<script>
import GoogleMapsLoader from 'google-maps';
import { mapGetters } from 'vuex';
export default {
name: 'google-map',
props: ['name'],
computed: {
...mapGetters({
locations: 'locations/locations',
}),
},
data() {
return {
mapName: `${this.name}-map`,
};
},
mounted() {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'locations/SAVE_LOCATIONS') {
GoogleMapsLoader.KEY = 'myKey';
GoogleMapsLoader.load((google) => {
/* eslint-disable no-new */
const map = new google.maps.Map(document.getElementById('locations-map'));
// loop through locations and add markers to map and set map boundaries
const bounds = new google.maps.LatLngBounds();
// I access the resulting locations array via state.module.property
state.locations.locations.forEach((location) => {
new google.maps.Marker({
position: {
lat: location.latitude,
lng: location.longitude,
},
map,
});
bounds.extend({
lat: location.latitude,
lng: location.longitude,
});
});
map.fitBounds(bounds);
});
}
});
},
};