20200728のvue.jsに関する記事は9件です。

Vueの環境構築を初心者ながらしてみた

入社研修で使ったVue.jsの環境構築をしたい

自分と彼女の写真をまとめたサイトを
作ろう!と思った笑
時間があまりないため、使ったことあるVue.jsをフロントのフレームとして採用

詰まったところ

・VueCliバージョン管理
Vueのバージョン確認むずかった。
vue-cliでインストールしてた。

VueCliバージョン管理が問題

結論
vue-cli@vue/cliのさにマジでひっかけられた。

$ npm run dev ????

サクッと検索して出てきた記事を元に環境構築をしてみたけど
サーバーを動かすところで$ npm run devを入力することに。

ここで、思いっきり違和感。
研修の時は$ npm run serveでやったぞ。。。
何かがおかしい。。。
(あとで、package.jsonの問題らしい記事を見つけたけど。)

バージョン確認

バージョン確認をしてみて、
VueCliが2.xx.xなバージョンになってた。
バージョンの確認方法(以外に苦戦した)

$ npm vue --version

これはnpmのバージョンが出ます。

$ vue --version

これはVue/Cliのバージョンがでます。

え、Vueのバージョンは???

?プロジェクトごとにインストールしてる時
$ npm list vue

プロジェクトにVueがインストールされてないと
└── (empty)を返されます。

?グローバルにインストールしている場合
$ npm list -g vue

└─┬ @vue/cli@4.4.6
  └── vue@2.6.11みたいな感じで返ってきます(2020/07/28現在)

インストールすべきは@vue/cli

そして、

$ npm install -g vue-cli
と
$ npm install -g @vue/cli

の差を発見できました!
どうやらVueCli3からは$ npm install -g @vue/cliを使用するのが正しいようです。

公式っぽい下記のサイトには$ npm install -g vue-cliで書いてあるのでご注意を。。
https://v1-jp.vuejs.org/guide/installation.html

ちなみに、package.jsonをいじったら
$ npm run sureveできるっぽい

プロジェクト作成

$ vue create [プロジェクト名]

聞かれることには、とりあえずエンターで応答。
うまくいくと

?  Successfully created project vue.
?  Get started with the following commands:

 $ cd [プロジェクト名]
 $ npm run serve

と教えてもらえるので、それに沿って$ npm run serveする!
うまくいくと

App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.2.200:8080/

と出るので、好きな方にアクセスして

Welcome to Your Vue.js App

を拝みましょう。
お疲れ様でした!

参考サイト

https://qiita.com/Sinhalite/items/64731c0a84cadb516a24
https://qiita.com/BlueMountLab/items/f44c4925009c3f94565b
https://www.it-swarm-ja.tech/ja/javascript/vuejs%E3%81%AE%E3%80%8Cnpm-run-serve%E3%80%8D%E3%81%A8%E3%80%8Cnpm-run-dev%E3%80%8D%E3%81%AE%E9%81%95%E3%81%84/810503343/

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

Docker×Laravel×Vue

開発で必要な記事をまとめていきます。
これを参考にして開発始める人の手助けになればいいなと思って
自分が参考にした記事をまとめていきます。

マルチログインの実装

参考記事
https://qiita.com/namizatop/items/5d56d96d4c255a0e3a87?utm_campaign=popular_items&utm_medium=feed&utm_source=popular_items

それぞれ別々のディレクトリに分けてあげて判断。

認証に必要な要素を付け加える

https://qiita.com/sasakure-kei@github/items/388a2900557a6079164d

Laravel authで404 nginxが使えない時

https://qiita.com/isaatsu0131/items/f1eafe7522f0bf0ff043

nginxのファイル探索部分がおかしいらしい

Docker

起動中のコンテナ一覧

docker ps

Image起動

docker run -it IMAGE名 bash

Docker起動

https://qiita.com/A-Kira/items/1c55ef689c0f91420e81

Docker Laravel 実行

https://qiita.com/A-Kira/items/1c55ef689c0f91420e81

docker-compose down


docker-compose up -d

Docker 起動してもexitするとき

docker logs コンテナID

https://qiita.com/mom0tomo/items/35dfacb628df1bd3651e

Mysql bin/bashで起動

docker exec -it db-host bin/bash

Dockerのイメージ壊してテーブルのデータが消えた時

ERROR 1812 (HY000): Tablespace is missing for table `engineerMatching`.`companies`.

https://qiita.com/___uhu/items/74168be48c05638c7ac5

まず以下を実行する

ALTER TABLE example_table DISCARD TABLESPACE;

ALTER TABLE example_table IMPORT TABLESPACE;
復活!

Docker 全て終了させる

https://qiita.com/shisama/items/48e2eaf1dc356568b0d7

docker rmi $(docker images -q)

Docker再構築

https://qiita.com/shisama/items/48e2eaf1dc356568b0d7

Docker Mysql起動

起動時の初期設定参照URL

https://qiita.com/pugiemonn/items/b17288494e4b627f4475

docker exec -it db-host  bin/bash

のコマンドの際に

Error response from daemon: Container 5d83ac9a96ef971c5016b2a6559e75f43bfd71fa71909e89c3c04fd22ff89b94 is not running

と出たので

docker start 5d83ac9a96ef971c5016b2a6559e75f43bfd71fa71909e89c3c04fd22ff89b94

で普通に入れました。

自分の場合 DBのデータが入っているディレクトリごと消すとうまくいきました。
→これはおそらく最終手段なので本番環境でやると致命的になるので注意。

Tablespace is missing for table

https://qiita.com/___uhu/items/74168be48c05638c7ac5

Nginx 413 Request Entity Too Large

Nginxのconfファイルに

server{
    client_max_body_size 30M;
}

みたいに最大許容量を指定してあげれば対応可能です。

https://qiita.com/takecian/items/639deeae094466de6546

SPAの実装

非同期での認証

https://blog.capilano-fw.com/?p=3458

画面

https://giga-log.com/material-icons-use/

コンパイル時のエラー

there are multiple modules with names that only differ in casing” but modules referenced are identical
大文字小文字が入っている時に出るっぽい
https://stackoverflow.com/questions/47534267/webpack-there-are-multiple-modules-with-names-that-only-differ-in-casing-but?rq=1

Vue Router 404ERROR対処方法

ルーティング指定
https://sashimistudio.site/rails-vuerouter-history404/

https://blog.hirokiky.org/entry/2019/08/22/111031

解決してくれたもの
https://teratail.com/questions/121182

web.php
Route::get('/{any}',function(){
    return view('./company/home');
})->where('any','.*');

この記述で画面リロードしてもページ表示されるっす。
ルーティングの一番前に持ってきてあげる必要あり!!!!

開発時に使えそうなもの

ホットリロード
https://vue-loader-v14.vuejs.org/ja/features/hot-reload.html

変更すべきPHP.ini確認方法

echo $(php -r 'echo php_ini_loaded_file();')

