- 投稿日:2021-12-05T22:10:54+09:00
forEachの中でasync awaitは使えない(コピペ例)
forEachの中で非同期処理で詰まったので備忘録として書く エラー例 console.log("before_forEach", this.datas); this.datas.forEach(async(data) => { //非同期処理 }); console.log("after_forEach", this.datas); 成功例 console.log("before_forEach", this.datas); for (let data of this.datas){ //非同期処理 }); console.log("after_forEach", this.datas); forで書き直せば、処理順はうまくいくはず! 参考にしたandより詳しい記事
- 投稿日:2021-12-05T19:15:24+09:00
グローバルステート管理ライブラリである「Pinia」について
はじめに 今回はグローバルステート管理ライブラリである「Pinia」について紹介と実際に自分で触ってみたいと思います。 興味を持ったきっかけとしては10月27日に開催された「v-tokyo オンライン Meetup#14」内で「Pinia」ライブラリの開発者である Posva氏の発表を聞き、複雑なコードを書かずともグローバルなステートの型安全を担保してくれるという魅力もさることながら、「アイコンが可愛い!!」と思って大変興味を持ちました。 Pinia ドキュメント Piniaとは まずPiniaとは、Vue.js向けの状態管理ライブラリであり、階層が深くなったコンポーネント間でデータの共有ができるものです。Vuexと同様にコンポーネント間で状態の受け渡しが容易になるため、ある程度規模の大きい開発において威力を発揮する反面、小規模な開発or個人開発でもTypeScriptのフルサポート・記述量が少なくできるということから開発規模の大小関係なく導入するメリットは大きいと思います。 Vuexとの違い Vuex3.x 4.x ・mutationsがなくなり、stateの値を変更する場合はactionsを使用する ・TypeScriptのサポートを受け、正しく型付けされた(Vuex4.xはTypeScriptのサポートを受けています) ・機能ごとに切り分けがしやすい ・ネストされたモジュールの廃止 ・名前空間付きモジュールの廃止 等があげられています。 Vuex.5(RFC段階) 基本的にはPiniaとVuex5でできることは変わらないようです。 というのも開発者である [Posva氏]はVuexの設計にも携わっており、Vuex5で新しく導入する機能のテストも兼ねてPiniaを開発したという意図があるそうです。Vuex5は従来の書き方と大きく違うことが想定されており、今の段階でPiniaを使用することできたるVuex5への導入が容易になる+Vuex5に統合する見通しがあるそうです。 試してみる 今回はお試しということ開発サーバーの起動が早い+高速で動作するビルドツールであるViteを使用します。また、Viteでプロジェクトを作成すると、Vue3.2から使用できる <script setup>構文がそのまま使用できますのでこちらの書き方で試してみたいと思います。また、今回はストップウオッチの機能を作成しますが、この機能のコードの説明は省かせていただきますのでご了承くださいませ。 まずは下記コマンドを実行し、環境のセットアップを始めます。 プロジェクト名は任意、フレームワークはvueで作成します。 $ npm init @vitejs/app $ cd hoge $ npm install piniaのインストール $ npm install pinia main.js内にPiniaライブラリを読み込むように記述の追記。 main.js import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' createApp(App) .use(createPinia()) // この行を追加します .mount('#app') src/stores/timer.js import { defineStore } from 'pinia' export const useStore = defineStore('hoge', { state: () => { return { // 初期値を入れる } }, getters: { // stateのデータに対する算出プロパティを定義 }, actions: { // stateで定義したデータの更新 }, }) state 関数を定義して戻り値に初期値を含めたオブジェクトを返してあげます。 getters stateのデータに対する算出プロパティを定義し、各コンポーネントで使用ができます。また、他のgetterへのアクセスもできます。 actions 定義するメソッド内でstateで定義したデータの更新を行います。 defineStore関数の第一引数にはアプリケーション全体でstoreを特定するためのユニークキーを記述してあげます。これにより、Chrome DevToolsのナビゲーションメニューの「Console」に使用しているstoreの名前が表示されます↓ また、Devtoolsのサポートもされているのでデータの内容が視覚的にわかりやすくなっています↓ では、中身も書いていきます。 src/stores/timer.js import { defineStore } from 'pinia' export const useStore = defineStore('hoge', { state: () => { return { time: 0, timeId: 0, stopTime: 0, status: false, // 計測状態 } }, getters: { // 引数でstateを受け取ることができます toFixedTimer: (state) => state.time.toFixed(2) }, actions: { startTimer() { const startTime = Date.now() const measureTimer = () => { // thisはstoreのインスタンス this.time = (Date.now() - startTime) * 0.001 if (this.stopTime) { this.time += this.stopTime } } this.timeId = setInterval(measureTimer, 10) this.status = true }, stopTimer() { this.stopTime = this.time clearInterval(this.timeId) this.status = false }, resetTimer() { clearInterval(this.timeId) // $reset()を使用することでstateのデータを初期状態に戻すことができます this.$reset() }, }, }) gettersオプション内のtoFixedTimerの引数ではstateを受け取ることができ、値を加工しています。 actions内のメソッドでstateのデータを参照する場合はthisを使ってアクセスします。また、storeのインスタンスはメソッドを持っており、その一つである$resetを使用してstateにあるデータの初期化を行なっています(その他にもdispose、onAction、patch、subscribeのメソッドがあります) App.vue <script setup> import Timer from './components/Timer.vue' </script> <template> <Timer /> </template> App.vueファイルはTimer.vueのコンポーネントをimportしているだけです。 Timer.vue <script setup> import { storeToRefs } from 'pinia' import { useStore } from '../store/timer' // useStore を呼び出すだけで、グローバルストアへのアクセスが可能 const store = useStore() // リアクティブさを損なわない為にstoreToRefsを使用 const { toFixedTimer, status } = storeToRefs(store) </script> <template> <div>{{ toFixedTimer }}</div> <button v-if="!status" @click="store.startTimer"> 開始 </button> <button v-if="status" @click="store.stopTimer"> 停止 </button> <button @click="store.resetTimer">リセット</button> </template> <style scoped> button { margin: 0 2px; } </style> storeはリアクティブなものなのでそのままES6の分割代入をしてしまうとリアクティブではなくなってしまいます。そこでstoreToRefsを使用して、プロパティをrefオブジェクトに変換したオブジェクトを返してあげることでリアクティブを維持することが可能です。 各ボタンのクリックイベントでは、store.startTimerのように記述することでactionsのメソッドを呼び出しています。 はい、ストップウオッチの機能が完成です。動画をGIF化するとカクカクになってしまいました。。が動画を滑らかにするのは一旦置いておきます。笑 所感 アイコンが可愛くて使いやすい印象から今回触ってみましたが、mutationsがないことでよりシンプルに書けるのはいいですね。また、Vue3からcompositionAPIが導入され、機能ごとのロジックを別ファイルに切り分けやすくなりましたが、Piniaを使用することでstoreのロジックも切り分けることができるので相性は凄く良いと感じました。TypeScriptのサポートも試してみたかったのですが絶賛勉強中の為、今回は省きましたがある程度できるようになったらPiniaまたはVuex5でいつか試してみたいと思います。 Piniaが好きになったのとVuex5のリリースが楽しみになった1日でした? おわりに 記事を読んでいただき、ありがとうございました!!今回の記事で誤字、脱字、間違った内容の箇所等ありましたらご指摘いただけると大変助かります。よろしくお願いいたします。 参考資料 Pinia Home Vuex の新しいライバル? Pinia のご紹介
- 投稿日:2021-12-05T17:26:39+09:00
【Nuxt.js】ボタンを押したらデータ値をとる&色が変わる方法(アプリ開発アウトプット)
はじめに こんにちは! 今回は【Nuxt.js】ボタンを押したら値をとる&色が変わる方法についてアウトプットしていきます! 前提 ・Nuxtの新規プロジェクト作成が既に済んでいる ・vue.jsの基礎学習が済んでいる ・tailwindcssが扱える 対象 ・真偽値を使用しての開発を学びたい方 ・診断アプリケーションを作りたい方 実装 template <button class="text-2xl my-6 mx-10 py-6 px-6 border-2 border-red-600 rounded-full h-28 w-28 flex items-center justify-center" :class="answers.q1 ? 'bg-red-200' : ''" @click="answer('q1',true)"> YES </button> <button class="text-2xl my-6 mx-10 py-6 px-6 border-2 border-blue-600 rounded-full h-28 w-28 flex items-center justify-center" :class="answers.q1 === false ? 'bg-blue-200' : ''" @click="answer('q1',false)"> NO </button> script data() { return { answers: { q1: null }, }; }, methods: { answer(questionNumber, bool) { this.answers[questionNumber] = bool; }, } 解説 上記のコード、下記の写真のようにYESとNOのボタンがあります。 ・YESを押したらtrueをデータ値としてとる。背景を赤色にする。 ・NOを押したらfalseをデータ値としてとる。背景を青色にする。 ということがしたいです。 初期値はnull。からの状態。 【YESのボタン】 @click="answer('q1',true)" YESボタンをクリックしたとき、script部分answerにq1はtureというデータ値が入ります。 それをthis.answers[questionNumber] = bool;と記述し、q1=trueと処理が働き、決定付けられます。 :class="answers.q1 ? 'bg-red-200' : ''" この記述の'bg-red-200' : ''この部分は'左辺(tureの時)' : 右辺(falseの時)という意味になります。よって全体を訳すとYESボタンを押すとデータ値にtrueが入る。answers.q1のデータ値がtrueの時YESボタンの背景カラーは赤色に変わるという意味になり、処理が実行されている。 【NOのボタン】 まず最初に、初期値はnullとお話ししました。boolにnullと定義してしまうと、性質的な問題で「nullはfalsyな値なので、if文の条件式ではfalseとなる」と処理してしまう働きがあります。 なので上記同様に'': 'bg-red-200'と記述してしまうと、 初期状態から色が入ってしまうことになります。(nullだが、falseの影響が出てしまう) なので'bg-blue-200' : ''というように記述しなければなりません。 NOボタンの場合は:class="answers.q1 === false(answer.q1はfalseと定義)と記述してしまいます。 これで初期値はnullではなくfalseなので、NOボタンをクリックしたら反対のtrueになることになります。 背景色も'bg-blue-200' : ''と記述しているので、初めから青く染まっているのではなく、しっかりクリックしてから青く染まるようになりました。 しかしこのままではYESもtrue、NOもtrueとデータをとってしまうので、@click="answer('q1',false)"と書き加えることで、NOボタンを押したら、answer.q1のデータににfalseが入るという処理法に塗り替えました! NOボタンの処理をまとめるとボタンを押したらtrueになるのだが、データとして入るのはfalseであるということになります! 最後に 今回はボタンを押したらデータ値をとる&色が変わる方法についてアウトプットしました。 今回の記事を読んで質問、間違い等あればコメント等頂けると幸いです。 今後ともQiitaにてアウトプットしていきます! 最後までご愛読ありがとうございました!
- 投稿日:2021-12-05T14:47:53+09:00
【Vuex】アクションの利用
はじめに こんにちは! 今回は【Vuex】アクションの利用についてアウトプットしていきます! アクションとは アクションはミューテーションと似ていますが、下記の点で異なります。 ・アクションは、状態を変更するのではなく、ミューテーションをコミットします。 ・アクションは任意の非同期処理を含むことができます。 書き方・解説 pages/index.js <p>{{ $store.state.message }}</p> <button v-on:click="$store.dispatch('updateMessageAction')">Dispatch</button> store/index.js mutations:{ updateMessage: function(state,payload){ // ⏬state.messageを書き換える処理 state.message = payload } }, actions: { updateMessageAction(context) { context.commit('updateMessage', 'Commit with payload') } } actionの中からmutatiomsを呼ぶように修正を行います。 定義したupdateMessageActionをコンポーネント側から呼びます。 この時、commitメソッドではなくdispatchメソッドを使います。 また、actionでもmutations同様、値渡しすることが可能です。 最後に 今回はアクションの利用についてアウトプットしました。 今回の記事を読んで質問、間違い等あればコメント等頂けると幸いです。 今後ともQiitaにてアウトプットしていきます! 最後までご愛読ありがとうございました!
- 投稿日:2021-12-05T13:25:32+09:00
vuedraggableで連想配列を操作した時に[vuex do not mutate vuex store outside mutation handlers]となった時の話
はじめに 本記事は複数の配列を用意してvuedraggableを使用する方法の続きとなっています。 また、今回紹介する方法は1つの方法であって、正しい方法があると思います。 その事を念頭に置いて温かい目で見ていただけると助かります。 また、正しい方法をご存知の方がいましたら、ご教授いただけると幸いです。 前回までのあらすじ アンパンヘッドボーイはアンパンを配る動物の数だけ袋(空の配列)を用意して欲しかった。 そこで、工場(store)であらかじめ動物の数を把握することにしました。 見事成功してアンパンを配ろうとしたその時、奴が現れこう言いました。 vuex do not mutate vuex store outside mutation handlers 茶番おわり 本題 この原因となっているのが、 computed: { items() { return this.$store.state.items; }, }, これです。 これを下記のように書き直しても computed: { animals: { get(){ return this.$store.state.items } set(value){ this.$store.commit("hoge",value) } }, }, としてもエラーが出ます。 原因 上記の渡し方だと参照渡しになってしまうみたいです。 そのために色々な方法を試してみたのですがうまくいかず、試行錯誤の末たどり着いたのがこの方法でした。 対策 data: () => ({ items: [], }), created() { const items = this.$store.state.items; items.forEach((item) => this.items.push(item)); }, dataでitemsを定義して、createdの段階で新しい配列として定義し直してあげました。 おわり 無事皆にアンパンを配ることができました。 方法は間違っているかもしれませんが、とりあえず動かしたい方は試してみてください。 また、他に方法をご存知の方がいましたら、ご指導のほどよろしくお願いします。 コード *コンポーネント化しているので、前の記事と多少コードが変わっています。 index.vue <template> <v-row> <v-col> <v-list> <v-list-item-title> アンパンヘッドボーイ </v-list-item-title> <draggable v-model="item1" group="item"> <v-list-item v-for="item in item1" :key="item.id"> {{ item.name }} </v-list-item> </draggable> </v-list> </v-col> <animals /> </v-row> </template> <script> import draggable from "vuedraggable"; import animals from "~/components/animals.vue"; export default { components: { draggable, animals }, data: () => ({ item1: [ { id: 1, name: "アンパン1" }, { id: 2, name: "アンパン2" }, { id: 3, name: "アンパン3" }, ], }), }; </script> animals.vue <template> <v-row> <v-col v-for="(animal, index) in animals" :key="index"> <v-list> <v-list-item-title> {{ animal.name }} </v-list-item-title> <draggable v-model="items[index]" group="item"> <v-list-item v-for="item in items[index]" :key="item.id"> {{ item.name }} </v-list-item> </draggable> </v-list> </v-col> </v-row> </template> <script> import draggable from "vuedraggable"; export default { components: { draggable }, data: () => ({ items: [], }), created() { this.$store.commit("items"); const items = this.$store.state.items; items.forEach((item) => this.items.push(item)); }, computed: { animals() { return this.$store.state.animals; }, }, }; </script> index.js export const state = () => ({ animals: [ { id: 1, name: "カバ" }, { id: 2, name: "うさぎ" }, { id: 3, name: "犬" }, ], items: [], }); // ------------------Mutations------------------------------- export const mutations = { items(state) { const newItems = new Array(); for (let i = 0; i < state.animals.length; i++) { newItems[i] = new Array(); } state.items = newItems; }, };
- 投稿日:2021-12-05T12:08:23+09:00
【Vue.js】v-modelの存在意義・使う理由・何ができるのか理解できない人用の説明【これからはじめる人向け】
この記事について Vueを学び始めたとき、 v-modelってなんのためにあるの? 使ったら何ができるようになるの? と疑問に思う人は多いと思います。 この記事では、v-modelの使い方だとか詳しいことを説明するよりも前に理解しておく必要がある 「v-modelの存在理由」について説明します。 v-modelは便利機能というより、<input>タグに最低限必要なもの v-model以外のVueのディレクティブ v-if や v-for などと違って、 v-modelは便利な機能というイメージではありません。 どちらかというと「まともな入力欄として動かすために最低限必要なもの」 という認識でいたほうが、理解しやすいと思います。 Vueでは動的なデータは変数で管理する必要がある Vueを使う場合、動的なデータ(ユーザーの入力やクリック等の操作によって表示が変わる部分)は コンポーネントのdataとして変数で管理することになります。 <script> export default { data:()=>({ dataA: "", dataB: "", dataC: "", }), } </script> ピュアなHTMLを書いていたときは、<input>タグを書けば、 入力した値は自動的に管理されていて、 なにも指定しなくても「文字の入力ができる」「文字を消したりできる」「文字を書き換えることができる」という機能は備わっていました。 しかし、Vueではそれだけではなく、 「文字を変数から取得できる」「変数を書き換えたら文字も書き換えることができる」という機能を付け加える必要があります。 たとえばチェックボックスのチェック状態も、なんらかの変数に代入しておいて、 その変数を取得するだけでチェック状態がわかる その変数を書き換えるだけでチェック状態を変更できる ↑のようにしておく必要があります。 <script> export default { data:()=>({ isChecked: true, // isCheckedを取得するだけでチェック状態がわかる & isCheckedを書き換えるだけで、チェック状態の変更ができる必要がある }), } </script> ※ dataの名前は自由につけてOKですが、今回は例としてisCheckedという名前にします。 このように、dataにひもづけて管理する必要があるということは、 つまりチェックボックスの表示内容は、常にdataの中のisCheckedに代入されている値と連動させる必要があるという事になります。 isCheckedがfalseだったら、チェックボックスの見た目はチェックが入ってない状態になっている必要がある isCheckedがtrueに変わったら、チェックボックスの見た目はチェックが入っている状態に変わっている必要がある 本来のHTMLでは勝手によろしくやってくれていた部分なのですが、 Vueではdataと紐付けて管理できるようにするために、 どのチェックボックスの状態を、どのdata(変数)で管理するのか を指定する必要があるのです。 そのためには、下記の2つの機能を追加する必要がある=2つのディレクティブを指定する必要がありました。 役割1. dataの内容を<input>タグの表示内容と連動させる ここからは、チェックボックスを具体例にして説明していきます。 まずは上の章で説明したように、<input>タグの表示内容と、 そのチェックボックスの状態を管理しているdataを連動させる必要があります。 チェックボックスの表示内容は、checked属性という値で指定できます。 (これはVue独自の機能ではなく、ピュアなHTMLでも元々指定できる属性です) <input type="checkbox" checked> <!-- checked属性を指定しておくと、チェックが入った状態で表示される --> <input type="checkbox"> <!-- checked属性を指定しないでおくと、チェックが入ってない状態で表示される --> そして、このchecked属性の内容を変数に連動させたいので コードで書くと v-bind:checked="isChecked" のように、 「checked属性の値はisCheckedという変数(コンポーネントのdata)の内容を自動的に読み取ってくださいね」と、 連動させておく必要があるのです。 (もともとのHTMLでは、chekced="false"ような書き方はしないので、そこだけ少し独特な使い方かもしれません) v-bind:の書き方は:と省略することができるので :checked="isChecked" でもOKです。 役割2. <input>タグの表示内容が変わったらdataの中身も変更する checked属性の内容がisCheckedと連動しているということは チェックボックスの状態が変更されたら、 isCheckedの中身は、切り替わった後の値(true/false)を再代入しなければいけないということです。 つまり、チェック状態が切り替わったら処理をするという意味の v-on:change というディレクティブで、 「isCheckedの中身を書き換える」という処理をする必要があるのです。 v-on:change="isChecked = 新しい値" (ピュアなHTMLにも、状態が変わったときにJavaScriptの処理を実行するonchangeという属性がありましたが、v-on:change はこれのVue版の書き方です) (onchange属性をつかったことがない人は、一度ピュアなHTMLでonchange属性をつかって処理を実行してみると、イメージがつかみやすいと思います。) 新しい値は、changeイベントのターゲット(今回の場合チェックボックス)の値にすればいいので、 常に $event.target.value でOKです。 v-on:change="isChecked = $event.target.value" v-on:の書き方は@と省略することができるので @change="isChecked = $event.target.value" でもOKです。 この2つの処理がよく必要になるので、まとめて書けるようにした チェックボックス以外にも、ユーザーから入力される テキストエリアやセレクトボックスなどのHTMLタグも、 これと同じような処理は必ず必要になります。 (テキストエリアやセレクトボックスの場合、dataの中身はtrue/falseではなく文字列になりますが。) この処理ができていないと 画面上の入力欄に入力をしたのに入力内容がdataに入ってない dataを書き換えたのに画面上の入力内容が変わらない 画面上の入力欄に入力をしたのに画面上の入力内容が変わらない=入力できない など、 入力欄としてまともに使えない状態になります。 このように、入力欄として機能させるために必ず必要になる紐付けなのに、 毎回2つのディレクティブを指定しなければいけないのは非常に面倒です。 そのため、この2つの指定をまとめて指定できるようにしてくれたディレクティブが、v-model です。 おわり いかがでしょうか。 v-modelは便利機能というよりも最低限必要なもの、という認識が伝わったなら幸いです。 この記事の内容を理解したうえで、v-modelの書き方、使い方などを学んでいただけると よりしっくり理解していただけるかな と思います。