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

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

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

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

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

解決済

Nuxt.js: Vuexにstoreされたデータを読み出す前に描画が始まり、正しく描画できないケースがある

Eskee
Eskee

総合スコア268

Vue.js

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

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

1回答

0リアクション

1クリップ

4028閲覧

投稿2018/08/14 01:53

編集2018/08/14 02:22

Nuxt.jsを使用し、ポートフォリオサイトを作成中です。

バックエンドは、Airtableを利用し、APIをGETのみで利用しています。

Airtableはリレーショナル・データベースをGoogle SpreadSheet風に操作するSaaSですが、他テーブルとリレーションを張る時に、他テーブルのレコードIDを記録しています。

現在テーブルは

  • 作品マスタ(works)
  • クライアントマスタ(clients)
  • 代理店マスタ(agencies)

の3つを作成しています。

routingを使って作品一覧ページ(/works/)と、作品詳細ページ(/works/:id)、クライアント一覧ページ(/clients)を作成しています。

掲題の通り、作品一覧ページとクライアント一覧ページで、おそらくですが、3つのテーブルのデータを読み込む前に描画が始まり、時折エラーがでます。ほんの0.数秒の差で先に3つのテーブルのデータが読み込めていれば、問題なく描画されます。

この読み込みのタイミングを上手くコントロールする方法がわからず、質問させていただきました。

各ファイルの構成

pages/store/index.js

JavaScript

