質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

592閲覧

Vue.jsのライフサイクルフック内での共通処理のベストプラクティスを知りたいです。

tukkun

総合スコア8

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/03/30 13:40

編集2018/04/02 08:57

Vueインスタンスが複数存在し、created等のライフサイクルフック内で共通処理が発生した場合どうすれば良いのかわからなくて困っています。
幾つかのVueインスタンスのライフサイクルフック内で同じAPIのエントリーポイントを叩く処理を想定しています。
APIのイメージは下記のようなjsonが返ってくる様なイメージで別々のタイトルの中に共通のkey"price"が設定されており、値が格納されています。

json

1{ 2 "title1": { 3 "price" : 1500 4 }, 5 "title2": { 6 "price" : 900 7 } 8}

各VueインスタンスはこのJsonの値であるtitle名と紐付いておりid名として利用しています。(例: "#title1", "#title2")
下記のような形で取得したresponseの結果一覧からpriceを取得し、価格に合わせてインスタンス内のデータを変更します。
``html

<div id="title1" v-bind:class="test-class" ></div> <div id="title2" v-bind:class="test-class" ></div> ``` ```javascript const instance1 = new Vue({ el: '#title1', data: { test-class: "" }, created() { const self = this; axios.get('http://localhost:4567/books').then(response => { let title = self.$el.slice(1) self.test-class = response.title.price >1000 ? "class1" : "class2"; }).catch(error => { }); } }); const instance2 = new Vue({ el: '#title2', data: { test-class: "" }, created() { const self = this; axios.get('http://localhost:4567/books').then(response => { let title = self.$el.slice(1) self.test-class = response.title.price >1000 ? "class1" : "class2"; }).catch(error => { }); } }); ```

#試した内容
元のままでは効率が悪いのでAPIを叩く処理をインスタンスの外で関数として定義し、responseを格納するグローバル変数の値がからだった場合にこの関数を叩いて値を取得し、クラスをセットすることにしました。

javascript

1let res; 2let getBooks = () => { 3 axios.get('http://localhost:4567/books').then(response => { 4 res = response; 5 }).catch(error => { 6 }); 7} 8 9const instance1 = new Vue({ 10 el: '#title1', 11 data: { 12 test-class: "" 13 }, 14 created() { 15 const self = this; 16 res = res || getBooks(); 17 let title = self.$el.slice(1) 18 this.test-class = res.title.price >1000 ? "class1" : "class2"; 19 } 20}); 21const instance2 = new Vue({ 22 el: '#title2', 23 data: { 24 test-class: "" 25 }, 26 created() { 27 const self = this; 28 res = res || getBooks(); 29 let title = self.$el.slice(1) 30 this.test-class = res.title.price >1000 ? "class1" : "class2"; 31 } 32});

上記の場合だと同期的にコードが書かれていないのでresに値が入っておらず関数が叩かれた場合、APIを叩き値を取得してくるよりも前にその先の判定式、dataへのセットが行われてしまいました。

解決策、疑問点

解決策としては getBooksをasyncで定義し、値の取得が確認できるまで待ってからdataをセットする方法を思いついたのですが、いまいちこれがいい方法なのかわかっていないです。。
もっと良い方がいっぱいありそうだとは思ったのですが、自分では思いつかず、調べてみても同期的にコードを書く方法しか思いつかなかったため、もし他に良い方法があればご教示頂きたいです。

長々とすみませんが、よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

karamarimo

2018/03/30 15:53

vueインスタンスは普通1つだと思いますが、1つにしてはいけないのでしょうか?質問文のjsonには"title"というキーはありませんが、res.title は undefined になるのではないでしょうか。
tukkun

2018/03/30 23:31 編集

karamarimo様 ご回答頂きありがとうございます。 Vueインスタンスはひとつなのですね。そこから勘違いしていそうです。。 titleというkeyはelの#title1などから#をsliceしてセットするイメージだったのですが、たしかにおかしいですね。。 こちら後ほど訂正させて下さい。 ご指摘頂きありがとうございました。
guest

回答3

0

ベストアンサー

同じ処理を別の部品で使い回したい時はmixinsを使うと良いでしょう。
質問者様のコードをそのまま使っているので、想定している動作が動くかどうかは保証できません(いろいろツッコミがあるコードでしたので)

const myMixin = { data: { test-class: "" }, created() { const self = this; axios.get('http://localhost:4567/books').then(response => { let title = self.$el.slice(1) self.test-class = response.title.price >1000 ? "class1" : "class2"; }).catch(error => { }); } } const instance1 = new Vue({ el: "#title1", mixins: [myMixin] }) const instance2 = new Vue({ el: "#title2", mixins: [myMixin] })

指摘1
JavascriptでObjectのキーにケバブケースを使う場合は、そのままでは動かないと思います。
data: {
'text-class': ""
}
のようにクオートで囲む必要があります。
キャメルケースにしたほうがいいですね。
textClass

指摘2

let title = self.$el.slice(1) self.test-class = response.title.price >1000 ? "class1" : "class2";

この部分は色々間違ってそうです。
title1にあたるものを抜き出してキーにして、Ajaxで取得した値から抜き取りたいはずですが、そのような場合response[title].priceとなるべきです。

ただしtitleにも、目的としている"title1"は文法のミスにより入っていません。
一番簡単に取得する方法は
let title = self.$el.id
でしょうか。

指摘3
最後にself.test-classも指摘1で述べたように、ハイフン区切りではアクセスできません。
self['test-class'] = response[title].price > 1000 ? "class1" : "class2";

投稿2018/03/30 16:28

編集2018/03/31 06:59
sakapun

総合スコア888

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tukkun

2018/03/30 23:21

sakapun様 ご回答頂きありがとうございます。 同一の処理を書く際にはmixinを利用するのですね! 帰宅後に試してみたいと思います。 もし手間でなければツッコミどころについても教えて頂けると嬉しいです。。 恥ずかしながら勉強を始めたばかりのもので、変な記述などありましたらご指摘頂きたいです。 お手数をお掛けしてしまい申し行けないのですがよろしくお願い致します。
sakapun

2018/03/31 06:40

返信ではコードが書きにくいので、回答に追記します
tukkun

2018/04/02 09:28

sakapun様 ご返信いただきありがとうございます! [指摘1について] ケバブケースを使う場合はクオートで囲むと動作するのですね。 objectのkeyを囲う場合と囲わない場合の差がわかっていなかったのですが、元々クオートを付けるのが形式的には正しく、値によって省略できているだけなのですね。(他の記事が見つからず解釈が誤っていたらご指摘下さい 次のリンクの記事を参考にさせていただきましたhttps://mathiasbynens.be/notes/javascript-properties ) キャメルケースを使うとスッキリかけそうなのでこれからはキャメルケースを使いたいと思います! [指摘2について] こちら別の方の回答でも頂きましたが、間違えたまま理解してしまっていました。 ご指摘頂きありがとうございます。 `let title = self.$el.id`で動作を確認した所、意図したとおりに動作を確認することができました。 [指摘3について] こちらは指摘1で頂いた内容の通りですね。 ケバブケースを使う場合はクオートで囲って使用したいと思います。 ご指摘いただきありがとうございました。 とても勉強になりました!
guest

0

間違い

js

1 let title = self.$el.slice(1) 2 self.test-class = ressponse.title.price >1000 ? "class1" : "class2";

まずressponseではなくresponseです。
そして、titleの変数に入っているstringをキーとしてresponseの値を取り出したいのだと思いますが、これでは"title"というstringがキーになっています。stringのexpressionをキーとするにはブラケットを用いてresponse[title]と書きます。

また、プログラム上でハイフンを含むプロパティにアクセスするにはself.test-classとそのまま書くことはできません。-は引き算の演算子としてparseされるので、self.test - classと認識されてしまいます。代わりに、self['test-class']のようにbracketの中にstringリテラルを書くようにする必要があります。

js

1 data: { 2 test-class: "" 3 },

objectリテラルでも、quoteなしでキー名にハイフンを使うとsyntax errorになります。'test-class'とquoteで囲む必要があります。

結局、プロパティ名に-を使わなければ楽だと思います。

js

1res = res || getBooks();

getBooks()は返り値がない(undefined)なので意味がないです。

別のアプローチ

複数のvueインスタンス間での共通処理をまとめるにはmixinなどを用いるのが良いかと思いますが、今の場合ではそもそもインスタンスを複数に分ける必要性がないため、1つにまとめる方法を書いておきます。

Vueインスタンスを1つにすれば、APIから取得した値を共有できますし、クラスの判定部分のコードもインスタンスのmethodとして共有できます。

JSFiddle Demo

APIからのデータが取得できるまで表示はどうしたいのかわからなかったので、とりあえずv-ifで非表示にしています。
fetchPricesは単にAPIへのリクエストのモックですので気にしないでください。

html

1<div id="app"> 2 <div v-if="prices"> 3 <div v-bind:class="classFor(prices.title1)">アイテム1</div> 4 <div v-bind:class="classFor(prices.title2)">アイテム2</div> 5 </div> 6</div>

js

1// 2秒後にjsonを返すpromise 2const fetchPrices = () => new Promise(resolve => setTimeout(() => 3 resolve({ 4 "title1": { 5 "price": 1500 6 }, 7 "title2": { 8 "price": 900 9 }, 10 }), 2000)) 11 12const vm = new Vue({ 13 el: '#app', 14 data: { 15 prices: null, 16 }, 17 created() { 18 fetchPrices().then(prices => { 19 this.prices = prices 20 }) 21 }, 22 methods: { 23 classFor: function(item) { 24 const expensive = item.price > 1000 25 26 return expensive ? "class1" : "class2" 27 }, 28 }, 29}) 30

補足

js

1 const self = this; 2 axios.get('http://localhost:4567/books').then(response => { 3 let title = self.$el.slice(1) 4 self.test-class = response.title.price >1000 ? "class1" : "class2"; 5 }).catch(error => { 6 });

thisselfに退避していますが、今の場合その必要はありません。なぜなら、普通の function () {}と違って、arrow function の中のthisは外側のthisと必ず同じになるからです。

投稿2018/03/31 07:20

編集2018/04/02 14:18
karamarimo

総合スコア2551

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tukkun

2018/04/02 09:06

karamarimo様 ご回答頂きありがとうございます。 typoの方修正させていただきました。 > そして、titleの変数に入っているstringをキーとしてresponseの値を取り出したいのだと思いますが、これでは"title"というstringがキーになっています。stringのexpressionをキーとするにはブラケットを用いてresponse[title]と書きます。 > また、test-classという変数名は無理です。-は引き算の演算子としてparseされるので、test - classと認識されてしまいます。-を使わないようにしましょう。 2点完全に誤って覚えていた為ご指摘いただけて助かりました。 以後気をつけたいと思います。 Vueインスタンスの使い方は1id=1instanceだと誤って覚えていたので助かりました。 どう考えてもそんな作りになってないですよね。。 サンプルもとても参考になりました。 実際に自分のコードに`v-if`を除いて反映させてみた所、思うように動作は下者のundefindがconsoleに`ClassFor`分出力されていたので、同じように`v-if`を使わせていただきました。 おそらく`created()`が呼ばれる前に`ClassFor`が呼ばれてしまっているのかなと思っているのですが、まだしっかりと理解できていないのでライフサイクル周りをこれから学習していきたいと思います。 ありがとうございました!
karamarimo

2018/04/02 11:22

> 実際に自分のコードに`v-if`を除いて反映させてみた所、思うように動作は下者のundefindがconsoleに`ClassFor`分出力されていた APIのデータは非同期で届くので、それまでは prices は初期値の null になってますが、classFor(prices.title1) を計算しようとしてエラーになっていると思います(undefined へのプロパティアクセスはエラーになる)。 まだ到着していないデータを表示しようとしてエラーがでるのは当然なので、v-if で非表示にするなり、prices が null かどうかをチェックするなりする必要があります。
karamarimo

2018/04/02 11:24

低評価を押された方は、どの点がよろしくないかご指摘いただけるとありがたいです。
karamarimo

2018/04/02 11:31

あっ、なぜか test-class という変数を宣言して用いていると勘違いしていましたね。キー名としてハイフンを含むstringは使えますので、低評価を頂いた理由もそれだと思います。修正しておきます。
sakapun

2018/04/02 13:10

Vueのインスタンスが1つでなければいけないという部分、及び質問における複数のコンポーネントにおける共通処理のベストプラクティスは何かという問いには答えにはなっていないと思った次第です。 共通処理を入れたくなった場合Mixinをいれるまたは、HoCを使って再利用するのがベストプラクティスかと思います
sakapun

2018/04/02 13:18

もちろん設計として妥当かといえば、この件に関してはまとめた方がいい案件なのは間違いありません。mixinが共通処理の答えとしては正しいけれど、この場合はそれをするほどではないという説明はいると思いました
karamarimo

2018/04/02 14:09

tukkunさんはvueインスタンスをあえて複数にしているというわけではなかったようなので、1つのインスタンスにまとめる方法を示すのがよいかと思ったのですが、確かに質問に対する答えにはなっていなかったですね。ご指摘ありがとうございます。
guest

0

Vue.jsの公式ガイドにある シンプルな状態管理をゼロから作る が参考になるのではないでしょうか?

js

1var store = { 2 debug: true, 3 state: { 4 message: 'Hello!' 5 }, 6 setMessageAction (newValue) { 7 if (this.debug) console.log('setMessageAction triggered with', newValue) 8 this.state.message = newValue 9 }, 10 clearMessageAction () { 11 if (this.debug) console.log('clearMessageAction triggered') 12 this.state.message = '' 13 } 14}

のようなストアを両方のインスタンスのdataで監視して、vue外のjsでaxiosで読んだデータをセットしてあげるというイメージです。

js

1var vmA = new Vue({ 2 data: { 3 privateState: {}, 4 sharedState: store.state 5 } 6}) 7 8var vmB = new Vue({ 9 data: { 10 privateState: {}, 11 sharedState: store.state 12 } 13})

投稿2018/03/30 17:12

euledge

総合スコア2404

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tukkun

2018/03/30 23:18

Euledge様 ご回答頂きありがとうございます。 なるほど、まだ全文は読めていないのですが、参考になりました。 帰宅後に一度実装して試してみたいと思います! ありがとうございます!
tukkun

2018/04/02 09:10

実際に「シンプルな状態管理をゼロから作る」を読んで少し実装を試してみたのですがFluxを使ったことが無い為、深く理解することが出来ませんでした。。 今後Vuexを使うタイミングで合わせて再度学習させていただきます。 ご回答頂きありがとうございました!
euledge

2018/04/02 09:16

例にあるものはfluxをより簡易にしたものという扱いですね。私もまだvuexは使ったことはなくこの例のやり方で実装を行っています。 共通のデータをデータストアとしてオブジェクトとしてまとめるという点と、データストアへの読み書きは必ずデータストアに持たせたメソッド(Getter,Setter)で行うという点のみ理解すればよいと思います。
tukkun

2018/04/02 09:33

なるほど。fluxを使った上で試すと理解が深まりそうですね。 > 共通のデータをデータストアとしてオブジェクトとしてまとめるという点と、データストアへの読み書きは必ずデータストアに持たせたメソッド(Getter,Setter)で行うという点のみ理解すればよいと思います。 ありがとうございます! ここまでであれば理解できそうです。(もう少し参考ページのリンク含め読み込んでみたいと思います) もう少し先になってしまいそうですが、vuexを使う前にfluxについても学習してみたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問