読み込まれているファイルを確認できるコマンド

最大投稿可能サイズ確認

php -i|grep max

php.ini設定変更

post_maxこれをdockerファイルに記述してあげたところ成功

RUN echo "file_uploads = On\n" \
         "memory_limit = 500M\n" \
         "upload_max_filesize = 500M\n" \
         "post_max_size = 500M\n" \
         "max_execution_time = 600\n" \
         > /usr/local/etc/php/conf.d/uploads.ini

https://web-memo-s.hatenablog.com/entry/2019/02/21/182815

さくらサーバにデプロイ

非常に感謝しています。簡潔でわかりやすい。

https://arrown-blog.com/laravel-sakura-deploy/

デプロイ時に Class 'Collective\Html\HtmlServiceProvider' not found

composerのアップデートしていないのが原因
https://stackoverflow.com/questions/54675520/composer-update-the-requested-php-extension-ext-http-missing

Class 'Egulias\EmailValidator\Validation\RFCValidation' not found

https://laracasts.com/discuss/channels/laravel/eguliasemailvalidatoremailvalidator-does-not-exist

同様に困ってる方の質問で
```
rm -rf vendor/*

の後に

composer install --no-dev
```

で万事解決!!

メールが文字化けする。。。

スクリーンショット 2020-06-29 1.22.52.png

右下の文字をUTF-8にしてあげることで解決。。

2hかかった。。。

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

【Nuxt.js】Nuxt文法編:asyncData

? この記事はWP専用です
https://wp.me/pc9NHC-ut

前置き

非同期通信で使うasyncData
まとめました??‍♀️

・非同期通信とは
・Promiseとは
・asyncDataの役割と書き方(Promiseとasync/await)

分かるところは飛ばしてくださいね ✈️

非同期処理とは

こちらのツイートが分かりやすいかと思います?

https://twitter.com/ichikun0000/status/1283909871310499840?s=20

結果を待たずに
すぐに処理ができるということです?‍♀️?

温めた商品が手元にくる前に
会計を先に済ませることができる
というわけです!

これが同期処理なら
温めてる間はずっと店員さんが
レンジの前にいて、
温め終わってからでしか会計できません。

コードでいうなら…
⬇️の例でご覧ください??

Promise

Promiseとは

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-ut

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

Vue.js を1から学んでみた〜修飾子〜

Vue.js を1から学んでみた。にて書いてきましたが、
長くなり編集しにくくなってきたので、分割しました(こんなやりかたしないのかな?知りたい。)

5-1.イベント修飾子

イベントハンドラ内でそのイベントを止めたり、バブリングを止めるために、
event.preventDefault()event.stopPropagation()をメソッドの最初にコールするが、
Vueには同等の機能をもった便利なイベント修飾子.stop.preventがある。

v-onディレクティブの引数に設定するDOMイベントに追加する形式で書く。例:v-on:click.stop

「バブリング」については、DOMイベントのキャプチャ/バブリングを整理する 〜 JSおくのほそ道 #017を参照

.stop.preventだけでなく、以下が用意されている。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
sample50.html
<!-- クリックイベントのバブリングが止まる -->
<a v-on:click.stop="doThis"></a>

<!-- submit イベントが実行されない -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修飾子は繋げることができる。バブリングを止める & doThatは実行されない。 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 値を指定せず、修飾子だけ利用することも可能 -->
<form v-on:submit.prevent></form>

<!-- イベントリスナーを追加するときにキャプチャモードで使います -->
<!-- 言い換えれば、内部要素を対象とするイベントは、その要素によって処理される前にここで処理されます -->
<div v-on:click.capture="doThis">...</div>

<!-- event.target が要素自身のときだけ、ハンドラが呼び出されます -->
<!-- 言い換えると子要素のときは呼び出されません -->
<div v-on:click.self="doThat">...</div>

<!-- 最大1回、クリックイベントはトリガされます -->
<a v-on:click.once="doThis"></a>

<!-- `onScroll` が `event.preventDefault()` を含んでいたとしても -->
<!-- スクロールイベントのデフォルトの挙動(つまりスクロール)は -->
<!-- イベントの完了を待つことなくただちに発生するようになります -->
<div v-on:scroll.passive="onScroll">...</div>

使用時の注意事項1:実行される順番

以下の通り、イベント修飾子を設定する順番で、挙動が異なるので注意する。

修飾子を使用するとき、関連するコードが同じ順序で生成されるため注意してください。
それゆえ、v-on:click.prevent.self を使用すると全てのクリックイベントを防ぐことはできますが、v-on:click.self.prevent は要素自身におけるクリックイベントを防ぐだけです。

使用時の注意事項2:.passive.prevent は同時使用不可

以下の通り、.preventが無視されることがあるので注意する。

.passive と .prevent を一緒に使わないでください。.prevent は無視され、ブラウザにはおそらく警告が表示されます。.passive はイベントのデフォルトの挙動を妨げないことをブラウザに伝達することを思い出してください。

5-2.キー修飾子

イベントハンドラ内で、キーボードに関するDOMイベントを扱う時に有効なのがキー修飾子。
特定のキーだけ反応するような処理を書ける。

イベント修飾子と同じく、v-onディレクティブに対して書く。例:v-on:keyup.enter="addChar($event)

Vueサポートキー

Vueがサポートしているのは、以下の最も一般的と思われるキー。

  • .enter
  • .tab
  • .delete (“Delete” と “Backspace” キー両方をキャプチャします)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
sample60.html
<!-- Enterキーを押下した時だけ、addCharメソッドが実行される -->
<input type="text" v-on:keyup.enter="addChar($event)">

<!-- キー修飾子はつなげることも可能。Space/Enterキー押下時、addCharメソッドが実行される -->
<input type="text" v-on:keyup.enter.space="addChar($event)">

Key Code

keycodeも直接設定できる。

sample61.html
<!-- Enterキーを押下した時だけ、addCharメソッドが実行される -->
<!-- 両方とも、同じ-->
<input type="text" v-on:keyup.enter="addChar($event)">
<input type="text" v-on:keyup.13="addChar($event)">

ただし、keycodeイベントは非推奨
新しいブラウザではサポートされない可能性もあるので注意が必要。

Key Values

Key Valuesに載っている任意のキー名をケバブケースに変換することで使用可能。

sample11.html
<!-- $event.key が 'PageDown' に等しい場合だけ呼ばれる -->
<input v-on:keyup.page-down="onPageDown">

5-3.システム修飾子キー

「Ctlキー + 4キー」押下した時など、ctl、alt等は他のキーと一緒に押下されるケースが多い。
このイベントをハンドリングしやすくしたのが、システム修飾キー。

Vueサポートキー

Vueは以下のキーをサポートしてくれる。

  • .ctrl
  • .alt
  • .shift
  • .meta(Mac:コマンドキー(⌘) Windows:ウィンドウキー(⊞))
