動機
- JSはほぼ初心者。
- Nuxt, Firebaseを使うことになった
- 【v2対応】Nuxt.jsとFirebaseを組み合わせて爆速でWebアプリケーションを構築するを読んだ
- しかしコード内の
auth
周りの処理やPromise
,async/await
の処理がどう行われているかよくわからなかった - Promiseの本の中に両方を用いたサンプルコードがあったので、それをNuxtで使うことができるように移植する
なお、サンプルコードは上のリンクのコードを用いる(下に転載)が、少し機能を変えて、
-
input
タグ内の文字列を,
区切りでリスト化する -
hoge
という文字列から始まっていればその文字列を、それ以外はfuga
という文字列を結果に格納 - 結果を1つずつ表示する
という風にしている。
function dummyFetch(path) {
return new Promise(function(resolve, reject){
setTimeout(function(){
if (path.startsWith("/resource")) {
resolve({ body: `Response body of ${path}` });
} else {
reject(new Error("NOT FOUND"));
}
}, 1000 * Math.random());
});
}
// 複数のリソースを取得しレスポンスボディの配列を返す
async function fetchResources(resources) {
// リソースをまとめて取得する
const promises = resources.map(function(resource){
return dummyFetch(resource);
});
// すべてのリソースが取得できるまで待つ
// Promise.allは [ResponseA, ResponseB] のように結果が配列となる
const responses = await Promise.all(promises);
// 取得した結果からレスポンスのボディだけを取り出す
return responses.map(function(response){
return response.body;
});
}
const resources = ["/resource/A", "/resource/B"];
// リソースを取得して出力する
fetchResources(resources).then(function(results){
console.log(results); // => ["Response body of /resource/A", "Response body of /resource/B"]
});
1ファイル内で完結させる実装
まずはvuexのStore
機能を使わず、dummy.vue
ファイル内のみで完結させる
<template>
<div>
<!-- 送信用 -->
<div>
<input v-model="resources" type="text">
<button @click="submit">
送信
</button>
</div>
<!-- 表示用 -->
<div>
<ul>
<li v-for="(result, index) in results" :key="index">
{{ result }}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
resources: '',
results: []
}
},
methods: {
async submit() {
const resources = this.resources.split(',')
const promises = resources.map((resource) => {
return this.dummyFetch(resource)
})
const responses = await Promise.all(promises)
return responses.map((response) => {
this.results.push(response.body)
})
},
dummyFetch(resource) {
return new Promise((resolve) => {
setTimeout(() => {
if (resource.startsWith('hoge')) {
resolve({ body: resource })
} else {
resolve({ body: 'fuga' })
}
}, 1000 * Math.random())
})
}
}
}
</script>
挙動はこんな感じ
わかりにくいですが、上の文字列が自分で打った文字列(入力)、下のリストが結果です。
Store機能を使って実装する
NuxtにおけるStoreは、2つの書き方があります、
・モジュールモード: store ディレクトリ内のすべての *.js ファイルが 名前空間付きモジュール に変換されます(index はルートモジュールとして存在します)
・クラシックモード(廃止予定): store/index.js がストアインスタンスを返します
(上のリンクより)
クラシックモードで書かれているコードが多いと思いますが、廃止予定とあったので今回はモジュールモードを使ってStore機能を使用します。
また、vueファイル内の<template>
は全く同じなので割愛します
<script>
import { mapActions, mapState } from 'vuex'
export default {
data() {
return {
resources: ''
}
},
computed: {
...mapState('dummy', ['results'])
},
methods: {
// `this.fetch()` を `this.$store.dispatch('dummy/fetch')` にマッピング
...mapActions('dummy', ['fetch']),
submit() {
this.fetch(this.resources)
}
}
}
</script>
export const state = () => ({
results: []
})
export const mutations = {
fetchResources(state, results) {
// state.results.push(response.body)
state.results = results
}
}
function dummyFetch(resource) {
return new Promise((resolve) => {
setTimeout(() => {
if (resource.startsWith('hoge')) {
resolve({ body: resource })
} else {
resolve({ body: 'fuga' })
}
}, 1000 * Math.random())
})
}
async function fetchResources(resources) {
const promises = resources.split(',').map((resource) => {
return dummyFetch(resource)
})
const responses = await Promise.all(promises)
return responses.map((response) => {
return response.body
})
}
export const actions = {
fetch({ commit }, resources) {
fetchResources(resources).then((results) => {
commit('fetchResources', results)
})
}
}
store内のresults
に対して、action, mutationを用いて結果を格納し、それを描画しています。
最後に
dummyFetch
, fetchResources
関数はどこに置けばよいのかがわからなかった。
一旦は呼び出しができるようにファイル内にかきましたが、plugins
ディレクトリ内にutils.js
などをおいて書いていけばいいのか、そのほかに方法があるのか...
わかる方、教えていただけますと幸いです。