import axios from 'axios' export const state = () => ({ works: [], clients: [], agencies: [] }) export const mutations = { setWorks (state, works) { state.works = works }, setClients (state, clients) { state.clients = clients }, setAgencies (state, agencies) { state.agencies = agencies }, } export const actions = { load(ctx) { axios.get("https://api.airtable.com/v0/appxxxxxxxxx/works?api_key=keyxxxxxxxxxxxxxxxx") .then( (res) => { ctx.commit('setWorks', res.data.records ) console.log(res) }) .catch(function (error) { // handle error console.log(error); }) axios.get("https://api.airtable.com/v0/appxxxxxxxxx/clients?api_key=keyxxxxxxxxxxxxxxxx") .then( (res) => { ctx.commit('setClients', res.data.records ) console.log(res) }) .catch(function (error) { // handle error console.log(error); }) axios.get("https://api.airtable.com/v0/appxxxxxxxxx/agency?api_key=keykeyxxxxxxxxxxxxxxxx") .then( (res) => { ctx.commit('setAgencies', res.data.records ) console.log(res) }) .catch(function (error) { // handle error console.log(error); }) } }

pages/works/index.vue

JavaScript

<template lang="pug"> // 省略 </template> <script> import moment from 'moment' import _ from 'lodash' import fz from 'fuzzaldrin-plus' export default { data () { return { query: '', mediaTypeQuery: '' } }, computed: { works () { return this.$store.state.works }, clients () { return this.$store.state.clients }, queryResults() { if(!this.query) { return this.works; } const preparedQuery = fz.prepareQuery(this.query); const scores = {}; return this.works .map((option, index) => { const scorableFields = [ option.fields.Name, option.fields.mediaType, option.fields.purpose, option.fields.pubDate, ].map(toScore => fz.score(toScore, this.query, { preparedQuery })); scores[option.fields.Name] = Math.max(...scorableFields); return option; }) .filter(option => scores[option.fields.Name] > 1) .sort((a, b) => scores[b.fields.Name] - scores[a.fields.Name]) ; }, }, methods: { outputPubDate (date) { let newStyle = moment(date, 'YYYY-MM-DD') let output = newStyle.format('YYYY') return output }, getClientName (cid) { let name = _.find(this.clients, (item) => { return _.includes(item.id, cid) }) return name.fields.Name console.log(name) }, clear () { this.query = '' }, setWeb () { this.query = '' this.query = 'Web' }, setMovie () { this.query = '' this.query = 'Movie' }, setPrint () { this.query = '' this.query = 'Print' }, setPhoto () { this.query = '' this.query = 'Photo' }, }, mounted () { this.$store.dispatch('load').then(() => {console.log('dispatched')}) } } </script>

pages/clients/_id.vue(参考)

JavaScript

<template lang="pug"> // 省略 </template> <script> import _ from 'lodash' import moment from 'moment' export default { data () { return { work: null } }, computed: { works () { return this.$store.state.works }, clients () { return this.$store.state.clients }, agencies () { return this.$store.state.agencies } }, methods: { outputPubDate (date) { let newStyle = moment(date, 'YYYY-MM-DD') let output = null if(this.work.fields.mediaType === 'Web') { output = newStyle.format('YYYY年M月公開') } else { output = newStyle.format('YYYY年M月') } return output }, getClientName (cid) { let name = _.find(this.clients, (item) => { return _.includes(item.id, cid) }) return name.fields.Name }, getAgencyName (aid) { let name = _.find(this.agencies, (item) => { return _.includes(item.id, aid) }) return name.fields.Name }, getClientOtherWorks (wid) { const name = _.filter(this.clients, (item) => { return _.includes(item.id, wid) }) console.log(name) const relWorks = _.without(name[0].fields.works,this.work.id) // strip this work itself console.log('relWorks: ' + relWorks) let works = [] for (let i = 0; i < relWorks.length; i++){ let relWorksName = _.find(this.works, (item) => { return _.includes(item.id, relWorks[i]) }) works.push({ name: relWorksName.fields.Name, url: relWorks[i], image_url: relWorksName.fields.square_image }) } console.log('works: ' + JSON.stringify(works)) return works }, }, mounted () { for (let work of this.$store.state.works) { if (work.id == this.$route.params.id) { this.work = work } } if (!this.work) { // 記事が存在しない場合トップへ this.$router.push("/") } this.$store.dispatch('load') } } </script> <style lang="stylus"> </style>

pages/clients/index.vue

JavaScript

<template lang="pug"> // 省略 </template> <script> import _ from 'lodash' import fz from 'fuzzaldrin-plus' export default { data () { return { query: '' } }, computed: { works () { return this.$store.state.works }, clients () { return this.$store.state.clients }, queryResults() { if(!this.query) { return this.clients; } const preparedQuery = fz.prepareQuery(this.query); const scores = {}; return this.clients .map((option, index) => { const scorableFields = [ option.fields.Name, option.fields.industry, option.fields.businessType ].map(toScore => fz.score(toScore, this.query, { preparedQuery })); scores[option.fields.Name] = Math.max(...scorableFields); return option; }) .filter(option => scores[option.fields.Name] > 1) .sort((a, b) => scores[b.fields.Name] - scores[a.fields.Name]) ; } }, methods: { getWorkName: function(wid) { const name = _.find(this.works, (item) => { return _.includes(item.id, wid) }) return `${name.fields.Name}` }, getWorkThumb: function(wid) { const url = _.find(this.works, (item) => { return _.includes(item.id, wid) }) if(url.fields.imagePC) { return url.fields.square_image } } }, mounted () { this.$store.dispatch('load').then(() => {console.log('dispatched')}) } } </script> <style lang="stylus"> </style>

スクリーンショット

pages/works/index.vueの成功時

pages/works/index.vueの成功時

pages/works/index.vueの失敗時

pages/works/index.vueの失敗時
4~5回に1回は、このようになります。再現性は一定しません。

pages/works/index.vue成功時のConsole

イメージ説明

pages/works/index.vue失敗時のConsole

イメージ説明

このソースを書くにあたり参考にしたページ

Nuxt.js 入門 #1 – Nuxt.js によるブログアプリ制作 – chatbox blog

以下のような質問にはリアクションをつけましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

リアクションが多い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
86.12%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Vue.js

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

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。