- 投稿日:2019-11-15T17:55:27+09:00
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をすっ飛ばしてる開発者にはこれがわからんのですよ。
- 投稿日:2019-11-15T17:27:54+09:00
【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>親から受け取った
props
をcomputed
を通して別名のプロパティinputData
として受け取り、子側でinputData
に変更があった場合、set
関数で新しい値を取得して親に返します。
$emit
でinput
イベントを送ることで親のv-model
の値にデータがバインドされます。これができるのは、
v-model
(=v-on:input
とv-bind:value
)で双方向データバインディングを行っているためですね。
- 投稿日:2019-11-15T11:58:45+09:00
とあるラーメン店のWordpressサイトをNuxt.js+Firebaseで作り直した話
お店と Web サイトの紹介
中華そば四つ葉
https://yotsuba628.com/ラーメンが好きならご存じの方もいらっしゃるのではないでしょうか。
最寄駅から徒歩 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で作ってます。宜しければご覧ください!
- 投稿日:2019-11-15T11:09:24+09:00
開発環境限定のルートを設定する方法
動機
masterブランチに食い込んでいるのに一般公開できないページが溜まってしまったので。
コード
index.jsimport Vue from 'vue' import Router from 'vue-router' import DevRoute from './dev-route' Vue.use(Router) const router = new Router({ routes: [ ...DevRoute, } }) export default routerdev-route.jsimport test from '@/pages/dev/test' const route = [] if (process.env.NODE_ENV !== 'production') { route.push(...[ { path: '/dev/test', component: test }, // ルートを足すならここ ]) } export default route雑感
計画性を持って正しくブランチが切れていたら、
ビルドのたびにルートをコメント化するような羽目にはならなかったのだろう…。スプレッド構文、超便利。
- 投稿日:2019-11-15T10:52:17+09:00
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>すると、以下のような画面になります。
ここで「×」ボタンを押すと、フロントでは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
にはid
とtest_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>現状だと、上記の記述を追加したとしても「×」ボタンの箇所を押したら、
setTestInfo
とdeleteTest
の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
を追加するだけで、
要素自身がクリックされたときだけ、ハンドラが呼び出されるようになります。参考
- 投稿日:2019-11-15T01:46:37+09:00
【Vue.js】【ナレッジ】オブジェクトのプロパティを子コンポーネントに渡す
オブジェクトのプロパティを子コンポーネントに渡す
下記のようなオブジェクトのデータを子コンポーネントに渡す時、方法はおおよそ2パターン!
board
のboardTitle
を子コンポーネントで表示させたい"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>別にオブジェクトのプロパティを個々にバインドさせる方法もあります。
場面によって使い分けることができそうです。話が少し逸れますが、パターン②で
board
のdefault
を関数で返しています。
これは、
propsのデフォルト値に指定したオブジェクトや配列は、参照が共有される
ことを防ぐためです。参照が共有されてしまうと、複数のvueインスタンスが同一のオブジェクトを参照し、同一のオブジェクトに変更を加えてしまう状況が発生します。
Vueのナレッジを忘れないように少しずつためていきます
ご指摘などもお待ちしています!
- 投稿日:2019-11-15T01:46:37+09:00
【Vue.js】オブジェクトのプロパティを子コンポーネントに渡す
オブジェクトのプロパティを子コンポーネントに渡す
下記のようなオブジェクトのデータを子コンポーネントに渡す時、方法はおおよそ2パターン!
board
のboardTitle
を子コンポーネントで表示させたい"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>別にオブジェクトのプロパティを個々にバインドさせる方法もあります。
場面によって使い分けることができそうです。話が少し逸れますが、パターン②で
board
のdefault
を関数で返しています。
これは、
propsのデフォルト値に指定したオブジェクトや配列は、参照が共有される
ことを防ぐためです。参照が共有されてしまうと、複数のvueインスタンスが同一のオブジェクトを参照し、同一のオブジェクトに変更を加えてしまう状況が発生します。
Vueのナレッジを忘れないように少しずつためていきます
ご指摘などもお待ちしています!
- 投稿日:2019-11-15T01:10:01+09:00
【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.jsmodule.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.jsimport { 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のテストサンプルも作成する予定
参考