sample70.html
<!-- altキーを押しながらエンターキーを押下した時、addCharメソッドが実行される -->
<!-- エンターキーを押しながらaltキーを押下しても、何も反応しない-->
<input type="text" v-on:keyup.alt.enter="addChar($event)">

<!-- key codeでもOK -->
<!-- Alt + C -->
<input type="text" v-on:keyup.alt.67="addChar($event)">

<!-- マウス操作とももちろんOK -->
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

.exact 修飾子

.exactはシステム修飾子の正確な組み合わせを制御する。

sample71.html
<!-- これは Ctrl に加えて Alt や Shift キーが押されていても発行されます -->
<button v-on:click.ctrl="onClick">A</button>

<!-- これは Ctrl キーが押され、他のキーが押されてないときだけ発行されます -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- これは システム修飾子が押されてないときだけ発行されます -->
<button v-on:click.exact="onClick">A</button>

マウスボタンの修飾子

マウス操作の、左クリック、右クリック、真ん中クリックを以下の修飾子でサポートしてくれる

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

vue-routerのrouter.goがsafariで機能しない時の対処法

はじめに

vue-routerを使ったSPAアプリケーションを開発している時に、router.goメソッドでリロードを実現していましたが、ChromeからSafariに切り替えて検証したら動作しなかったため、対処法を調べてみました。

this.$router.go

window.location.reload()でブラウザをリロードする

window.location.reload()

ウィンドウ毎リロードすれば、safariでもリロードできます!

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

Vue.js + TypeScript覚え書き

概要

Vue.js + TypeScriptに関する覚書。インストールオプション等は後で変えるかもしれません。

バージョン

  • Node.js : 12.18.1
  • TypeScript(tsc) : 3.9.3
  • vue : 2.6.11
  • vue-router : 3.2.0
  • VueCLI(@vue/cli) : 4.4.6
  • axios : 0.18.0
  • element-ui : 2.4.5

プロジェクト作成と起動

$ vue create training
================================================
Vue CLI v4.4.6

### TypeScriptを使うためManuallyを選ぶ
? Please pick a preset: (Use arrow keys)
  default (babel, eslint)
? Manually select features

### 「TypeScript」「Router」を選択
? Check the features needed for your project:
 ◯ Babel
 ? TypeScript
 ◯ Progressive Web App (PWA) Support
 ? Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

### Noを選択(class-styleではなくVue.Extendを使った記法にする)
### https://github.com/vuejs/rfcs/pull/17#issuecomment-494242121
? Use class-style component syntax? (Y/n) n

### Noを選択(自動検出されたポリフィルにBabelとTypeScriptを使用しないため)
### ポリフィル : 古いブラウザーで、最近機能を使えるようにするためのコード
### https://developer.mozilla.org/ja/docs/Glossary/Polyfill
### https://www.sejuku.net/blog/100946
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? n

### Yesを選択(Historyモードを使う。Hashモードは若干速くなるがURLに「#」が入る)
### https://qiita.com/kozzzz/items/af9ad63fa70d4724cc2a
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y

### 「In package.json」を選択(パッケージを全てpackage.jsonで管理するため)
? Where do you prefer placing config for Babel, ESLint, etc.?
  In dedicated config files
? In package.json

### Noを選択(毎回プロジェクトを作成する毎に設定を選ぶため)
? Save this as a preset for future projects? (y/N) n

### 起動
$ cd training
$ npm run serve
================================================

初期のディレクトリ構造

