- 投稿日:2020-02-17T22:50:24+09:00
vue.js内で使うaxiosで複数APIリクエストをまとめて行う方法
vue.js内でaxiosを使う場合複数のAPIリクエストをしたい場合があります。その時にきれいにやる方法です。
index.vue<template> 省略 </template> <script> import axios from 'axios' function getData () { return Promise.all([ axios.get(process.env.API_URL + '/posts'), axios.get(process.env.API_URL + '/services'), axios.get(process.env.API_URL + '/shops'), ]).then(([posts, services, shops]) => { const data = {} data.posts = posts.data data.services = services.data data.shops = shops.data return Promise.resolve(data) }) } export default { async asyncData () { const data = await getData() return { posts: data.posts, shops: data.shops, services: data.services } } } </script> <style> 省略 </style>
- 投稿日:2020-02-17T21:39:14+09:00
vueのpropsとemitを使ったコンポーネント間の値の受け渡し
1.はじめに
Vueのコンポーネントを学習する中で、propsとemitの理解が難しかったので、ここでまとめたいと思います。
記事の中に検証ソースをあげますが、webpack(ver:4.41.6)で動かしておりファイル構成は以下の通りです。
├─public │ ├── bundle.js │ └── index.html ├─src/ ├── components │ ├── emitChild.vue │ ├── parent.vue │ └── propsChild.vue └── index.js本記事ではpublic/index.htmlへ親コンポーネントを使用します。よってindex.htmlは以下の内容で固定です。
public/index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>VueComponent</title> </head> <body> <div id="app"> <parent></parent> </div> <script src="/public/bundle.js"></script> </body> </html>webpackの使い方は以下のQiita記事を参照しているため、こちらで確認してください。
webpack4入門2.propsについて
propsは親⇨子へ値を受け渡す際に使用するプロパティです。
親コンポート内に、v-bindで定義したプロパティの値を子へ受け渡します。parentとpropsChildのファイルを編集します。
src/components/parent.vue<template> <div id="parent-area"> <!-- 子コンポーネントへ受け渡すcontext,numberを定義 --> <propsChild :context="text" :number="num"></propsChild> </div> </template> <script> import propsChild from "./propsChild.vue"; export default { components:{ emitChild:emitChild, propsChild:propsChild, }, data(){ return { // 受け渡す値はdataプロパティで設定しないとエラーするので注意 text:'このテキストを子コンポーネントへ受け渡します', num:1000 } } } </script>親コンポーネントで子コンポーネントを定義する際に、v-bindでpropsへ渡すプロパティを定義します。
私自身つまづいたところですが、propsプロパティ(context)へ渡すのは値は、vueで定義済みのプロパティ(text:dataで定義したプロパティ等)の必要があります。
直接値を埋め込むと定義されていないとエラーしますので注意しましょう。src/components/propsChild.vue<template> <div id="props-area"> <h3>Props Area</h3> <p>Content:{{context}}</p> <p>Number:{{number}}</p> </div> </template> <script> export default { // 親コンポーネントで定義したプロパティをpropsで定義して受け取る props:{ context:String, number:Number }, } </script>propsの定義は上記のようにしていますが、他にも記述方法があり、型のバリデーションやオブジェクトの定義などが可能です。
上の例では、String型とNumberのバリデーションをしています。
公式ドキュメントに詳細があるので一度眺めるのがいいと思います。3.emitについて
emitは子⇨親へ値を受け渡す方法です。(ここは理解するのにちょっと苦戦しました。。。)
子⇨親のデータ受け渡しはイベントを利用して行います。以下のような動的ページをemitを使い表示します。
EimitAreaで入力した値を親コンポーネントのResultAreaへ表示します。parentとemitChildのファイルを編集します。
src/components/parent.vue<template> <div id="parent-area"> <!-- 「emit-event」は子コンポーネントと繋ぐ定義、「receive」は親コンポーネントで子コンポーネントからの値を取得するためのメソッド --> <emitChild @emit-event="receive"></emitChild> <div id="result"> <!-- 子コンポーネントからの値を表示するエリア --> <p>Result Area</p> <p>Result Text:{{result1}}</p> <p>Result Number:{{result2}}</p> </div> </div> </template> <script> import emitChild from './emitChild.vue'; export default { components:{ emitChild:emitChild, }, data(){ return{ result1:'', result2:'' } }, methods:{ receive(data1, data2){ //data1,data2は子コンポーネントから受け取った値 this.result1 = data1; this.result2 = data2; } } } </script>親と子のコンポーネントを繋ぐイベント(emit-event)を親の中の子コンポーネントで定義します。
また、methodsプロパティでは、子コンポーネントからの値を受け取ります。
data1,data2の引数は子コンポーネントから受け取った値になります。src/components/emitChild.vue<template> <div id="emit-area"> <p>Emit Area</p> <label>Text:<input type="text" v-model="context"></label> <label>Number:<input type="number" v-model="number"></label> <button @click="onClickEmit">送信</button> </div> </template> <script> export default { data(){ return{ context:'', number:0, } }, methods:{ onClickEmit(){ //親コンポーネントで定義した「emit-event」を第一引数へ定義し、以降の引数は親コンポーネントへ渡す値を定義 this.$emit('emit-event', this.context, this.number); } } } </script>ここでは子コンポーネントのmethodsで親コンポーネントへ値を渡します。なので、必ずしもclickイベントで定義しなくてはいけないわけではありません。
$emitの第一引数には、親コンポーネントで定義したイベントを入れて、以降の引数には親コンポーネントへ渡す値を入れてあげます。
なので、親コンポーネントのmehods>receiveイベントの引数(data1、data2)はここのthis.content,this.number)となります。4.コンポーネント間の値の受け渡しの例
以下のようなコンポーネント間で値の受け渡しをやってみます。
emitChild ⇨ parent ⇨ propsChild※2,3章で使ったemitchildとpropsChildのソース内容は上書きして以降は使っていきます。
src/components/parent.vue<template> <div id="parent-area"> <!-- <emitChild v-model="content"></emitChild> --> <emitChild :value="content" @input="content = $event.target.value"></emitChild> <propsChild :text="content"></propsChild> </div> </template> <script> import emitChild from './emitChild.vue'; import propsChild from './propsChild.vue'; export default { components:{ emitChild:emitChild, propsChild:propsChild, }, data() { return { content:'', } }, } </script>emitChildで子コンポートからの値を取得しています。
また、この書き方はv-modelでも置換可能です。理由とすると、v-modelを解体すると以下の書き方ためです。(公式リファレンス参照)<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" >この$eventはinputタグのインスタンスなため、inputタグのメソッドが利用可能になります。
src/components/emitChild.vue<template> <div id="emit-area"> <h3>Emit Area</h3> <textarea :value='context' @input="OnInput"></textarea> </div> </template> <script> export default { methods: { OnInput($event){ this.context = $event.target.value; // 引数へinputイベントを投げます this.$emit('input', $event); } }, data() { return { context:'' } }, } </script>src/components/propsChild.vue<template> <div id="props-area"> <h3>Props Area</h3> <p>{{text}}</p> </div> </template> <script> export default { props:{ text:String, }, } </script>emitChildとpropsChildのemit、propsの使い方は前章とほぼ同様です。
5.まとめ
vueのpropsとemitが自分的にわかりにくかったのでまとめてみました。
値の受け渡しによって、コンポーネントの部品としての可用性が上がり、細分化が可能だと思うので積極的に使っていこうと思います。
また記事内で間違いがあればご指摘お願いいたします。6.参照
Vue.jsコンポーネント入門 (5) コンポーネント間のコミュニケーション
【Vue.js】 コンポーネント間の通信について解説
- 投稿日:2020-02-17T20:55:49+09:00
初心者によるプログラミング学習ログ 241日目
100日チャレンジの241日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
241日目は、
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) February 16, 2020
241日目
・youtubeでvue.jsミニアプリ模写#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-02-17T16:52:36+09:00
【Vue.js】for文を使って取ってきたオブジェクトのプロパティを子コンポーネントに渡す
問題
Vue.jsでfor文を使って配列を回したが、取ってきたオブジェクトのプロパティを子コンポーネントに渡す方法がわからない
やりたいことの整理
for文で取ったtemperature内の値をTheWeatherItemコンポーネントに入れたい
▼親コンポーネント
TheToday.vue<template> <div class="today"> <TheTitle label="Today" :is-today="true" :day="displayDay()"></TheTitle> <div class="today-weather"> <template v-for="(temperature, index) in temperature.todayTemperature"> <TheWeatherItem :key="index"></TheWeatherItem> </template> </div> </div> </template>▼子コンポーネント
TheWeatherItem.vue<template> <div class="weather-item"> <p class="weather-item__time">Now</p> <img src="img/rain.png" alt="" class="weather-item__img" /> <p class="weather-item__temp">5°</p> </div> </template> <script> import { weatherMixin } from "@/mixins/weather-mixin"; export default { name: "TheWeatherItem", mixins: [weatherMixin] }; </script>▼現在の画面
一旦欲しい値は時間と気温の2つ(アイコン部分は後ほど修正)
やってみる
子コンポーネントのpropsを追加すれば解決できそう
▼子コンポーネント修正TheWeatherItem.vue<template> <div class="weather-item"> <p class="weather-item__time">{{ content.time }}</p> <img src="img/rain.png" alt="" class="weather-item__img" /> <p class="weather-item__temp">{{ content.temperature }}</p> </div> </template> <script> import { weatherMixin } from "@/mixins/weather-mixin"; export default { name: "TheWeatherItem", mixins: [weatherMixin], //props追加 props: { content: { type: Object, required: true } } };▼親コンポーネント修正
TheToday.vue<template> <div class="today"> <TheTitle label="Today" :is-today="true" :day="displayDay()"></TheTitle> <div class="today-weather"> <template v-for="(temperature, index) in temperature.todayTemperature"> <!--contentにtemperatureを渡す--> <TheWeatherItem :key="index" :content="temperature"></TheWeatherItem> </template> </div> </div> </template>結果
取れたけどタイムスタンプのままだったり温度が四捨五入されていないので修正が必要
修正
できた?
- 投稿日:2020-02-17T16:04:59+09:00
React/Vueを利用したサービス
概要
SPAの代表的なフレームワークであるReactとVue(+Nuxt.js)を利用したサービスについて。
サービス一覧(React)
- Yahoo Japan
- freee
- Airbnb
- Kibela
Abeja
- ABEJAの技術スタックを公開します(2019年11月版)
- https://tech-blog.abeja.asia/entry/tech-stack-201911
Backlog(ヌーラボ)
- ヌーラボの川端が、React勉強会@福岡 vol.2 に登壇します #react_fukuoka
- https://nulab.com/ja/blog/nulab/2019-6-5-react-fukuoka-announcement/
Slack
Uber
Vue.js
Vue.jsを導入している国内の企業・サービス一覧
- Nintendo
- https://my.nintendo.com/
- ZOZOテクノロジーズ
- https://speakerdeck.com/amatsukiku/frontend-architecture-design-of-zozo
- 一休com
- 一休.comレストランのスマートフォン検索ページがSPAになりました
- https://user-first.ikyu.co.jp/entry/2018/10/09/080000
- Retty
- Webサービスを支えるユーザログ基盤開発@Retty
- https://engineer.retty.me/entry/2018/12/01/120019
- DMM
- DMM動画サービスの問題を解決しようとしている話(コンポーネント編)
- https://inside.dmm.com/entry/2018/07/12/components
- Gunosy
- 社内管理画面を Vue + Go で作る
- https://tech.gunosy.io/entry/admin-vue-go
- マンガZERO
- https://blog.nagisa-inc.jp/archives/2980
- ITプロパートナーズ
- サービスの管理画面で Vue + element.ui を活用する(table編)
- https://tech.itpropartners.jp/entry/2018/12/04/132144
- M3
- 「レガシーアプリケーションのリニューアルにNuxt.jsで戦う」というタイトルでVue Fes Japan 2018 Reject Conferenceに登壇してきました
- https://suzan2go.hatenablog.com/entry/2018/11/10/225810
- DeNA
- アバター着せ替えアプリ開発におけるフロントエンド技術(Vue.js活用事例) #denatechcon
- https://www.slideshare.net/dena_tech/vuejs-denatechcon-72603570
- Codeal
- コデアルリニューアルと技術的なトピック
- https://www.codeal.work/contents/archives/6448
- CoupLink
- 弊社マッチングアプリ「CoupLink」をVue.jsでSPA化しました
- https://tech.linkbal.co.jp/4834/
- ALIS
- 【ALISのシステム】フロントエンドアーキテクチャ:その1
- https://alis.to/AB2/articles/34ZkxZ1pwylQNuxt.js
- リクルートライフスタイル
- note
- noteのフロントエンドをNuxt.jsへ刷新します
- https://note.mu/konpyu/n/n9b7bf4343514
- NoSchool
- 【実録】WordPressサイトをAWS+Laravel+Nuxtにフルリプレイスした話(技術選定編)
- https://qiita.com/mejileben/items/f68a50ec9164b261b9cd
- TERIYAKI
- Nuxtのプロダクション事例
- https://speakerdeck.com/tameto/nuxtfalsepurodakusiyonshi-li
- LINE
- Vue Fes Japan 2018 LINE株式会社 LunchスポンサーLT
- LINEとNuxtの話
- RoomClip
- 投稿日:2020-02-17T15:53:11+09:00
moment.jsをVue.jsで使う
タイムスタンプを日付に変換したい
moment.jsをインストール
npm install momentフィルターをつくる
filter.jsimport moment from "moment"; export const filDate = value => { if (value === "") { return ""; } return moment.unix(value).format("YYYY/MM/DD HH:mm"); }; export default { install(vue) { vue.filter("filDate", filDate); vue.prototype.$customFilter = { filDate }; } };main.jsに下記2文を追加して使えるようにする
import filter from "@/plugins/filter"; //インポートする Vue.use(filter); //使う使ってみる
<template> <div class="today"> <template v-for="temperature in temperature.todayTemperature"> {{ temperature.time | filDate }} </template> </div> </template>結果
- 投稿日:2020-02-17T14:18:37+09:00
Vue)簡単なローンシミュレーター
- 投稿日:2020-02-17T12:52:44+09:00
Nuxtで本番リリースした際にのみスタイル崩れ
Nuxtで運用しているサービスで
productionビルドリリース
したもののみスタイル崩れが発生する、という不具合に遭遇。
詳しく調査した結果、スタイル適用順序がproductionビルドとそれ以外とで違ってくる、というのが原因でした。確認した環境
- nuxt@2.11.0
- スタイルはsass(scss)を選択
発生パターンと根本的な原因
これは幅広く発生するというものではなく、特定の条件の場合にのみ発生。
以下のように自ら定義したカスタムコンポーネントにスタイル上書きをする
場合にのみ発生します。components/CustomComponent.vue<template> <div class="container"> </div> </template> <style lang="scss" scoped> .container { width: 100%; height: 100px; background-color: #f00; // 背景色に赤を設定 } </style>pages/index.vue<template> <div> <CustomComponent class="override" /> </div> </template> <style lang="scss" scoped> .override { background-color: #ff0; // CustomComponentの背景色を黄色で上書き } </style> <script> import CustomComponent from '~/components/CustomComponent'; export default { components: { CustomComponent, }, }; </script>こちらのコードを実行する際、
NODE_ENV=staging
やNODE_ENV=development
の場合には
- CustomComponentのstyleタグ
- Indexページのstyleタグ
の順でstyleタグが挿入されるため、想定した通りにCustomComponentの背景が黄色でスタイル上書きされます。
ですが
NODE_ENV=production
ではこれが逆転し
- Indexページのstyleタグ
- CustomComponentのstyleタグ
の順でstyleタグ挿入されてしまうためスタイル上書きが効かなくなり、結果想定したスタイルが適用されなくなります。
対処方法
1. class属性でなくstyle属性で上書き
単純にスタイル詳細度を高めることにより、適用順序に関係なく上書きスタイルが想定通り適用されるようにします。
pages/index.vue<template> <div> <CustomComponent :style="{'background-color': '#ff0'}" /> </div> </template>2. extractCSSをONにしてスタイル適用順序をコントロール
若干無理矢理感とextractCSSを入れること自体に抵抗があるかもしれませんが、一応載せます。
nuxt.config.js... build: { extractCSS: true, optimization: { splitChunks: { cacheGroups: { // globalスタイル styles: { name: 'styles', test: /\.(css|scss)$/, chunks: 'initial', enforce: true }, // components配下のスタイル components: { name: 'components', test: /app\/components/, chunks: 'all', enforce: true }, } } }, } ...このようにbuildオプションを設定してcomponents配下のスタイルのみ別の外部cssとして吐き出し読み込みます。
この設定で
- components.css
- styles.css
- pages/index.css(ページ単位で生成され遷移のたびに追加される)
の順序でcss読み込みされるようになるため、pages/indexで定義したCustomComponentの上書きスタイルが常に想定通りに適用される、という寸法です。
1のstyle属性上書きの方が手軽ではありますが、こちらの場合は開発者が
productionビルドの癖を意識せず自由に実装できる
というメリットがあります。まとめ
どちらの対処方法も一長一短ではありますが、私が試行錯誤した範囲ではこの2つしか対処方法が見つかりませんでした。
(というかなんでproductionビルドでこの挙動になってるの・・・)
何か良い方法がありましたらぜひコメントでつっこみお願いします!
- 投稿日:2020-02-17T12:08:59+09:00
nuxtjsのhead()で$nuxt.$route.paramsは使わないほうがいいのかもしれない?
pages配下のページコンポーネントのhead()内で
this.$nuxt.$route.params
を通してページのidなどをtitleやmetaに使用したい場合に
SSRのリクエストが複数来ると
リクエストをまたがってあべこべのthis.$nuxt.$route.params
が使われてしまうということがあった。例えば
head() { return { title: `ID「${this.$nuxt.$route.params.id}」の記事` } }と設定したページに対して
idが1,2,3のurl(/1
,/2
,/3
)それぞれに同時にアクセスすると
SSRでのレンダリング後のタイトルが
/1
でID「3」の記事
/2
でID「1」の記事
/3
でID「3」の記事
のようにurlと一致しない状態になった(一番最後のリクエストだけはうまく動く模様)。
あくまでもこの現象が起きるのはheadだけのようで
fetchやasyncDataでは発生しなかった。
もちろんprocess.browser
でも発生しない。今回はたまたま
$nuxt.$route.params
の値を使ってfetchでstoreにapiのデータを保存する仕様にしていたため
head内で直接$nuxt.$route.params
は使わず、fetchでstoreに保存したstateの値を利用することでこの問題は回避できた。サーバー側の設定の問題なのかもしれないがどう対応するのが適切なのだろうか。。。
備考
nuxt.js v2.11.0
- 投稿日:2020-02-17T08:05:09+09:00
Vue.nextTickとthis.$nextTick
再描画を待つために使う
nextTick
ですが、グローバルなVue.nextTick
を使うべきか、それともthis.$nextTick
を使うべきか。
迷ったらthis.$nextTick
を使った方がよさそうと感じました。Vue.jsでは アロー関数 は期待しない動作になる場合があります。つまり
this
が直感的に使えないです。こちらの記事にも書いています。例えば アロー関数 がそのまま使えない
watch
でVue.nextTick
を使う場合は以下のようになります。this
の参照を同一スコープ内に保持しておく必要があります。watch: { hoge() { const vm = this; // 参照を保持 Vue.$nextTick(function() { vm.fuga++; // thisを使う処理 }); } }これに対して
this.$nextTick
を使うとthis
の束縛が行われるため直感的です。watch: { hoge() { this.$nextTick(function() { this.fuga++; // thisがそのまま使える }); } }迷ったらとりあえず
this.$nextTick
でよいと感じます。参考
- 投稿日:2020-02-17T00:13:47+09:00
Nuxt.jsのSSRでミドルウェアを使ってVuexに値をストアする方法
SSRのクライアントサイドでレンダリングされる前にstoreに保存するにはミドルウェアを使います。
そのときの参考に残します。
ここでは例にjwtをstoreに保存することをやってみます。普通はcookieとかに保存しますが一例として。nuxt.config.jsmodule.exports = { //省略 router: { middleware: 'hoge' //ミドルウェアのファイル名 }, //省略 }middleware/hoge.jsexport default async ({ store }) => { if (store.state.auth.token === null) { await store.dispatch('auth/setToken') } }store/fuga.jsimport axios from 'axios' export const state = () => ({ token: null }) export const mutations = { login (state, token) { state.token = token } } export const actions = { async setToken ({ commit }) { await axios.post('http://api.your-site.com/v1/auth', { identifier: 'hogefuga@hoga.com', password: 'password' }).then(res => commit('login', res.data.jwt) ) } }