初めに
Vue.js&Nuxt.js超入門を読んだためアウトプットの内容を投稿する。
簡単な使い方
コンテンツデリバリーネットワークを用いてVueを使用する場合はHTMLに以下のような記述をする。
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
new Vueを使用してvueを使ってみる
<!DOCTYPE html>
<html>
<head>
<title>My first Vue app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
// Vueのインスタンスを作成している。この中に幾つかのオブジェクトを配置する。
var app = new Vue({
// appというid属性を指定している
el: '#app',
// messageというデータプロパティを定義し、初期値を"Hello Vue!"に設定している
data: {
message: 'Hello Vue!'
}
})
</script>
</body>
</html>
実際に表示されるHTML
<div id="app">
Hello Vue!
</div>
messageの箇所が定義したHello Vue!という値に変更されていることがわかる。
Mustache構文
Mustache構文とは、データとテンプレートを紐づけるための構文であり、{{}}
(ダブルカーリーブラケット)を使用する。
先ほどの{{ message }}
が該当する。
Vueでは、データの変更を検知して自動的にテンプレートを更新するため、データが変更されると自動的にビューが更新される。
では、自動的にデータの変更を検知してテンプレートを更新するか確認してみる。
<body>
<div id="app">
{{ message }}
</div>
<button onclick="doAction();">
click
</button>
<script>
var data = {
message: 'Hello Vue!'
};
var app = new Vue({
el: '#app',
data: data
})
function doAction() {
data.message = "クリックしました";
}
</script>
</body>
上記では、クリックボタンを押すとdata
オブジェクトのプロパティであるmessage
にクリックしました
という文字列が格納されて表示が変更される。
また、data.message
はVueの変数ではなく、JavaScriptにある値である。
このように、元の変数の値を変更すると自動的に表示が変更される仕組みをリアクティブという。
テキストの入力で使用
よく使われる例として、以下のように、inputタグ内で入力した値をmessageに格納してリアクティブに表示することができる。
<body>
<div id="app">
{{ message }} <!-- 3. 2より値が自動的に変更される -->
</div>
<hr>
<input type="text" onInput="doAction(event);"> <!-- 1. 発火 -->
<script>
var data = {
message: ''
};
var app = new Vue({
el: '#app',
data: data
})
// 2. dataオブジェクトのmessageプロパティを変更
function doAction(event) {
data.message = event.target.value;
}
</script>
</body>
{{}}で式を使用
{{}}
でJavaScriptの式を使用することもできる。
先ほどのmessageの箇所を{{ message * 1.08 }}
に変更して数値を入力すると1.08倍の値を表示することも可能である。
プロジェクトの作成
Vueのプロジェクトを作成して起動してみる
- Vue 3を選択
% npm install vue
% vue create hello_app
% cd hello_app
% npm run serve
これでVueのプロジェクトを起動することができる。
作成したプロジェクトの主なディレクトリの役割は以下である。
publicディレクトリ・・・HTMLやCSSなどの公開されているファイル類が保管されている。
srcディレクトリ・・・Vue.jsで作成したファイルなどがまとめられている。
ビルド
実際にプロジェクトを公開する際には以下のコマンドを使用してビルドする必要がある。
npm run build
ビルドするとdist
というフォルダが作成される。
renderメソッド
複雑な表示や、HTMLを組み込みたい場合などにはrenderを使用する。
renderの前に仮想DOMに関して簡易的に知る必要があったので下記にまとめる。
- Vueでは、ブラウザの実際のDOM (Document Object Model) と同じような構造を持つ、仮想的なメモリ上のDOMを使用する。
- 仮想DOMでは、DOMの変更を検知して必要なものだけ実際のDOMへ反映するので効率が良い。
renderメソッドは以下のように記載する。
render: function(createElement) {
// 仮想DOMの生成を行う処理
}
引数にはcreateElement
が格納されている。
これは、仮想DOMの生成関数であり、VueのコンポーネントをHTMLのDOMに変換することができる。
下記は、renderを使用して先ほど記載したHTML同様にボタンをクリックするとHTMLが変更される。
<body>
<div id="app">
{{ message }}
</div>
<button onclick="doAction();">
click
</button>
<script>
var data = {
message: 'Hello Vue!'
};
var app = new Vue({
el: '#app',
data: data,
render: (element)=>{
return element("p", data.message);
}
})
function doAction() {
data.message = "クリックしました";
}
</script>
</body>
生成されるHTMLでは、divタグが表示されていない。
{{}}
を使用する場合とは異なり、renderを用いる場合はel
で取得しているHTMLも書き換えるようである。
<p>Hello Vue!</p>
createElement
createElement関数に関して詳細に記載する。
createElement(tagName, options, children);
optionsにcssを当てる
optionsは省略可能である。
下記はstyle属性を付与する例である。
createElement(
タグ名,
{
style: {
color: 'red',
},
},
表示する値
);
children
配列を使用して子要素を組み込むことができる。
createElement(
'div',
[
createElement('p', 'This is a paragraph.'),
createElement('ul', [
createElement('li', 'Item 1'),
createElement('li', 'Item 2'),
createElement('li', 'Item 3'),
]),
]
);
v-html
v-htmlは、{{}}
のようにテキストを表示するのではなく、 HTMLタグを解釈してHTMLとして表示することができる。
下記は諸々省略している。
<div v-html="htmlContent"></div>
<script>
export default {
data() {
return {
htmlContent: "<strong>This is <em>dynamic</em> HTML content!</strong>"
};
}
};
</script>
v-bind
v-bindは、HTMLの属性にVueのデータを割り当てることができる。
以下はhref
属性にurlというVueの変数を割り当てている。
これで動的にHTMLの属性の値を変更することができる。
<a v-bind:href="url">This is a link</a>
// v-bindという記述は省略も可能
<a :href="url">This is a link</a>
class
v-bindはclassにも使用することができる。
以下の例だと値がtrue
だとred
classが適応される
<p v-bind:class="{red: bool値}"></p>
classが複数ある場合などはオブジェクトを使用すると簡潔に記載できる。
※ class以外でもオブジェクトを使用することは可能なのでデータが複数ある場合に使用するとよさそう。
<div id="app">
<p v-bind:class="classes"></p>
</div>
<script>
var classObj = {
red: true,
blue: false
};
var data = {
classes: classObj
};
var app = new Vue({
el: '#app',
data: data
});
v-if
if文に相当するものである。
< タグ v-if"条件"> ・・・表示内容・・・</タグ>
< タグ v-else>・・・表示内容・・・</タグ>
例
通常のif文のようにcondition1
がtrueであればv-ifのpタグが表示されて、falseであればv-else-ifが評価されていく。
<template>
<div>
<p v-if="condition1">one</p>
<p v-else-if="condition2">two</p>
<p v-else>three</p>
</div>
</template>
template
templateタグは、コンポーネントのテンプレートを定義するために使用される。
v-ifで複数要素をレンダリングする際やv-forを使用する場合などに用いる。
v-for
for文に相当する機能である。
オブジェクトの場合はプロパティを取り出す順番は保証されていないので注意が必要である。
< タグ v-for"変数 in 配列 or オブジェクト">
// インデックス番号を用いる
< タグ v-for"{ 変数, インデックス } in 配列">
< タグ v-for"{ 変数, キー } in オブジェクト">
v-if v-forの組み合わせ
<tr v-for="(v, index) in arr" v-if="index % 2 == 0">
<td>{{index}}</td>
</tr>
<tr v-else>
<td>{{index}}</td>
</tr>
この場合はv-forが優先され、偶数の場合は v-if が 奇数の場合は v-else の方が表示される。
コンポーネント
Vueのコンポーネントは再利用可能なVueのインスタンスである。
基本的な使い方は以下である。
// 定義
Vue.component( 名前, {設定情報});
// コンポーネントの呼び出し
<コンポーネント名 />
例
idapp
のdivタグをルートインスタンスとし、その中でhelloコンポーネントを呼び出している。
<body>
<div id="app">
<hello/>
</div>
<script>
Vue.component('hello',
{
template: '<p>Hello!</p>'
})
var app = new Vue({
el: '#app',
});
</script>
</body>
コンポーネントに変数を渡す
<body>
<div id="app">
<hello/>
</div>
<script>
Vue.component('hello',
{
data: function() {
return {
message: 'これは新しいメッセージです。'
};
},
template: '<p>{{message}}</p>',
});
var app = new Vue({
el: '#app',
});
</script>
</body>
propsを使用して値を渡す。
propsとは、コンポーネントに値を渡すプロパティであり、親コンポーネントから子コンポーネントに値を渡す際などに使用される。
propsには、型の指定やデフォルト値の指定などのオプションがある。
以下の例だと、作成したhelloコンポーネントをルートインスタンスの子要素内で呼び出している。
props:['name']
で値を受け取り{{}}
で表示している。
今回の例だとpropsの配列の要素は1つだが複数受け取ることも可能である。
<body>
<div id="app">
<div><hello name="Taro" /></div>
<div><hello name="Hanako" /></div>
</div>
<script>
Vue.component('hello',
{
props:['name'],
template: '<p class="hello">Hello, {{name}}!</p>',
});
var app = new Vue({
el: '#app',
});
</script>
</body>
v-model
v-modelは、フォームの入力とVueのデータを双方向バインディングする。
これによって、フォームの値が変更されるとそれに同期してVueのデータも変更される。
input
select
textarea
などで用いることができる。
v-bindと異なる点は、フォームの入力をバインドするために使用されることである。
例としては以下であり、inputタグのmessageが変更されるごとにpタグの表示が変わる。
<div id="app">
<input type="text" v-model="message">
<p>{{ message }}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>
v-on
v-onは以下のイベントリスナーを登録することができる。
以下のイベントを登録することができる。
- click
- submit
- keydown
- keyup
- mouseover
また、v-onは@
と記載して省略可能である。
例
下記例だとbuttonタグをクリックした際にv-on:clickが発火し、incrementメソッドが呼び出される。
<div id="app">
<button v-on:click="increment">Increment</button>
<p>{{ count }}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
increment: function () {
this.count++;
}
}
})
</script>
computed
vueのcomputedプロパティは算術プロパティとも呼ばれ、プロパティの値を算出する手段である。
computedプロパティで定義された関数が依存するデータプロパティの値が変更された場合、自動的に再評価され、新しい値が返される。
そのため、動的な値を評価する際に用いられる。
以下の例だと、v-model
がバインドしているnum
が変更されると、computedプロパティのincrementが呼び出される。
<body>
<div id="app">
<hello/>
</div>
<script>
Vue.component('hello', {
data:function() {
return {
num:0,
};
},
computed:{
increment:function(event) {
return this.num++;
}
},
template: '<div><p>{{increment}}</p><div><input type="number" v-model="num"></div></div>',
});
var app = new Vue({
el: '#app',
});
</script>
</body>
ローカルコンポーネント
今まで使用していたVue.component
はグローバルなコンポーネントである。
コンポーネントをローカルで使用する場合は以下のようにcomponentsオプションを使用する。
new Vue({
el: '#app',
components: {
'my-component': MyComponent
}
})
子コンポーネントから親コンポーネントに値を渡す
子コンポーネントから親コンポーネントに値を渡す場合は$emitを使用する。
this.$emit('イベント名', データ);
イベント名は、発行したいカスタムイベントであり、データにはそのイベントに渡すデータである。
コンポーネントの詳細に関して
propsのバリデーション
propsのバリデーションには、型チェック・必須プロパティ・デフォルト値・カスタムバリデーションの4つがある。
props: {
prop1: {
// 型が `Number` であることを示します
type: Number,
// 必須プロパティであることを示します
required: true,
// デフォルト値を示します
default: 'default value',
// バリデーション関数を示します
validator: function (value) {
return value.length > 0
}
},
prop2: {
// 型が `String` or `Number`であることを示します
type: [String, Number]
}
}
ゲッターとセッター
getメソッドは、プロパティが読み取られたときに呼び出され、setメソッドは、プロパティに新しい値が設定されたときに呼び出される。
data:function(){
return {
val:0,
};
},
computed: {
a:{
get:function(){
// thisはdataの値を参照することができる
return this.val * 2;
},
set:function(value){
this.val = Math.floor(value / 2);
},
},
},
watch
watchオプションは、リアクティブなデータの変更を監視し、その変更に応じて任意の処理を実行する機能である。
watch: {
dataPropertyName: function(newVal, oldVal) {
// カスタムの処理をここに記述
},
}
上記の場合はdataPropertyName
というデータオブジェクト内にあるプロパティが変更された場合のみ関数が実行される。
watchオプションは、Vueコンポーネントがインスタンス化されたときに実行されるため、初期値に対してもコールバック関数が呼び出されます。
初期値に対してはコールバック関数を呼び出したくない場合は以下のようにimmediate: false
を付与する。
dataPropertyName: {
handler: function(newVal, oldVal) {
// カスタムの処理をここに記述
},
immediate: false // 初期値に対してはコールバック関数を呼び出さない
}
Vueのイベントに関しての詳細
Vueのイベントは、バブリングとキャプチャリングというDOMイベントの伝播方法が存在する。
バブリングは、子要素から親要素へイベントが伝播していく。
キャプチャリングは、親要素から子要素に向かってイベントが伝播する方法である。
バブリングの例
<div id="parent">
<button id="child">Click me</button>
</div>
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.addEventListener('click', function(event) {
console.log('parent');
});
child.addEventListener('click', function(event) {
console.log('child');
});
child
parent
上記例だと、子要素のaddEventListenerが発火した後に親要素のaddEventListenerが発火する流れになる。
では、イベントの流れを止めたい場合は以下のstopを使用することで可能になる。
<div id="parent">
<button id="child" @click.stop>Click me</button>
</div>
他にもstopのような機能が色々るが、紹介されていたのはsalfだった。
salfはstopと同様に記載し、自身の要素がクリックされた場合にのみイベントをトリガーすることができる。
slot
Vueのslotはコンポーネントのテンプレート内部で外部からコンテンツを挿入するための場所を定義するものである。
親コンポーネントから子コンポーネントに値を渡す際に用いられるため、外部のコンポーネントを渡す際に値を柔軟に変更することができ、コンポーネントの再利用も可能である。
slotには名前なしと名前ありのものがあ、名前ありは複数のslotを用いる場合などに使用する。
名前なし
<Child>
<p>slot</p>
</Child>
<div><slot /></div>
名前あり
<Child>
<p slot="a">slot a</p>
<p slot="b">slot b</p>
</Child>
<slot name="a" />
<slot name="b" />
nuxt.js
Nuxt.jsは、Vue.jsの上に構築されたフレームワークで、サーバーサイドレンダリング・ルーティングの自動生成などが提供される。
ルーティング
router-linkを使用すると、ユーザーがクリックすることで他のページに移動できる。
<router-link to="/other">Go to Other</router-link>
$route変数のparamsでパラメーターを取得できる。
$route.params.id
validate プロパティという動的なルートパラメータの検証を行うためのメソッドがある。
validate ({ params }) {
if (params.id == undefined || params.pass == undefined){
return false;
} else {
return true;
}
},
Vuex
Vuexは、Vue.jsアプリケーションのための状態管理ライブラリであり、各コンポーネントで状態を共有することができる。
vueでは、コンポーネントが複数あることがあるため、データの共有ができるようにする。
ストアの値はページをリロードした際に初期化されるので注意が必要。
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: function(){
return {
message: 'This is store message!',
};
},
})
}
export default createStore
下記でアクセス可能になる。
$store.state.message
ミューテーション
実際にストアの値は全てのコンポーネントで使用されるため、コンポーネントで更新することは推奨されていない。
そのため、ミューテーションというストア内のメソッドを使用してストアの値を変更する。
mutationsにメソッドを定義して、$store.commit('ミューテーション名')
でミューテーションを呼び出すことができる。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
$store.commit('increment')
console.log(store.state.count) // 1
ミューテーションは第二引数にペイロード(任意のデータ)を受け取ることができる。
mutations: {
increment (state, n) {
state.count = n
}
}
$store.commit('increment', 1)
複数の値を渡したい場合はオブジェクトを使用する。
アクション
アクションはミューテーションを呼び出すことができる。
複数のミューテーションを呼び出したり、アクションで非同期処理を行い、それが完了したらミューテーションを呼び出すなどの使い方がされる。
アクションは、commitメソッドを使用してミューテーションを呼び出す。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
})