はじめに
新しいサービスに、ようやくVue.jsを取り入れてみようということで
SoftwareDesign 2020年9月号 ステップアップ式 Vue.jsを参考に、理解を深めていきたいと思います。
久しぶりにコードに触れるので、なかなか興味深い内容でした。
はじめて触れににはよい生地でしたのでみなさんもぜひ。
Vue.js
Vue.js、React、Angular は3大フロントエンドフレームワークと呼ばれている。
リアクティブ?
Web上の操作とJavaScript上のデータを一致させるフレームワークに組み込まれた仕組み。
データの流れの例
inputイベント発生
↓
イベントに関連したJavaScript上のデータが更新される
↓
その変化に合わせてDOMが書き変わる
※DOMはHTMLから生成され、ブラウザ上で実際に表示されているオブジェクトをメモリ上で表現しているもの。
メモリ上で表現って何。。。?となるけれどDOMの正式な説明って長い。。。!
メモリなので、一時的な感じなのかな?
JavaScriptからJavaScript上へデータの書き込みを行ってもOK!
これにより、
データを必要なときにDOMから呼び出すものではない!
常にJavaScriptもあるもの!として扱うことができる。
そして、この考えは仮想DOMというアーキテクチャにもつながる。
なんでJQueryをやめるの?
状態管理の責任をフレームワークに任せることができる。
よって、より複雑なアプリケーションをつくる余裕ができる
やってみよう!
Codepenでのサンプル。
https://codepen.io/gihyosd/pen/zYrmeZW
Vue の部分やCountの数字を5にしてみたり。
Vue.jsの読み込み
学習やプロトタイプで使用したい場合はCDN版を利用してみるとよい。
CDNはContents Delivery Network。
Vueインスタンスの有効範囲
<html>
<head>
<title>Hello Vue</title>
</head>
<body>
<!-- 1. CDNからのVueの読み込み -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<div id="app"><!-- 5. Vueインスタンスの有効範囲ここから・VueインスタンスはこのidでVueインスタンスとHTMLをマッピングしてる -->
<!-- 6. マスタッシュ構文 {{}}が口ひげに似ているから-->
<p>Hello {{world}}</p> <!--←オブジェクトが持つプロパティをVue.jsは監視してる-->
<p>Counter: {{count}}</p><!--←オブジェクトが持つプロパティをVue.jsは監視してる-->
<!-- 7. v-ifディレクティブ v-で始まる属性をVue.jsではディレクトティブと呼ぶ-->
<p v-if="count == 5">見えたよ!</p>
<!-- 8. v-modelディレクティブ 双方向バインディング。タグだけでなくコンポーネントに対してもバインディングできる-->
<input v-model="world"><br>
<input type="number" v-model="count" />
</div>
<script>
// 2. Vueインスタンスの作成
new Vue({
el: "#app", // 3. elプロパティ・DOMに対し、指定した値に該当するエレメントを無つけて対象のエレメントが見つかったときにVue.jsのインスタンスとHTMLうぃマッピングするセレクタ(document.querySelector()メソッドを実行して該当エレメントを取得している)
data() { // 4. data()メソッド
return { //引数に渡しているオブジェクトがVueのコンポーネントになる
world: "Vue",
count: 0
}
}
})
</script>
</body>
</html>
jQueryコードとの比較
JavaScriptとDOM間の反映については「ほとんど意識することなく書いたのに何故か動く」といった状態であっただろう。
これが、フレームワークの力。
改めて、Vue.jsとは?特徴など
入力と、出力の責務をフレームワークが担ってくれること
Vue.jsの何よりも大きな特徴
HTMLの延長線上にVue.jsの構文を書き足すことで
豊かなインタラクティブを備えたWebページをつくれること。
Vue.jsとコンポーネント志向
Vue.jsはコンポーネント指向を取り入れている。
Webサイト上のパーツをコンポーネントという単位に切り出して再利用できるようにしている。
コンポーネントを疎結合に保つようにすると、そのコンポーネントはあらゆるシーンで再利用できるようになる
プログレッシブフレームワーク
プログレッシブフレームワークはライブラリを段階的に取り入れることで、
「ページの一部分だけ取り入れる」~「シングルページアプリケーションのようにページ遷移までJavaScriptで擬似的に行うような高度なアプリケーション」まで段階的にスケールすることが可能なフレームワークであるということ。
さらに、サンプルみてみる!
- 変数が保持
- 分岐ができる
- イベントが取り扱える
上記ができれば、簡単なアプリケーションぐらいはつくれるのではないか?
CodePenの中に書かれたサンプル
<html>
<head>
<title>Hello Vue</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<div id="app">
<p>Counter: {{count}}</p>
<!-- 6. v-bind -->
<a target="_blank" v-bind:href="'https://gihyo.jp/contentslist?start=2020/04/' +('0' + count).slice(-2)">04/{{('0' + count).slice(-2)}}</a>
<!--注目ポイントは v-bind:href の部分。これは、動的値を決定する、というデレクティブ。
""の中はJavaScriptとして解釈されている。また、''で囲まれた部分は文字列として扱われる。
countには変数countの値が入る-->
<div>
<!-- 5. v-on:click -->
<button v-on:click="increment()">+1</button><!--<button>タグがclickイベントを発火したときにincrement()メソッドを呼ぶようになっている-->
<button v-on:click="increment(5)">+5</button><!--引数を扱うこともできる-->
</div>
<ul>
<!-- 7. v-for -->
<li v-for="item in list">{{item}}</li>
<!--ディレクティブ{-v}の中はthisが省略されている。
listはthis.listを表している。
ループしながらlistのデータを配列の個数分だけ、itemとして取り出している。
リストレンダリングという構文
list: [2, 4, 6] の部分を list: [2, 4, 6, 8] に変更すると、確かに増える!!
-->
</ul>
<button v-on:click="addItem">addItem</button><!--引数の()を省略することもできる-->
<!-- 8. オブジェクトの表示
{{オブジェクト名.プロパティ名}}でアクセスできることの例
-->
<p>userオブジェクト: {{user}}</p>
<p>名前: {{user.name}}, メール: {{user.email}}</p>
</div>
<script>
new Vue({ //data()メソッドとmethodsプロパティを持っている
el: "#app",
data() {
return {
count: 0,
list: [2, 4, 6], // 1. メソッドが返す内容として配列もOK
user: { // 2. メソッドが返す内容としてObjectもOK
name: "Riin",
email: "riin@example.com"
}
}
},
// 3. methodsプロパティ・メソッドを含むオブジェクトを渡している
methods: {
increment(val = 1) {
// 4. <script></script>内ではthisは省略できない
this.count += val
},
addItem() {
this.list.push(this.count)
}//increment()が呼ばれたら引数のvalでthis.countをカウントアップ・addItemが呼ばれたら現在のthis.countの数値をthis.listの配列の最後に追加しているイメージ
}
})
</script>
</body>
</html>
thisって?
thisは「中身が変化する変数」であると言える。詳細はまた、別途。
シンタックスシュガー
Vue.jsでは下記のようなシンタックスシュガー(省略記法)がある。
本来の記法 | シンタックスシュガー | 備考 |
---|---|---|
v-on:hoge | @hoge | イベントハンドラ |
v-slot:hoge | #hoge | 名前付きスロット |
v-bind:hoge | :hoge | 可変の属性値 |
jQueryとVue.jsの共存て危ないって本当?
本当。
ただ、注意点を気をつければある程度は混在させることもできる。
コツは生のDOM操作による書き込みが行れない領域を確保し、その中でのみVue.jsを使いはじめること。
jQueryで操作する部分と、しない部分をきっかり分けて、しない部分だけでVue.jsを使いましょう、ということですね。
気をつけないと、コードリーディングにコンテキストスイッチが入ってしまったり、挙動が読めなくなくなってしまう。
コンテキストスイッチとは、コンピュータの処理装置(CPU)が現在実行している処理の流れ(プロセス、スレッド)を一時停止し、別のものに切り替えて実行を再開すること。
IT用語
コードを普通に読もうとしたら、変な処理が走ってしまっちゃった、という状態のことでしょうか?
挙動が読めなくなるとは?
Vue.jsはDOMから仮想DOMの構築を一度しか行わない。
繰り返しDOMの読み取りを行わない、必要最低限のDOM書き込みで済ます、という戦略で高速化させた。
つまり、仮想DOMと生のDOMは常に一致しているという前提になる
この前提なので、外部からJavaScriptでDOMの更新を行ってしまうとその変更が仮想DOMに反映されない為に何が起こるのか予測できなくなる。
とにかく作ってみよう!コンポーネント化とページ遷移の基礎を学んでSPAを作る
Vue.jsを使ったアプリケーションの開発はNode.jsを使ったビルド開発環境を用意することで真の力を発揮する。
Vue.jsのプロジェクト管理ツールを使用すると、簡単に環境の構築ができる。
これにより、新しいプロジェクトをローカル環境でつくってみる。
それには、Node.jsの実行環境が必要となる為、あらかじめインストールしておく。
Node.jsのバージョンは最低でも8.x系以上である必要がある!
そして、npm、node_modulesにパスを通しておく。
この記事にて試してみたときのnode.jsのバージョンは 14.15.1.LTS。
Windows10で実行
(参考記事だと12.18.1、MacOSX)
node.jsインストールに伴って参考になりそうな記事
npmとは?
Node Package Managerの略で、Node.jsのモジュール、つまり部品を管理するツール。
パスを通すとは!?
「PATH を通す」とは、特定のプログラムを「プログラム名だけで実行できるようにする」こと。
もっと言うと、プログラム名だけで実行できるようにするために、PATH という環境変数(設定の一種)に「このプログラムも名前だけで実行できるようにしてください」という値を追加すること。
プログラムは本来なら「フルパス」を指定しないと実行できない。
Windows 「PATH を通す」とは? メモ帳での例
たとえば「メモ帳」は C:\Windows\System32\notepad.exe というフルパスなので、メモ帳を開きたい時は「 C:\Windows\System32\notepad.exe を実行してください」と Windows に頼まなきゃいけない。
そうすると Windowsは
「C: ドライブ、の中にある Windows フォルダ、の中にある System32 フォルダ、の中にある notepad.exe というファイルを実行すればいいんだな?」
と解釈して、実行してくれる。
でもメモ帳を実行したい時に、毎回 C:\Windows\System32\notepad.exe と書くのは大変。
そこでメモ帳に関して PATH を通してあげると notepad.exe と指定するだけでメモ帳が開けるようになる。
PATH を通したおかげで C:\Windows\System32 の部分が自動的に補完されることとなる。
さっそくインストール
npm install -g @vue/cli
Node.jsをインストールさせる前にシェルを立ち上げていたら、npmコマンドは使えないと言われてしまった。
シェルを再度、立ち上げると無事始動。
PS C:\Users\user> npm install -g @vue/cli
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated @hapi/joi@15.1.1: Switch to 'npm install joi'
npm WARN deprecated @hapi/topo@3.1.6: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/hoek@8.5.1: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/bourne@1.3.2: This version has been deprecated and is no longer supported or maintained
~中略~
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules\@vue\cli\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN @vue/compiler-sfc@3.0.4 requires a peer of vue@3.0.4 but none is installed. You must install peer dependencies yourself.
+ @vue/cli@4.5.9
added 1343 packages from 699 contributors in 210.535s
自分の環境だと、5分前後かかりました。
vue create my-project
大規模開発に向けて コンポーネント設計のアイデア
Vue.jsを使用したアプリケーションの開発は、Node.jsを使用したビルド環境を用意することで真のちからを発揮する。
さまざまな「ツールチェイン」によって実現されてる「開発を支援する仕組み」を取り入れてみると、きっと便利!
Vue CLIを使用すると比較的用意に環境構築ができる。
$ npm install -g @vue/cli
-g は グローバルインストールする際のスイッチ?
グローバルインストールはシステム共通の場所にパッケージをインストールすること。
ビルドツールなど、システム全体で利用するツール・コマンド類をインストールしたい時に使う。
やたらとグローバルインストールはしない方がよいとの記事もあった。
※command not found:vue と表示されたら、再度シェルを立ち上げなおすとパスが通る筈
$ vue create my-project
Vue CLI v4.5.9
? Please pick a preset:
> Default ([Vue 2] babel, eslint)
Default (Vue 3 Preview) ([Vue 3] babel, eslint)
Manually select features
Vue CLI v4.5.9
✨ Creating project in C:\Users\username\my-project.
⚙️ Installing CLI plugins. This might take a while...
> yorkie@2.0.0 install C:\Users\mediwel\my-project\node_modules\yorkie
> node bin/install.js
setting up Git hooks
can't find .git directory, skipping Git hooks installation
> core-js@3.8.1 postinstall C:\Users\mediwel\my-project\node_modules\core-js
> node -e "try{require('./postinstall')}catch(e){}"
> ejs@2.7.4 postinstall C:\Users\mediwel\my-project\node_modules\ejs
> node ./postinstall.js
added 1243 packages from 938 contributors and audited 1246 packages in 123.968s
63 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
🚀 Invoking generators...
📦 Installing additional dependencies...
added 53 packages from 36 contributors and audited 1299 packages in 21.391s
67 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
⚓ Running completion hooks...
📄 Generating README.md...
🎉 Successfully created project my-project.
👉 Get started with the following commands:
$ cd my-project
$ npm run serve
PS C:\Users\mediwel>
「Pick the package manager to use installing dependencies」は現れなかったけどな。
参考にした記事はYarnを選ぶことになってたけど。
$ cd my-project
$ yarn serve
ではなく
$ cd my-project
$ npm run serve
となったな。
ちょっとここで出てきた気になる用語について見ていってみる。
- babel
- eslint
- npm
- Yarn
Babel
Babelとは、簡潔にいうと、JavaScriptのコードを新しい書き方から古い書き方へと変換するツール。
公式(英語)をみるとトップにでかでかと
Babel is a JavaScript compiler.
と書いてあります。コンパイラーなんでしょう。
eslint
ESLint は JavaScript のための静的検証ツール。
コードを実行する前に明らかなバグを見つけたり、括弧やスペースの使い方などのスタイルを統一したりしてくれる。
同様のツールとしては JSLint, JSHint 等がある。
lint は、主にC言語の用の静的解析ツールとして存在しているんですね。
npmとyarn
npm
Node.jsをインストールすれば一緒にインストールされる。
2009年にNode.jsがリリースされた翌年にnpmがリリースされた。
yarn
yarnは2016年にリリース。
npmと互換性があり、npmで使用していたプロジェクト設定ファイル(package.json)がそのまま使える。
npmと比べてインストールが速い、セキュリティが高いという特徴がある。
セキュリティが高いというのは、インストール時にパッケージが不正に変更されていないかなどをチェックサムを用いて検証することができ、安全なパッケージのインストールが可能であるということ。
チェックサムとは、誤り検出符号の一つで、データ列を整数値の列とみなして和を求め、これをある定数で割った余り(余剰)を検査用データとするもの。
要は、比較して同じかどうかを確かめる機能があるよ、ということなんでしょう。
バージョン管理についても優れていて、yarnではプログラムのインストール後に、yarn.lockというファイルが作成される。
それにはインストールしたプログラムが使用している別のプログラム(依存プログラム/パッケージ)のバージョンが明確に書き込まれている。
依存プログラム/パッケージをその後再度インストールしてもバージョンの整合性が保たれるので、バージョン不一致でプロジェクトが動かなくなる危険性が無くなる。
npm、homebrew、MacPortsからインストールできる。
そこで、yarnをインストールしてみた。
$ npm install -g yarn
んで、立ち上げてみた。
$ yarn serve
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.128.103:8080/
Note that the development build is not optimized.
To create a production build, run npm run build.
すると、おお、立ち上がった!
では、この出来たてホヤホヤのプロジェクトの構成ファイルを見てみる。
余談ですけどPower Shell treeのコマンドだと、そこの階層はどどどっとすべて表示しちゃうんですな。
node_modules
public
src
.gitignore
babel.config.js
package.json
package-lock.json
README.md
Pythonだとtree図、どどどっと、つくれそう。
この記事が参考になりそう。
https://qiita.com/amowwee/items/e63b3610ea750f7dba1b
それぞれなんだ?っというのはひとまずおいておいて。
yarn serve を実行。
そうすると、開発版のビルドが実行され、ローカルサーバーが立ち上がる。
8080版ポートがすでに専有されている場合別のポート番号になっていることもある。
ブラウザで http://localhost:8080/ にアクセスしてみると
神々しいVの文字が現れました!
※一度、PCをシャットダウンしたりすると、再度プロジェクトディレクトリに移動して
$ yarn serve
をする必要がありました。
先程作成した、プロジェクトディレクトリ、今回の例ですと my-project の中の /src/App.vue を見てみる。
アプリケーションのソースコードとして、扱うのは主にsrcディレクトリの中。
App.vueを開いてみると、
<template></template>
という見慣れないタグが。
更に、
<HelloWorld msg="Welcome to Your Vue.js App"/>
という、見慣れないタグみたいなものがあります。
<img alt="Vue logo" src="./assets/logo.png">
上記を消してみると、ブラウザを更新していないのにきえる!
今度は、
<img alt="Vue logo" src="./assets/logo.png">
のところを
<p>Hello My Vue</p>
と、書いてみると
これも即座に反映される!
この機能をファイルの変更を検知して、動的にレンダリングする。
この機能をホットロードという。
これは、Vue CLIによって作成されたプロジェクトに含まれる vue-loader の機能となる。
新規ファイルを作成したときや新しくVue オブジェクトにプロパティを追加したときはホットロードの対象でないときがある為、「おかしいな?」と思った際はブラウザをリロードしてみたりしてみよう。
コンポーネントの分割とは?
Webページをパーツ単位に分割する事ができて、その単位を「コンポーネント」と呼ぶ。
Vue.jsはコンポーネント指向を思想として持っている。
- コンポーネントはインスタンスを持つ
- それぞれのライフサイクルに従って独立する
- 独立しつつ、propsやeventといったインターフェイスでほかのコンポーネント情報のやりとりを行う
例えば、ヘッダー・フッター・サイドバー・メインコンテンツは別のコンポーネントに分離する事ができる。
どの範囲までを、管理対象として切り分けるかがポイントとなるか、ということかな?
コンポーネントの分割
Vue.jsでは.vueという拡張子のファイルをシンプルファイルコンポーネント(SFC)と呼ぶ。
これはvue-loaderというパッケージよって解釈され、JavaScriptに変換されるようになっている。
重要なのはこのファイルの構成に親しみがもてるかとうか?
このSFCがどのような構成でできているのかを my-project の中の /src/App.vue で見ていってみる。
<template>
<script>
<style>
の3つのブロックに分かれている。それぞれ、見ていってみる。
template
の中にはVue.jsのシンタックスを使用してHTMLを書くことができる。
一番親にあたるタグには兄弟要素を持てない。
「error The template root requires exactly one element」
というエラーになってしまう。
<!--これはエラーになる!!-->
<template>
<div>親1</div>
<div>親2</div>
</template>
上記のようは場合はそっとそれらをdivで囲ってあげよう。
<!--これはエラーになる!!-->
<template>
<div>
<div>親1</div>
<div>親2</div>
</div>
</template>
script
の中には new Vue()に渡すオブジェクトをexportする。
App.vueでexportされたこのオブジェクトはsec/main.js内でimportされ new Vue()される。
このオブジェクト内のconponentsプロパティに書き込まれたオブジェクトはローカルなコンポーネントとして登録される。
<script>
import HellowWorld from './components/HellowWorld.vue'
export default {
name 'App',
components: {
HelloWorld
}
}
</script>
Vue.jsのプロジェクトをこのひな形で作るならES6以降で追加された構文は要チェック!
ES6で書かれているということはES6未対応のブラウザ、例えばIE11では動作しない?
と思うかもしれないが安心してください。大丈夫。
プロジェクトを作成した際、プリセットとして default(babel,eslint)を選択している。
ES6以降で追加された構文はこのプリセットに含まれるBabelでコンパイルされ、IE11でも利用可能なES5のコードに変換してくれる。
style
の中にはこのファイルを読み込んだときに適用されるCSSを定義できる。
このstyleにはscopedというオプションがあり、stylescopedのように指定可能。
こオプションを有効にするとコンポーネントに自動でクラスが割り当てられほかのコンポーネントにCSSが波及しないようしてくれる。
また、langというオプションもあり stylelang=”scss”のように指定することができる。
このようにすると、
の中をSCSSの文法で書くことが可能となる。
最初、 sass-loader というパッケージが含まれていあに為、最初はエラーになるが一度、開発サーバーを止め、
yarn add sass-loader node-sass
を実行しインストール、そして再度 yarn serve することで SCSS が利用可能となる。
scoped と lang は併用することが可能で、この協力な2つのオプションにより複雑なコンポーネントが作成でき、ほかのコンポーネントへのCSSの汚染を心配せずに済む。
どういうことだろ?
コンポーネントの分割
Vue.jsで大きなアプリケーションを作成する際に必要になるコンポーネントの分割方法についてみてみる。
子コンポーネントを作成する
今回作成したプロジェクトの src/components/HelloWorld.vueを見てみる。
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<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 noreferrer">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-babel" target="_blank" rel="noopener noreferrer">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener noreferrer">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener noreferrer">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener noreferrer">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener noreferrer">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener noreferrer">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener noreferrer">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener noreferrer">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener noreferrer">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener noreferrer">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener noreferrer">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener noreferrer">awesome-vue</a></li>
</ul>
</div>
</template>
沢山記載されているが、Vue.jsの構文が作成されているのは{{msg}}だけ。
次に、 の中。
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
この中のpropsが、親コンポーネントから状態を受け取るインターフェイスとなっている。
ここで受け取る値)メソッド同様変化するとリアクティブにレンダリングが変化する。
props: {
msg: String
}
````
は、親コンポーネントからmsgというプロパティを受け取る、という事を示している。
次に、新しい子コンポーネントとして、カウンターを作成してみる。
src/Counter.vueを作成し、下記のコードを記載する。
```js
<template>
<div class="counter">
<p>{{ name }}</p>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
export default {
props: {
name: String,
initCount: Number
},
data(){
return {
count: this.initCount
}
},
methods: {
increment(){
this.count++;
}
}
}
</script>
<style lang="scss" scoped>
.counter{
width: 200px;
}
</style>
yarn add sass-loader node-sass
を実行してして、エラーになるので実行し、再度サーバーを立ち上げ直す。
このコンポーネントはpropsでnameとinitCountを受け取り、ボタンをクリックする度にcountが増えていく。
methods: {
increment(){
this.count++;
}
しているのは、コンポーネントの初期化テクニック。
渡された値を直接書き換えないのか、というと props は親コンポーネントから渡される値が一方的に代入されるインターフェ-ースであり、コンポーネントの内側で置き換えてはいけないという決まりごとになっている為。
子コンポーネントの呼び出し方
作成した子コンポーネントをApp.vueで呼び出してみる。
まず、呼び出したいコンポーネントファイルをインポートする必要がある。
src/App.vue を見ていく。
import HelloWorld from './components/HelloWorld.vue'
の、部分はHelloWorldコンポーネントをimportしている。
importする変数名は何でもよいが、ファイル名のパスカルケース、単語の先頭を大文字にするものが一般的。
(例:HelloWorld)
components:{
HelloWorld
}
で、Vueオブジェクトの中にcomponentsというキー名のオブジェクトを作成している。
ここで、importした変数を展開している。
キーと値(変数名)が同じ場合、値を省略できる構文を作成している。
省略せずに書くと HelloWorld: HelloWorld となる。
このキー名で子コンポーネントとして登録される。
<HelloWorld msg="Welcome to Your Vue.js App"/>
最後に、components でマウントしたキー名でHTMLタグのようにに書き入れている。
これで、子コンポーネントを親コンポーネントで呼び出すことができる。
ここから、新たに作成したコンポーネントを呼び出す記述を書いていく。
import Counter from `./components/Counter.vue'
<Counter name="Counter 1" :initCount="5" />
<Counter name="Counter 2" :initCount="10" />
src/Ape.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<!--ここにカウンターコンポーネントを追記-->
<Counter name="Counter 1" :initCount="5" />
<Counter name="Counter 2" :initCount="10" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
<!--ここに呼び出し用のコンポーネントを記載-->
import Counter from './components/Counter.vue'
export default {
name: 'App',
components: {
HelloWorld,
Counter
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
気を付けポイント!
- conponentsに入っているコンポーネントは,で区切る
initCount=”5″
と書いてしまうと String型のpropsとして解釈される。
:initCount=”5″
のように書くと ” ” の中はJavaScript式として解釈される。
よって、
:initCount=”5″ は Number型の5として実行することができる。
:はv-bindのシンタックスシュガー。
v-bindディレクティブは、リアクティブに HTML 属性を更新する。
子コンポーネントから親コンポーネントへデータのやりとり!
子コンポーネントから親コンポーネントへのデータのやりとりはVue.$emit()メソッドを使用し、イベント(event)を子から親に伝える。
Counter.vue に下記を追記する。
this.$emit("emitUp",{name: this.name, counted: this.count})
追記したCounter.vue。
<template>
<div class="counter">
<p>{{ name }}</p>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
export default {
props: {
name: String,
initCount: Number
},
data(){
return {
count: this.initCount
}
},
methods: {
increment(){
this.count++;
this.$emit("emitUp",{name: this.name, counted: this.count})//←追記箇所
}
}
}
</script>
<style lang="scss" scoped>
.counter{
width: 200px;
}
</style>
次にApp.vueを下記のように変更する。
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<!--置き換わった部分-->
<Counter name="Counter 1" :initCount="5" @emitUp="getEvent" />
<Counter name="Counter 2" :initCount="10" @emitUp="getEvent" />
<!--追記する部分-->
<p>
EventStack:
{{ stack }}
</p>
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import Counter from './components/Counter.vue'
export default {
name: 'App',
components: {
HelloWorld,
Counter
},
//追記する部分
data(){
return {
stack: []
}
},
methods: {
getEvent(payload){
this.stack.push(payload)
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
すると、カウンターをクリックする度にイベントスタックが表側に表示されるようになる。
このデータの流れを押さえようとするのはちょっと複雑…!
ここまでをうけて
Vue.jsは簡単に利用できて、ページをリロードしなくても画面が変化するのは楽しい!
次はSPAに調整したいと思います。