training
|-- README.md
|-- node_modules
|-- package-lock.json
|-- package.json
|-- public
|   |-- favicon.ico   # ファビコン
|   `-- index.html    # index.htmlのテンプレート
|-- src
|   |-- App.vue
|   |-- assets        # webpackに処理されるアセットを格納するディレクトリ
|   |   `-- logo.png
|   |-- components
|   |   `-- HelloWorld.vue
|   |-- main.ts
|   |-- router
|   |   `-- index.ts
|   |-- shims-tsx.d.ts
|   |-- shims-vue.d.ts
|   `-- views
|       |-- About.vue
|       `-- Home.vue
`-- tsconfig.json
  • /public
  • /src
    • App.vue
      • アプリケーション全体で使うナビゲーションをここで設定
      • viewsディレクトリに格納したページデータはApp.vueを通して表示する
    • /components
      • 単一ファイルコンポーネント(.vueなど)を格納
      • アプリケーション内で何度も使うようなパーツを入れる
    • main.ts
      • Vueの基本設定ファイル。App.vueをhtmlに紐付ける。
    • /router/index.ts
      • ルーティングを設定
      • VueリソースのURLやURLに紐づくコンポーネント等を設定
    • shims-tsx.d.ts
      • IDEでjsx syntaxsupportを有効にして、JSXスタイルのTypeScriptコードを記述できる
      • 実は末尾がd.tsで終わるファイルであればファイル名は何でも良い
    • shims-vue.d.ts
      • VueファイルをTypeScriptで利用可能とするファイル
      • webpackを使わない場合 import Foo from "./Foo.vue"; はTypeScriptはモジュールであることを理解できない
      • 実は末尾がd.tsで終わるファイルであればファイル名は何でも良い
    • /views
      • ページデータの(.vueなど)を格納する

ファイルの内容

/src/App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

image.png

/src/view/About.vue

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

image.png

  • 一番上に「App.vue」のtemplateが出力されている
  • その下に「About.vue」のtemplateが出力されている

/src/view/Home.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">  ロゴを表示
    <HelloWorld msg="Welcome to Your Vue.js App"/> ※1
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'Home',    // nameは自身のファイル名から.vueを除いたものをつける?
  components: {
    HelloWorld
  }
}
</script>
  • ※1
    • HelloWorldコンポーネントを出力
    • msgには「Welcome to Your Vue.js App」の文字列が入る

/src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>      // 「Home.vue」でmsgに代入した値が入る
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  name: 'HelloWorld',    // nameは自身のファイル名から.vueを除いたものをつける?
  props: {               // 親コンポーネントから子コンポーネントにデータを受け渡す
    msg: String,
  },
});
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

image.png

  • props(プロパティ)
    • 親コンポーネント(App.vue)から子コンポーネント(HelloWorld.vue)にデータを受け渡すときに使う
    • 流れ
      • 子コンポーネント(HelloWorld.vue)のscriptタグ内でプロパティ名を列挙
        • ここでは msg: String のみ
      • 子コンポーネント(HelloWorld.vue)のtemplateタグ内でプロパティを記載
        • ここでは <h1>{{ msg }}</h1>
      • 親コンポーネント(Home.vue)のtemplateタグ内で子の呼び出しとデータの受け渡し
        • ここでは <HelloWorld msg="Welcome to Your Vue.js App"/>

/src/router/index.ts

import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

  const routes: Array<RouteConfig> = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',             // historyモードで利用
  base: process.env.BASE_URL,
  routes
})

export default router

/src/main.ts

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

単語帳

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

Vue.js を1から学んでみた〜ディレクティブ〜

Vue.js を1から学んでみた。にて書いてきましたが、
長くなり編集しにくくなってきたので、分割しました(こんなやりかたしないのかな?知りたい。)

4.ディレクティブ

特徴としては、以下かと。

  • v- から始まる特別な属性
  • 属性値は、単一の JavaScript 式を期待する(例外はv-for
  • 属性値の式が変更された時に、DOMを更新する
  • 省略記法あり。
    • 例:<a v-bind:href="url"> ... </a><a :href="url"> ... </a>

v-のディレクティブについて捉えることが必要そうなので、まとめようと思います。

※次の記事にもまとまっており、参考にもさせていただきました。
体で覚えるVue.js - ディレクティブ編 〜 JSおくのほそ道 #023

v-text

  • DOMの内側に展開。
  • Mustache構文({{}}で囲んだコード)と一緒。
  • 省略系→

サンプルコード

sample1.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-1">
  <!-- フィールド展開の例 -->
  <p v-text="message"></p>
  <p>{{ msg }}</p>

  <!-- メソッド展開の例 -->
  <p v-text="showMessage()"></p>
  <p>{{ showMsg() }}</p>
</div>
sample1.js
var vm = new Vue({
  el: '#app-1',
  data: {
    message: 'Hello Vue.js!',
    msg: 'サンキュー'
  },
  methods: {
    showMessage: function () {
      return "はろー、Vue.js"
    },
    showMsg: function () {
      return "Thank you!!"
    }
  }
})

結果
image.png

v-once

  • 1度DOM展開したら、そのあとは変更させない。
  • なんかの処理で、バインディングしたデータが変更されても、描画を変更させたくないときに有効。
    • 個人的にはこれを使うのは最終手段かなと。感覚的に。
  • 省略系→

サンプル(v-onceを使わない)

sample2-1.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
  <p v-text="message"></p>
  <p v-text="showMessage()"></p>
</div>
sample2.js
var vm = new Vue({
  el: '#app-2',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    showMessage: function () {
      this.message = 'hoge';
      return "はろー、Vue.js"
    }
  }
})

結果(「Hello Vue.js!」がshowMessage()で書き換えられて表示される)
image.png

サンプル(v-onceを使う・JavaScriptは変更しないので割愛)

sample2-2.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
  <!-- v-onceを追加 -->
  <p v-once v-text="message"></p>
  <p v-text="showMessage()"></p>
</div>

結果(「Hello Vue.js!」が表示される)
image.png

v-html

  • DOMの内側に展開。
    • v-textは「文字列」で展開する
    • v-htmlは「HTML」で展開する
  • HTMLをそのまま展開されるので、XSSに注意して使うことが必要。
  • 省略系→

サンプルコード:h1タグを表示させたいときー

sample3.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-3">
  <p v-text="message"></p>
  <p v-html="message"></p>
</div>
sample3.js
var vm = new Vue({
  el: '#app-3',
  data: {
    message: '<h1>タイトルです<h1>'
  }
})

結果
image.png

v-bind(その1:属性値)

  • htmlタグの属性に埋め込む時に使用。
  • 引数が必要なディレクティブ。引数は:の後ろに設定するv-bind:href="url
  • 使う頻度がスーパー高そう。
  • 省略系→:href='url'のように、コロンだけでOK。

サンプルコード:aタグのリンク先を設定したいときー

sample4.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-4">
  <!-- :(コロン)の後に引数を設定する-->
  <a v-bind:href="url">モノタス</a>


  <!-- 省略系
  <a :href="url">モノタス</a>
  -->
</div>
sample4.js
var vm = new Vue({
  el: '#app-4',
  data: {
    url: 'https://www.monotas.net/'
  }
})

結果
aaa.gif

v-bind(その2:属性)

  • htmlタグそのものを定義することも可能
  • 属性を[]で囲うことで指定可能。例:<a :[attr]="hogehoge">
  • つまり、v-bindだけで、各種タグに属性とそのvalueのバインディングができる。
  • これも使う頻度がスーパー高そう。
sample5.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-5">
  <a :href="monotas_url">モノタス</a>
  <br>
  <a :[attr]="jta_url">日本テニス協会</a>
</div>
sample5.js
var vm = new Vue({
  el: '#app-5',
  data: {
    attr: 'href',
    monotas_url: 'https://www.monotas.net/',
    jta_url: 'https://www.jta-tennis.or.jp/'
  }
})

結果
aaa2.gif

v-bind(その3:オブジェクト化)

  • 1つのタグに複数のv-bindを指定することもできるが、一つのオブジェクトとして記載することも可能。
  • Vueフィールドにオブジェクトを用意して、それをv-bindで指定することも可能。
  • 見た目すっきり系でもあるし、DOMの操作しやすくなりそうな感覚。まだふんわりとしかわかってない。
sample6.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-6">
  <!-- 複数指定できる -->
  <a :id="monotas_id" :href="monotas_url">モノタス(省略系でそれぞれ指定)</a>
  <br>
  <!-- 一つのオブジェクトでも書ける -->
  <a v-bind="{id: monotas_id, href: monotas_url}">モノタス(オブジェクトを直接指定)</a>
  <br>
  <!-- VueインスタンスフィールドのオブジェクトもOK -->
  <a v-bind="monotas">モノタス(Vueインスタンスのオブジェクトを指定)</a>
  <br>
</div>
sample6.js
var vm = new Vue({
  el: '#app-6',
  data: {
    attr: 'href',
    monotas_id: 2,
    monotas_url: 'https://www.monotas.net/',
    monotas: {
        id: 2,
      href: 'https://www.monotas.net/'
    }
  }
})

結果:全て同じIDとurlが設定されている
image.png
image.png

v-on(その1:基本)

  • クリック等のDOMが提供しているイベントのリスナー。
  • イベント発火時のJavaScriptの実行が可能。
  • 引数が必要なディレクティブ。引数は: の後ろに設定するv-on:click="change"
    • clickはDOMが提供しているイベント
    • changeが Vueで定義したメソッド。
  • 使う頻度は超スーパー高そう。
  • 省略系→@click="change"のように、コロンだけでOK。

サンプルコード:clickイベントの例。ボタンが押されたら文字列を変更します的なときー

sample7.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-7">
  <button v-on:click="change">クリックすると文字が変わります</button>
  <p>{{ first_str }}</p>
</div>
sample7.js
var vm = new Vue({
  el: '#app-7',
  data: {
    first_str: '最初に表示されています',
    clicked_str: 'ボタンが押されました'
  },
    methods:{
      change: function() {
        this.first_str = this.clicked_str
      }
    }
})

結果
aaa3.gif

v-on(その2:引数とイベント)

  • v-onでメソッドを実行する際に、引数を渡すことが可能。change('モノタス')
  • DOMイベントをそのまんま渡すことも可能。$eventという特別な変数を指定する。change($event, 'モノタス')

サンプルコード:引数とイベントオブジェクトを渡した時の例

sample8.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-8">
  <!-- $eventでイベントオブジェクト渡します -->
  <button v-on:click="change($event, '株式会社モノタス')">クリックすると文字が変わります</button>
  <p>{{ first_str }}</p>
  <p>{{ event_name }}</p>
</div>
sample8.js
var vm = new Vue({
  el: '#app-8',
  data: {
    first_str: '最初に表示されています',
    clicked_str: 'ボタンが押されました',
    event_name: 'イベントが表示されます'
  },
    methods:{
      change: function(event, msg) {
        this.first_str = msg
        this.event_name = event.target.tagName
      }
    }
})

結果
aaa4.gif

v-on(その3:イベントをJavaScriptから指定)

  • click等のDOMイベントは、JavaScriptからも指定できる

*サンプルコード:その2のコードをちょっと変更

sample8-2.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-8-2">
  <!-- vm_eventに変更 -->
  <button v-on:[vm_event]="change($event, '株式会社モノタス')">クリックすると文字が変わります</button>
  <p>{{ first_str }}</p>
  <p>{{ event_name }}</p>
</div>
sample8-2.js
var vm = new Vue({
  el: '#app-8-2',
  data: {
    first_str: '最初に表示されています',
    clicked_str: 'ボタンが押されました',
    event_name: 'イベントが表示されます',
    vm_event: 'click' // このイベントを設定
  },
    methods:{
      change: function(event, msg) {
        this.first_str = msg
        this.event_name = event.target.tagName
      }
    }
})

v-on(その4:引数となるDOMイベント)

DOMイベントの一覧は以下を参照。
https://developer.mozilla.org/ja/docs/Web/Events

上記のページの内、以下をよく使うと想定。
* キーボードイベント
* マウスイベント
* ドラッグ & ドロップイベント
* フォームイベント
* フォーカスイベント

v-model(その1:基本)

  • Vueのインスタンスの変更をリアルタイムにDOM要素へ展開してくれる
  • 逆も然りであり、DOM要素の更新をVueインスタンスへ展開してくれる
  • 双方向バインディング
  • 使う頻度は超スーパーすげー高そう。
  • 省略系→

サンプルコード:DOM要素を更新した時に、Vueインスタンスの情報が更新されることの例

sample9.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9">
  <p>タイトル「v-bind + v-on で対応」</p>
  <input 
    v-bind:value="message_1"
    v-on:input="message_1 = $event.target.value"
  >
  <p>表示:{{ message_1}}</p>

  <hr>

  <p>タイトル「v-model で対応」</p>
  <input v-model="message_2">
  <p>表示:{{ message_2}}</p>
</div>
sample9.js
var vm = new Vue({
  el: '#app-9',
  data: {
    message_1: 'v-bind + v-on で対応',
    message_2: 'v-model で対応'
  },
    methods:{
      change: function(event, msg) {
        this.first_str = msg
        this.event_name = event.target.tagName
      }
    }
})

結果
aaa7.gif

v-if / v-else / v-else-if

  • if文用のディレクティブ。他の言語と同様、if, else,else-if がある。
  • 条件付きレンダリングの一つ
  • 使う頻度は・・・もちろん高いが、複雑になりすぎないように元々の設計が大事そう。
  • 省略系→なし

ルール
v-if,v-else,v-else-ifはセットで使うようにと、以下のルールがある。

v-else 要素は、v-if または v-else-if 要素の直後になければなりません。それ以外の場合は認識されません。
v-else と同様に、v-else-if 要素は v-if 要素またはv-else-if 要素の直後になければなりません。

サンプルコード:単純な例

sample10.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-10">
  <input v-model="message">
  <button @click="change()">クリックすると文字が変わるかもしれません。</button>
  <p v-if="changeStr==='1'">
    1が押下されました
  </p>
  <p v-else-if="changeStr==='2'">
    2が押下されました
  </p>
  <p v-else-if="changeStr==='3'">
    3が押下されました
  </p>
  <p v-else>
    1~3の間の数字を押してください。
  </p>
</div>
sample10.js
var vm = new Vue({
  el: '#app-10',
  data: {
    message: '',
    changeStr: ''
  },
  methods:{
    change: function() {
      this.changeStr = this.message
    }
  }
})

結果
aaa9.gif

templateを使ってグループ化もできます。以下、例。

sample10-1.html
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-show

  • 条件付きレンダリングの一つ
  • v-ifと同じように使えるが、else的なディレクティブは存在しない。
  • 省略系→なし
v-show.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-10">
  <input v-model="message">
  <button @click="change()">クリックすると文字が変わるかもしれません。</button>
  <p v-show="changeStr==='1'">
    1が押下されました
  </p>
  <p v-show="changeStr==='2'">
    2が押下されました
  </p>
  <p v-show="changeStr==='3'">
    3が押下されました
  </p>
</div>

v-for(その1:基本)

  • for文用のディレクティブ。
  • リストレンダリングと呼ばれる、配列を扱うディレクティブの一つ。
  • 配列でもオブジェクト(kotlinでいうとMap)でもOK。
    • 配列の場合、2つめの引数がindex(任意)
    • オブジェクトの場合、2つめの引数がオブジェクトのキー(任意)、3つめの引数がindex(任意)
  • 使う頻度はもち高い。
  • 【超重要】必ずkey属性を指定する
  • 省略系→なし

サンプルコード:いろいろな書き方

sample-11.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-11">
  <p>example-1:オブジェクトのみ</p>
  <ul id="example-1">
    <!-- 書き方は様々 -->
    <!-- keyを忘れない-->
    <li v-for="item in items" :key="item.message">
      {{ item.message }}
    </li>
  </ul>

  <hr>
  <p>example-2:インデックスも。</p>
  <ul id="example-2">
    <!-- インデックスを使える -->
    <!-- keyを忘れない-->
    <li v-for="(item, index) in items"  :key="item.message">
      {{ item.message }}:インデックス→{{ index }}
    </li>
  </ul>

  <hr>
  <p>example-3:オブジェクトにて</p>
  <ul id="example-3">
    <!-- マップもOK -->
    <!-- keyを忘れない-->
    <li v-for="(value, key, index) of object" :key="value">
    {{ index }}. {{ key }}: {{ value }}
    </li>
  </ul>
</div>
sample-11.js
var vm = new Vue({
  el: '#app-11',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ],
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

結果
image.png

v-for(その2:その他)

templateタグも使える&回数指定のループも大丈夫。

sample10-1.html
<!-- template -->
<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

<!-- 回数指定 -->
<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

番外編:v-showv-ifのどちらを使うか?

  • v-if
    • DOM自体を生成・破棄される。
    • template使える。
    • DOM自体を作成しないパターンがあるため、初期描画は早い。
    • コンポーネントの表示・非表示の切り替えの場合は、DOMの生成・破棄を実行するため、遅い。
  • v-show
    • display noneを指定して、画面に表示されないようになる。
    • templateは使えない。
    • DOM自体は作ってしまうため、初期描画が遅い。
    • コンポーネントの表示・非表示の切り替えの場合は、display noneを切り替えするだけなので早い。
  • 頻繁に表示・非表示を切り替える場合は、v-showを使う。それ以外の場合は、v-ifを使う

番外編:v-forv-ifの同時使用は禁止

v-if と v-for を同時に利用することは 推奨されません。 詳細については スタイルガイド を参照ください。
v-if といっしょに使用されるとき、v-for は v-if より優先度が高くなります。詳細については リストレンダリング のガイドを参照してください。

とのこと。

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

Vue.jsでユーザーエージェントを使ってみよう

vue.jpeg

Vue.jsのチュートリアルを兼ねてユーザーエージェントを実装します。

本記事ではOSごとにボタンの表示を変えていきます。

テンプレートの準備

https://startbootstrap.com/themes/creative/ にアクセスし、テンプレートをダウンロードします。

ダウンロードが終わったら、Zipファイルを解凍し、好きなテキストエディタで開きます。

index.htmlを編集します。

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>Vue.jsでユーザーエージェントを使ってみよう</title>
        <link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
        <!-- Font Awesome icons (free version)-->
        <script src="https://use.fontawesome.com/releases/v5.13.0/js/all.js" crossorigin="anonymous"></script>
        <!-- Google fonts-->
        <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css" />
        <link href="https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic" rel="stylesheet" type="text/css" />
        <link href="https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css" />
        <!-- Core theme CSS (includes Bootstrap)-->
        <link href="css/styles.css" rel="stylesheet" />
    </head>
    <body id="page-top">
        <!-- Navigation-->
        <nav class="navbar navbar-expand-lg navbar-dark fixed-top" id="mainNav">
            <div class="container">
                <a class="navbar-brand js-scroll-trigger" href="#page-top"><img src="assets/img/navbar-logo.svg" alt="" /></a>
                <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
                    Menu
                    <i class="fas fa-bars ml-1"></i>
                </button>
                <div class="collapse navbar-collapse" id="navbarResponsive">
                    <ul class="navbar-nav text-uppercase ml-auto">
                        <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#services">Services</a></li>
                        <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#portfolio">Portfolio</a></li>
                        <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">About</a></li>
                        <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#team">Team</a></li>
                        <li class="nav-item"><a class="nav-link js-scroll-trigger" href="#contact">Contact</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        <!-- Masthead-->
        <header class="masthead">
            <div class="container">
                <div class="masthead-subheading">Welcome To Our Studio!</div>
                <div class="masthead-heading text-uppercase">User Agent</div>
                <a class="btn btn-info btn-xl text-uppercase js-scroll-trigger" href="#services">Windows</a>
                <a class="btn btn-primary btn-xl text-uppercase js-scroll-trigger" href="#services">Mac</a>
            </div>
        </header>
        <!-- Services-->
        <section class="page-section" id="services">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Services</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <div class="row text-center">
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-shopping-cart fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">E-Commerce</h4>
                        <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
                    </div>
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-laptop fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">Responsive Design</h4>
                        <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
                    </div>
                    <div class="col-md-4">
                        <span class="fa-stack fa-4x">
                            <i class="fas fa-circle fa-stack-2x text-primary"></i>
                            <i class="fas fa-lock fa-stack-1x fa-inverse"></i>
                        </span>
                        <h4 class="my-3">Web Security</h4>
                        <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
                    </div>
                </div>
            </div>
        </section>
        <!-- Portfolio Grid-->
        <section class="page-section bg-light" id="portfolio">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Portfolio</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <div class="row">
                    <div class="col-lg-4 col-sm-6 mb-4">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal1">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/01-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Threads</div>
                                <div class="portfolio-caption-subheading text-muted">Illustration</div>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-4 col-sm-6 mb-4">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal2">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/02-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Explore</div>
                                <div class="portfolio-caption-subheading text-muted">Graphic Design</div>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-4 col-sm-6 mb-4">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal3">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/03-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Finish</div>
                                <div class="portfolio-caption-subheading text-muted">Identity</div>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-4 col-sm-6 mb-4 mb-lg-0">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal4">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/04-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Lines</div>
                                <div class="portfolio-caption-subheading text-muted">Branding</div>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-4 col-sm-6 mb-4 mb-sm-0">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal5">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/05-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Southwest</div>
                                <div class="portfolio-caption-subheading text-muted">Website Design</div>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-4 col-sm-6">
                        <div class="portfolio-item">
                            <a class="portfolio-link" data-toggle="modal" href="#portfolioModal6">
                                <div class="portfolio-hover">
                                    <div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
                                </div>
                                <img class="img-fluid" src="assets/img/portfolio/06-thumbnail.jpg" alt="" />
                            </a>
                            <div class="portfolio-caption">
                                <div class="portfolio-caption-heading">Window</div>
                                <div class="portfolio-caption-subheading text-muted">Photography</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
        <!-- About-->
        <section class="page-section" id="about">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">About</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <ul class="timeline">
                    <li>
                        <div class="timeline-image"><img class="rounded-circle img-fluid" src="assets/img/about/1.jpg" alt="" /></div>
                        <div class="timeline-panel">
                            <div class="timeline-heading">
                                <h4>2009-2011</h4>
                                <h4 class="subheading">Our Humble Beginnings</h4>
                            </div>
                            <div class="timeline-body"><p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p></div>
                        </div>
                    </li>
                    <li class="timeline-inverted">
                        <div class="timeline-image"><img class="rounded-circle img-fluid" src="assets/img/about/2.jpg" alt="" /></div>
                        <div class="timeline-panel">
                            <div class="timeline-heading">
                                <h4>March 2011</h4>
                                <h4 class="subheading">An Agency is Born</h4>
                            </div>
                            <div class="timeline-body"><p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p></div>
                        </div>
                    </li>
                    <li>
                        <div class="timeline-image"><img class="rounded-circle img-fluid" src="assets/img/about/3.jpg" alt="" /></div>
                        <div class="timeline-panel">
                            <div class="timeline-heading">
                                <h4>December 2012</h4>
                                <h4 class="subheading">Transition to Full Service</h4>
                            </div>
                            <div class="timeline-body"><p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p></div>
                        </div>
                    </li>
                    <li class="timeline-inverted">
                        <div class="timeline-image"><img class="rounded-circle img-fluid" src="assets/img/about/4.jpg" alt="" /></div>
                        <div class="timeline-panel">
                            <div class="timeline-heading">
                                <h4>July 2014</h4>
                                <h4 class="subheading">Phase Two Expansion</h4>
                            </div>
                            <div class="timeline-body"><p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p></div>
                        </div>
                    </li>
                    <li class="timeline-inverted">
                        <div class="timeline-image">
                            <h4>
                                Be Part
                                <br />
                                Of Our
                                <br />
                                Story!
                            </h4>
                        </div>
                    </li>
                </ul>
            </div>
        </section>
        <!-- Team-->
        <section class="page-section bg-light" id="team">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Our Amazing Team</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <div class="row">
                    <div class="col-lg-4">
                        <div class="team-member">
                            <img class="mx-auto rounded-circle" src="assets/img/team/1.jpg" alt="" />
                            <h4>Kay Garland</h4>
                            <p class="text-muted">Lead Designer</p>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
                        </div>
                    </div>
                    <div class="col-lg-4">
                        <div class="team-member">
                            <img class="mx-auto rounded-circle" src="assets/img/team/2.jpg" alt="" />
                            <h4>Larry Parker</h4>
                            <p class="text-muted">Lead Marketer</p>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
                        </div>
                    </div>
                    <div class="col-lg-4">
                        <div class="team-member">
                            <img class="mx-auto rounded-circle" src="assets/img/team/3.jpg" alt="" />
                            <h4>Diana Petersen</h4>
                            <p class="text-muted">Lead Developer</p>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
                            <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-lg-8 mx-auto text-center"><p class="large text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut eaque, laboriosam veritatis, quos non quis ad perspiciatis, totam corporis ea, alias ut unde.</p></div>
                </div>
            </div>
        </section>
        <!-- Clients-->
        <div class="py-5">
            <div class="container">
                <div class="row">
                    <div class="col-md-3 col-sm-6 my-3">
                        <a href="#!"><img class="img-fluid d-block mx-auto" src="assets/img/logos/envato.jpg" alt="" /></a>
                    </div>
                    <div class="col-md-3 col-sm-6 my-3">
                        <a href="#!"><img class="img-fluid d-block mx-auto" src="assets/img/logos/designmodo.jpg" alt="" /></a>
                    </div>
                    <div class="col-md-3 col-sm-6 my-3">
                        <a href="#!"><img class="img-fluid d-block mx-auto" src="assets/img/logos/themeforest.jpg" alt="" /></a>
                    </div>
                    <div class="col-md-3 col-sm-6 my-3">
                        <a href="#!"><img class="img-fluid d-block mx-auto" src="assets/img/logos/creative-market.jpg" alt="" /></a>
                    </div>
                </div>
            </div>
        </div>
        <!-- Contact-->
        <section class="page-section" id="contact">
            <div class="container">
                <div class="text-center">
                    <h2 class="section-heading text-uppercase">Contact Us</h2>
                    <h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
                </div>
                <form id="contactForm" name="sentMessage" novalidate="novalidate">
                    <div class="row align-items-stretch mb-5">
                        <div class="col-md-6">
                            <div class="form-group">
                                <input class="form-control" id="name" type="text" placeholder="Your Name *" required="required" data-validation-required-message="Please enter your name." />
                                <p class="help-block text-danger"></p>
                            </div>
                            <div class="form-group">
                                <input class="form-control" id="email" type="email" placeholder="Your Email *" required="required" data-validation-required-message="Please enter your email address." />
                                <p class="help-block text-danger"></p>
                            </div>
                            <div class="form-group mb-md-0">
                                <input class="form-control" id="phone" type="tel" placeholder="Your Phone *" required="required" data-validation-required-message="Please enter your phone number." />
                                <p class="help-block text-danger"></p>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-group form-group-textarea mb-md-0">
                                <textarea class="form-control" id="message" placeholder="Your Message *" required="required" data-validation-required-message="Please enter a message."></textarea>
                                <p class="help-block text-danger"></p>
                            </div>
                        </div>
                    </div>
                    <div class="text-center">
                        <div id="success"></div>
                        <button class="btn btn-primary btn-xl text-uppercase" id="sendMessageButton" type="submit">Send Message</button>
                    </div>
                </form>
            </div>
        </section>
        <!-- Footer-->
        <footer class="footer py-4">
            <div class="container">
                <div class="row align-items-center">
                    <div class="col-lg-4 text-lg-left">Copyright © Your Website 2020</div>
                    <div class="col-lg-4 my-3 my-lg-0">
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
                        <a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
                    </div>
                    <div class="col-lg-4 text-lg-right">
                        <a class="mr-3" href="#!">Privacy Policy</a>
                        <a href="#!">Terms of Use</a>
                    </div>
                </div>
            </div>
        </footer>
        <!-- Portfolio Modals-->
        <!-- Modal 1-->
        <div class="portfolio-modal modal fade" id="portfolioModal1" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/01-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Threads</li>
                                        <li>Category: Illustration</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Modal 2-->
        <div class="portfolio-modal modal fade" id="portfolioModal2" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/02-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Explore</li>
                                        <li>Category: Graphic Design</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Modal 3-->
        <div class="portfolio-modal modal fade" id="portfolioModal3" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/03-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Finish</li>
                                        <li>Category: Identity</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Modal 4-->
        <div class="portfolio-modal modal fade" id="portfolioModal4" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/04-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Lines</li>
                                        <li>Category: Branding</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Modal 5-->
        <div class="portfolio-modal modal fade" id="portfolioModal5" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/05-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Southwest</li>
                                        <li>Category: Website Design</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Modal 6-->
        <div class="portfolio-modal modal fade" id="portfolioModal6" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="close-modal" data-dismiss="modal"><img src="assets/img/close-icon.svg" alt="Close modal" /></div>
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <div class="modal-body">
                                    <!-- Project Details Go Here-->
                                    <h2 class="text-uppercase">Project Name</h2>
                                    <p class="item-intro text-muted">Lorem ipsum dolor sit amet consectetur.</p>
                                    <img class="img-fluid d-block mx-auto" src="assets/img/portfolio/06-full.jpg" alt="" />
                                    <p>Use this area to describe your project. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est blanditiis dolorem culpa incidunt minus dignissimos deserunt repellat aperiam quasi sunt officia expedita beatae cupiditate, maiores repudiandae, nostrum, reiciendis facere nemo!</p>
                                    <ul class="list-inline">
                                        <li>Date: January 2020</li>
                                        <li>Client: Window</li>
                                        <li>Category: Photography</li>
                                    </ul>
                                    <button class="btn btn-primary" data-dismiss="modal" type="button">
                                        <i class="fas fa-times mr-1"></i>
                                        Close Project
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Bootstrap core JS-->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
        <!-- Third party plugin JS-->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
        <!-- Contact form JS-->
        <script src="assets/mail/jqBootstrapValidation.js"></script>
        <script src="assets/mail/contact_me.js"></script>
        <!-- Core theme JS-->
        <script src="js/scripts.js"></script>
    </body>
</html>

index.htmlをブラウザに表示します。

img1.png

上の画面のように表示されれば、テンプレートの準備は完了です。

Vue.jsの導入

index.htmlのbodyの終了タグの上にコードを追加します。

index.html
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
        //ここにVue.jsのコードを記述する
        </script>

次にheaderの開始タグ内にid="app"と記述します。

index.html
<header id="app" class="masthead">

scriptタグ内にコードを記述します。

index.html
<script>
  let app = new Vue({
    el: "#app",
    data: {
      value: false,
    }
  })
</script>

次にWindowsボタンにv-ifを記述します。

index.html
<a v-if="value" class="btn btn-info btn-xl text-uppercase js-scroll-trigger" href="#services">Windows</a>

ブラウザを更新します。

img2.png

上の画面のように表示されれば、Vue.jsの導入は完了です。

ユーザーエージェントの利用

scriptタグ内を編集します。

index.html
<script>
  let app = new Vue({
    el: "#app",
    methods: {
      isntPlatform: function(platform) {
        let agent = navigator.userAgent.toLowerCase()
        return agent.indexOf(platform) === -1
      }
    }
  })
</script>

次にWindowsボタンとMacボタンにv-ifを記述します。

index.html
<a v-if="isntPlatform('mac')" class="btn btn-info btn-xl text-uppercase js-scroll-trigger" href="#services">Windows</a>
<a v-if="isntPlatform('windows')" class="btn btn-primary btn-xl text-uppercase js-scroll-trigger" href="#services">Mac</a>

ブラウザを更新します。

Google Chromeを使っている方は、デベロッパーツールでOSごとに表示が変わるかどうか確認してみてください。

Windowの場合

img3.png

MacOSの場合

img4.png

これでVue.jsにおけるユーザーエージェントの実装は完了です。

ソースコードが見たい方はこちらで確認できます。

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

「親子」のお小遣いで考えるVueのEmitの理解

なぜVueのEmit処理はややこしいのか。自分で理解したときのコツを残しておく。
* TypeScriptで記述しています。

VueのEmitで必要になる処理

例えば、何かクリックのようなイベントがあったときを考えよう。

単に親から子供のデータにアクセスしたいだけのに、親側では以下のようなコードが必要になる。

  • データにアクセスするための関数を用意する。
    • 例:changeParentValueToのような関数。
  • その関数を@click=changeParentValueToでトリガーさせる。

一方で、子側では以下のようなコードが必要になる。

  • @click=onClickのようにクリック時に発火する関数を定義する
    • これはまだ自然なのでわかりやすい。
  • @Emitでデコレートされたpublic click関数をオーバーライドする。
  • onClickの中で、click関数に、渡したい値を引数として入れる。

理解のコツ

上で見たとおり、やりたいことは単純なのにも関わらず実装はわりと面倒になる。素直に、<子コンポーネント>.<変数>のような形でアクセスできたらもっと簡単なのに、と思ったことはないだろうか。

しかし<子コンポーネント>.<変数>という形で子供の変数にアクセスすることは、あまり良い方法とはいえない。

これを理解するためには、「親側はなるべく子供の変数をダイレクトに反映させたくない」という原則を腹落ちしておく必要がある。

以下ではこの原則を理解するポイントと、この原則に沿ったコードの読み方について述べておく。

親側はなるべく子供の変数をダイレクトに反映させたくないという原則

ここでは暫定的に「親」「子」という意味を本来の意味(人間の「親子」というように使うときの意味)としてあえて曖昧にとらえ、親が子供に、作業量に応じたお小遣いをあげるという状況を考えよう。

この状況において、例えば以下のようにコードを書いたとしよう。

vue.js
paymentToChild = Child.workLoad * 100 //子供の作業量から計算。比例係数100
sendMoney(paymentChild) //自分の口座から送金実行

このコードは、シンプルで読みやすいがちょっと恐ろしいコードでもある。2つの理由がある。

  1. 変な値が入ってきたときの挙動そのまま入ってしまう
    例えば、子どものworkLoadがなぜかnullになってしまうとこのロジックは破綻してしまう。

  2. 口座の変化に気が付きづらい
    子供のワークロードに変化があったとしても、親側ではその変化が一切記述されていないので、このコードのデバッグは難しいものになるだろう。

ということで、自然とこれらのデメリットをカバーするようなスクリプトを書こうということになる。それぞれのデメリットに対応するようにコードを書くと、

  1. 値をラップする関数を書く
    直接的に代入するのではなく、変な値が入ってきたときに備えてのtry-catchとかを書いておく。

  2. 変化を親がコントロールできるようにする
    値の変化を子のコンポーネントのイベントで管理するのではなく、親のコンポーネントで管理できるようにする。

というようになる。

...と、ここまで書けば気がついた方もいらっしゃると思うが、実はこれらをわざわざ自分で書かなくても良いというのがVueにおける(親側の)Emit処理なのだ。

具体的にみていこう。

子供の「作業」が押される回数countに相当し、親が子供に払う金額がresultに相当するとして考えてもらいたい。

コメントはコードの中に記してゆく。

親コンポーネント

<template>
    <div>
        <!--クリックしたら「値を返す関数」を呼ぶ-->
        <MyButton @click="changeParentValueTo"></MyButton>
        <br>
        子コンポーネントでの計算結果×100 : {{result}}</div>
</template>

<script lang="ts">
    import {Component, Vue} from 'vue-property-decorator';
    import MyButton from "@/components/MyButton.vue";

    @Component({
        components: {
            MyButton,
        },
    })
    export default class Home extends Vue {
        private result = 0; // 画面に表示したい、最終計算結果

        // ほしい値を返す関数
        // なるべく、簡単にアクセスできないようにしてある。
        // (result = MyBotton.count*100とは記述できない。理由は下記のとおり。)
        public changeParentValueTo(emittedValue: number) {
            // このように子の変数を直接覗けないようにすることで、2つのメリットが生じる。
            // 1. 自然に(このような)関数を一度挟むようになるので制御しやすい(try-catchで変な値を抑えるとか)
            // 2. 1.とほぼ同義だが、値の変化を検知しやすい。(もしこの関数がないと、子のコードを覗かないと変化のトリガーが調べられない)
            this.result = emittedValue; // 子コンポーネントとつながる変数emittedValueをcountに格納し、値を変える
        }
    }
</script>

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

<template>
    <button @click="onClick">MyButton</button>
</template>

<script lang="ts">
    import {Component, Emit, Prop, Vue} from "vue-property-decorator";

    @Component
    export default class MyButton extends Vue {
        private count = 0;

        @Emit() // こうすることで、click関数を呼べば親に通知されるようになる。
        public click(valueToEmit: number) 
            // 引数を定義すると、そこに入れられた値は通知の際に親へゆく
            console.log(`emitted ${valueToEmit} from child`)
        }

        public onClick() {
            this.count++;
            // @EmitのついたClickの引数に親へ渡す変数を入れると親へデータが行く。
            this.click(this.count); 
        }
    }
</script>

ということで、VueのEmit処理はややこしいが、「なるべく子供の変数をダイレクトに反映させたくない」という原則を理解していれば、ある程度素直に認めることができるだろう。

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