20191115のvue.jsに関する記事は8件です。

vue.config.js で webpackの設定を使えるようにする方法

import時代についていけないエンジニアがvueで四苦八苦してると出会うwebpackの設定。

参考になるgithubやblogには、こういうやつが書いている。が、使い方がわからない。

module.exports = () => {
        plugins: [
            new webpack.IgnorePlugin(...),
        ],

これは webpack.config.js(相当のファイル)に書くのが前提なarticle。注意点は、これは vue-cli ではなくてwebpack-dev-serverの話ということ。

// webpack.config.jsとか、いろんな名前で使われてるけどとりあえずconfigぽいもの
const webpack = require('webpack');           <------ よく省略されてる
module.exports = () => {
        plugins: [
            new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
        ],

vue-cliの vue.config.js でどう書くかというと、 module.exports の中にあった第1階層を configureWebpack の中に移動するとよい。

// vue.config.js
module.exports = {
  configureWebpack: {                  <----------- これでくくる
    plugins: [
            new webpack.IgnorePlugin(...),
    ]
  }
}

参考: https://cli.vuejs.org/guide/webpack.html

簡単なことだけど、webpackをすっ飛ばしてる開発者にはこれがわからんのですよ。

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

【Vue.js】子コンポーネントに渡したデータの変更を親コンポーネントに反映する

子コンポーネントに渡したデータの変更を親コンポーネントに反映する

親から子へpropsでデータを渡したとき、
子側でそのpropsデータを直接変えようとするとvueに怒られてしまいます。

では、どうすればいいか、備忘録でまとめておきます。

親コンポーネント

<child-component v-model="parentData"/>

子コンポーネント(ChildComponent.vue)

<script>
export default {
  props: {
    parentData: {
      type: String,
      default: ''
    }
  },
  computed: {
    inputData: {
      get() {
        return this.parentData
      },
      set(newVal) {
        this.$emit('input', newVal)
      }
    }
  }
}

</script>

親から受け取ったpropscomputedを通して別名のプロパティinputDataとして受け取り、子側でinputDataに変更があった場合、set関数で新しい値を取得して親に返します。

$emitinputイベントを送ることで親のv-modelの値にデータがバインドされます。

これができるのは、v-model (= v-on:inputv-bind:value)で双方向データバインディングを行っているためですね。

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

とあるラーメン店のWordpressサイトをNuxt.js+Firebaseで作り直した話

お店と Web サイトの紹介

中華そば四つ葉
https://yotsuba628.com/

y_soba2.jpg

ラーメンが好きならご存じの方もいらっしゃるのではないでしょうか。

最寄駅から徒歩 90 分という衝撃のアクセスの不便さ。
にもかかわらず多数の祭事出店、テレビ出演、都内から足繁く通う芸能人もいる埼玉県川島町が誇る名店です。

経緯

何を隠そう私は川島町出身なのですが、学生時代に店舗に通っているうちに店長に顔を覚えてもらうことができ、Web ページの作成をさせてほしいと申し出たところ承諾を頂けて 2018 年の 3 月頃に Web ページをリリースしました。

しかし当時の技術力不足もあり、Wordpress の入門書を写経しながらのものとなり・・・
サイズの大きい画像を多数保有しているため表示が遅く、見た目が芋臭いというなんともお粗末な状態で 1 年以上運用を続けていました。

時がたち社会人になってからいくつかの技術的知識を付け、このお粗末な状態でも多くの閲覧者がいる現状に恥ずかしさを覚え、つい先日リニューアルを決意。約 3 日で完了することができました!(Nuxt.jsとFirebase便利すぎです)

どのようなスケジュールで行ったか、リニューアルした点などを記録に残しておきたいと思い、今回記事を書かせていただいております。

スケジュール

  • 1 日目 現行サイトの改善したい点洗い出し

    • 表示速度の改善
    • レイアウトの一新 など
  • 2 日目 コーディング

  • 3 日目 確認依頼、OK が出た後新規ドメインを取得して接続。旧ドメインのリダイレクト設定

実装内容

エックスサーバ + Wordpress ⇒ Nuxt.js + Firebase

  • 見た目

Vuetify を使用しました。テンプレートそのまま使えるものが多く、非常にスピーディに開発が進められて助かりました。

またこだわった点として写真を非常に良いものを使っています。プロのカメラマンさんに依頼して店舗側ともアポを取り、時間を取って写真を取っていただきました。

  • 機能

一部紹介させていただくと、お品書きページで各メニューにお気に入り機能のようなものを実装しています。以前のページでは一方的で、今回リニューアルにあたって少しだけ閲覧者の方からリアクションを貰える仕組みを追加出来たらうれしいなと思ったのがきっかけです。

コードの一部分はこんな感じです。
template 部分は

        <v-card>
          <v-img :src="rcard.src" class="white--text align-end" height="300px"></v-img>
          <v-list-item two-line>
            <v-list-item-content>
              <v-list-item-title class="subtitle">{{ rcard.title }}</v-list-item-title>
              <span>{{ rcard.content }}</span>
            </v-list-item-content>
          </v-list-item>
          <v-card-actions v-if="rcard.isFavorite">
            <v-alert color="pink lighten-4" icon="mdi-emoticon-happy">Thanks!</v-alert>
            <v-spacer></v-spacer>
            <v-btn icon>
              <v-icon>mdi-heart</v-icon>
              {{ favs[index].count }}
            </v-btn>
            <v-btn icon @click="twitterShare()">
              <v-icon>mdi-twitter</v-icon>
            </v-btn>
          </v-card-actions>
          <v-card-actions v-else>
            <v-spacer></v-spacer>
            <v-btn icon @click="addFavoriteRamen(index, rcard.id, rcard)">
              <v-icon>mdi-heart-outline</v-icon>
              {{ favs[index].count }}
            </v-btn>
            <v-btn icon @click="twitterShare()">
              <v-icon>mdi-twitter</v-icon>
            </v-btn>
          </v-card-actions>
        </v-card>

script 部分は

    addFavoriteRamen: function(index, id, shina) {
      var favRef = firebase
        .firestore()
        .collection("favorite")
        .doc(id);
      var count = this.favs[index].count;
      favRef.set({
        title: shina.title,
        src: shina.src,
        content: shina.content,
        count: (count += 1)
      });
      this.ramencards[index].isFavorite = true;
    }

よくあるお気に入り機能のようにもう一度押したら解除できるように最初はしていたのですが、短時間に何度もデータを書き込まれてしまうことを懸念してこのような仕様にしました。
(※防ぐ良い方法が思いつかなかった・・・ご教授いただければ幸いです。)

おいしそう!と感じたものがありましたらぜひクリックしてみてください。

ドメインの接続、リダイレクト設定

Firebase、 エックスサーバ、 お名前ドットコムの設定部分ですが結構躓きました・・・

時間あるときに記録しておきたいと思います。

おわりに

最後まで見てくださりありがとうございました!

本当においしいラーメン屋さんなのでぜひ一度行ってみてください。
Web サイトも随時更新予定なので見て頂けたらうれしいです。

こちらも

JobQuest ジョブマッチングSNS風サイト
https://jquest.jp/

Vue.js + Firebaseで作ってます。宜しければご覧ください!

https://qiita.com/em0/items/44bf334575ce28bc6c47

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

開発環境限定のルートを設定する方法

動機

masterブランチに食い込んでいるのに一般公開できないページが溜まってしまったので。

コード

index.js
import Vue from 'vue'
import Router from 'vue-router'
import DevRoute from './dev-route'

Vue.use(Router)

const router = new Router({
  routes: [
    ...DevRoute,
  }
})

export default router

dev-route.js
import test from '@/pages/dev/test'

const route = []

if (process.env.NODE_ENV !== 'production') {
  route.push(...[
    {
      path: '/dev/test',
      component: test 
    },
    // ルートを足すならここ
  ])
}

export default route

雑感

計画性を持って正しくブランチが切れていたら、
ビルドのたびにルートをコメント化するような羽目にはならなかったのだろう…。

スプレッド構文、超便利。

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

VueでMaterializeのChipsを上手に使う方法

Materialize というCSSフレームワークにはさまざまな物が用意されています。
今回は Chips というものを Vue.js で上手に使う方法を紹介していきます。

Chips を用意する

まずは、 Chips が画面に表示されるようにします。
今回は Vue を使うので、 Vue ファイルに書いていきます。

Test.vue
<template>
  <div class="chip">
    aaaaa
    <i class="close material-icons">close</i>
  </div>
</template>

すると、以下のような画面になります。
スクリーンショット 2019-11-15 9.56.32.png
ここで「×」ボタンを押すと、フロントでは aaaaa という Chips は削除されますが、
ページを更新すると、また aaaaa は表示されてしまいます。

Vue にて API からデータを取得・削除する

そこで、 vue ファイルに以下の記述を追加します。
axios を使用していきます。)

Test.vue
<template>
  <div
    v-for="(test, index) in tests" :key="index"
    class="chip"
  >
    {{ test.title }}
    <i class="close material-icons">close</i>
  </div>
</template>

<script>
import axios from 'axios';

data: function () {
    return {
      tests: [],
    }
  },

  mounted: function () {
    this.fetchTests();
  },

  methods: {
    fetchTests: function () {
      axios
      .get('/api/tests')
      .then((res) => {
        for (var i = 0; i < res.data.tests.length; i++) {
          this.tests.push(res.data.tests[i]);
        }
      }, (error) => {
        console.log(error);
      });
    },

    deleteTest: function (test_id) {
      if ( confirm('投稿を削除しますか?') ) {
        axios
        .delete('/api/tests/' + test_id)
        .then((res) => {
          var test_title = res.data.test.title;
          M.toast({html: '' + test_title + '」の情報を削除しました'});
        }, (error) => {
          console.log(error);
        });
      }
    },
  },
</script>

ここでは、 api/tests には idtest_title という情報が入っており、
それぞれを v-for でまわして取得しています。

上記の変更で、 Chips の「×」ボタンを押すことによって、
ページを更新したら、「×」ボタンを押した Chips は再度表示されることはなくなりました!

番外編( Chips の詳細に飛ぶ)

Chips の「×」ボタン以外の場所を押したら、
データの詳細のようなものを表示するようにしていきます。

Test.vue
<template>
  <div
    v-for="(test, index) in tests" :key="index"
    v-on:click="setTestInfo(test.id)"
    class="chip"
  >
    {{ test.title }}
    <i class="close material-icons">close</i>
  </div>

  <div class="row" v-if="testInfoBool === true">
    <div class="col s12 m12">
      <div class="card amber darken-4">
        <div class="card-content white-text">
          <span class="card-title">
            {{ tesuInfo.id }}{{ testInfo.title }}
          </span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

data: function () {
    return {
      tests: [],
      testInfo: {},
      bInfoBool: false,
    }
  },

  mounted: function () {
    this.fetchTests();
  },

  methods: {
    fetchTests: function () {
      axios
      .get('/api/tests')
      .then((res) => {
        for (var i = 0; i < res.data.tests.length; i++) {
          this.tests.push(res.data.tests[i]);
        }
      }, (error) => {
        console.log(error);
      });
    },

    setTestInfo: function (test_id) {
      axios
      .get('api/tests/' + test_id)
      .then((res) => {
        this.testInfo     = res.data.test;
        this.testInfoBool = true;
      }, (error) => {
          console.log(error);
      });
    },

    deleteTest: function (test_id) {
      if ( confirm('投稿を削除しますか?') ) {
        axios
        .delete('/api/tests/' + test_id)
        .then((res) => {
          var test_title = res.data.test.title;
          M.toast({html: '' + test_title + '」の情報を削除しました'});
        }, (error) => {
          console.log(error);
        });
      }
    },
  },
</script>

現状だと、上記の記述を追加したとしても「×」ボタンの箇所を押したら、
setTestInfodeleteTest の2つが反応してしまします。

そこで、 <template> 内の記述を変更していきます。

Test.vue
<template>
  <div
    v-for="(test, index) in tests" :key="index"
    v-on:click.self="setTestInfo(test.id)"
    class="chip"
  >
    {{ test.title }}
    <i class="close material-icons">close</i>
  </div>
</template>

<script>

・・・

v-on:click の後に .selfを追加するだけで、
要素自身がクリックされたときだけ、ハンドラが呼び出されるようになります。

参考

Chips - Materialize
イベントハンドリング — Vue.js

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

【Vue.js】【ナレッジ】オブジェクトのプロパティを子コンポーネントに渡す

オブジェクトのプロパティを子コンポーネントに渡す

下記のようなオブジェクトのデータを子コンポーネントに渡す時、方法はおおよそ2パターン!

boardboardTitleを子コンポーネントで表示させたい:writing_hand:

"board": {
  "boardTitle": "11/12 TODO",
  "todos": []
}

パターン① オブジェクトを省略記法を使って渡す

親コンポーネント

<board-index v-bind="board"/>

子コンポーネント(BoardIndex.vue)

<template>
  <div>{{ boardTitle }}</div>
</template>

<script>
export default {
  props: {
    boardTitle: {
      type: String,
      required: true
    },
    todos: {
      type: Array
    }
  }
}
</script>

パターン② オブジェクトをそのまま渡す

親コンポーネント

<board-index :board="board"/>

子コンポーネント(BoardIndex.vue)

<template>
  <div>{{ board.boardTitle }}</div>
</template>

<script>
export default {
  props: {
    board: {
      type: Object,
      default: () => ({
        boardTitle: '',
        todos: []
      }),
      required: true
    }
  }
}
</script>

別にオブジェクトのプロパティを個々にバインドさせる方法もあります。
場面によって使い分けることができそうです。

話が少し逸れますが、パターン②でboarddefaultを関数で返しています。
これは、
propsのデフォルト値に指定したオブジェクトや配列は、参照が共有される
ことを防ぐためです。

参照が共有されてしまうと、複数のvueインスタンスが同一のオブジェクトを参照し、同一のオブジェクトに変更を加えてしまう状況が発生します。

Vueのナレッジを忘れないように少しずつためていきます:fist:
ご指摘などもお待ちしています!

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

【Vue.js】オブジェクトのプロパティを子コンポーネントに渡す

オブジェクトのプロパティを子コンポーネントに渡す

下記のようなオブジェクトのデータを子コンポーネントに渡す時、方法はおおよそ2パターン!

boardboardTitleを子コンポーネントで表示させたい:writing_hand:

"board": {
  "boardTitle": "11/12 TODO",
  "todos": []
}

パターン① オブジェクトを省略記法を使って渡す

親コンポーネント

<board-index v-bind="board"/>

子コンポーネント(BoardIndex.vue)

<template>
  <div>{{ boardTitle }}</div>
</template>

<script>
export default {
  props: {
    boardTitle: {
      type: String,
      required: true
    },
    todos: {
      type: Array
    }
  }
}
</script>

パターン② オブジェクトをそのまま渡す

親コンポーネント

<board-index :board="board"/>

子コンポーネント(BoardIndex.vue)

<template>
  <div>{{ board.boardTitle }}</div>
</template>

<script>
export default {
  props: {
    board: {
      type: Object,
      default: () => ({
        boardTitle: '',
        todos: []
      }),
      required: true
    }
  }
}
</script>

別にオブジェクトのプロパティを個々にバインドさせる方法もあります。
場面によって使い分けることができそうです。

話が少し逸れますが、パターン②でboarddefaultを関数で返しています。
これは、
propsのデフォルト値に指定したオブジェクトや配列は、参照が共有される
ことを防ぐためです。

参照が共有されてしまうと、複数のvueインスタンスが同一のオブジェクトを参照し、同一のオブジェクトに変更を加えてしまう状況が発生します。

Vueのナレッジを忘れないように少しずつためていきます:fist:
ご指摘などもお待ちしています!

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

【Nuxt.js】 Jestを使ってコンポーネントのテスト書く

今までフロントエンドでテストケースを書いたことがありませんでしたが、とある案件で挑戦する機会があったのでやってみました

やりたいこと

  • 作成済みの Nuxt.jsアプリに、後からJestを導入する
  • コンポーネントにpropsで渡した値が表示されるか確認するのテストを用意して実行する

事前準備

ライブラリのインストール

  • NuxtアプリでJestを動かすために必要なライブラリが入っていない場合は導入する
    (警告を消すために色々と入れましたが、全部は必要ないかもです)
yarn add -D @babel/core @nuxt/babel-preset-app @vue/test-utils babel-core babel-jest babel-preset-env jest vue-jest vue-template-compiler

設定ファイルの用意

Nuxtアプリのルートディレクトリ直下に用意

jest.config.js
module.exports = {
  transform: {
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest'
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1'
  },
  moduleFileExtensions: ['js', 'json', 'vue']
}
.babelrc
{
  "presets": [
    ["env", {
      "modules": false
    }]
  ],
  "env": {
    "test": {
      "presets": [
        ["env", {
          "targets": {
            "node": "current"
          }
        }]
      ]
    }
  }
}

コンポーネント作成

propsにtextを渡して、それを表示させるだけのシンプルなコンポーネントです

Button.vue
<template>
  <nuxt-link>
    {{ text }}
  </nuxt-link>
</template>

<script>
export default {
  props: {
    text: {
      type: String,
      required: true
    }
  }
}
</script>

テストケース作成

test/components/Button.spec.js
import { mount } from '@vue/test-utils'
import Button from '~/components/shared/Button'

describe('Button', () => {
  test('Display text', () => {
    const props = {
      text: '検索する'
    }
    const wrapper = mount(Button, {
      propsData: props
    })
    // propsに指定したデータが正しく表示されること
    expect(wrapper.props('text')).toBe(props.text)
  })
})

今回は簡単なpropsの表示テストだけですが、追って別記事でVuexのテストサンプルも作成する予定

参考

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