20200217のvue.jsに関する記事は11件です。

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>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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で定義したプロパティの値を子へ受け渡します。

以下の画面をpropsを使って表示します。
スクリーンショット 2020-02-17 16.12.55.png

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へ表示します。

vue_emit.gif

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のソース内容は上書きして以降は使っていきます。

最終的なページの動きは以下の通りです。
vue2.gif

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】 コンポーネント間の通信について解説

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 241日目

100日チャレンジの241日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

241日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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>

▼for文で取ってきたtemperetureの中身
スクリーンショット 2020-02-17 16.17.07.png

▼子コンポーネント

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"></p>
  </div>
</template>
<script>
import { weatherMixin } from "@/mixins/weather-mixin";
export default {
  name: "TheWeatherItem",
  mixins: [weatherMixin]
};
</script>

▼現在の画面
一旦欲しい値は時間と気温の2つ(アイコン部分は後ほど修正)
スクリーンショット 2020-02-17 16.22.32.png

やってみる

子コンポーネントの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-17 16.37.16.png

修正

フィルターを作って取ってきた値にかける
スクリーンショット 2020-02-17 16.46.53.png

できた?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React/Vueを利用したサービス

概要

SPAの代表的なフレームワークであるReactとVue(+Nuxt.js)を利用したサービスについて。

サービス一覧(React)

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/34ZkxZ1pwylQ

Nuxt.js

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

moment.jsをVue.jsで使う

タイムスタンプを日付に変換したい

YYYY/MM/DD hh:mmの形式で欲しい
スクリーンショット 2020-02-17 12.47.30.png

moment.jsをインストール

npm install moment

フィルターをつくる

filter.js
import 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>

結果

YYYY/MM/DD hh:mmの形式でとれた?
スクリーンショット 2020-02-17 15.45.51.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue)簡単なローンシミュレーター

完成版

Vue勉強用


See the Pen
LYVNmJq
by sphenisc (@sphenisc)
on CodePen.



年齢と年収を入力すると借入可能額を計算するだけの簡単なコードです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxtで本番リリースした際にのみスタイル崩れ

Nuxtで運用しているサービスで productionビルドリリース したもののみスタイル崩れが発生する、という不具合に遭遇。
詳しく調査した結果、スタイル適用順序がproductionビルドとそれ以外とで違ってくる、というのが原因でした。

確認した環境

発生パターンと根本的な原因

これは幅広く発生するというものではなく、特定の条件の場合にのみ発生。
以下のように 自ら定義したカスタムコンポーネントにスタイル上書きをする 場合にのみ発生します。

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=stagingNODE_ENV=development の場合には

  1. CustomComponentのstyleタグ
  2. Indexページのstyleタグ

の順でstyleタグが挿入されるため、想定した通りにCustomComponentの背景が黄色でスタイル上書きされます。

ですが NODE_ENV=production ではこれが逆転し

  1. Indexページのstyleタグ
  2. 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として吐き出し読み込みます。
この設定で

  1. components.css
  2. styles.css
  3. pages/index.css(ページ単位で生成され遷移のたびに追加される)

の順序でcss読み込みされるようになるため、pages/indexで定義したCustomComponentの上書きスタイルが常に想定通りに適用される、という寸法です。

1のstyle属性上書きの方が手軽ではありますが、こちらの場合は開発者が productionビルドの癖を意識せず自由に実装できる というメリットがあります。

まとめ

どちらの対処方法も一長一短ではありますが、私が試行錯誤した範囲ではこの2つしか対処方法が見つかりませんでした。
(というかなんでproductionビルドでこの挙動になってるの・・・)
何か良い方法がありましたらぜひコメントでつっこみお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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でのレンダリング後のタイトルが

/1ID「3」の記事
/2ID「1」の記事
/3ID「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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.nextTickとthis.$nextTick

再描画を待つために使う nextTick ですが、グローバルな Vue.nextTick を使うべきか、それとも this.$nextTick を使うべきか。
迷ったら this.$nextTick を使った方がよさそうと感じました。

Vue.jsでは アロー関数 は期待しない動作になる場合があります。つまり this が直感的に使えないです。こちらの記事にも書いています。

例えば アロー関数 がそのまま使えない watchVue.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 でよいと感じます。

参考

公式リファレンス

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsのSSRでミドルウェアを使ってVuexに値をストアする方法

SSRのクライアントサイドでレンダリングされる前にstoreに保存するにはミドルウェアを使います。
そのときの参考に残します。
ここでは例にjwtをstoreに保存することをやってみます。普通はcookieとかに保存しますが一例として。

nuxt.config.js
module.exports = {
//省略
router: {
 middleware: 'hoge' //ミドルウェアのファイル名
},
//省略
}
middleware/hoge.js
export default async ({ store }) => {
  if (store.state.auth.token === null) {
    await store.dispatch('auth/setToken')
  }
}
store/fuga.js
import 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)
    )
  }
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む