Open hysryt opened 6 years ago
$ npm install vue
または
$ yarn add vue
var d = {
message: 'hello'
}
new Vue({
el: '#app1',
data: d,
});
<div id="app1">
{{ message }}
</div>
el
: バインドする要素data
: バインドするデータVueインスタンスによってel
とdata
をバインドする
d.message = 'bye'
とすると自動的にメッセージが変わる
{{ message }}
が出力されてしまうコンポーネントを使用する
コンポーネントは、テンプレートとデータを持つ。
new Vue(...)
で指定したel
要素内にコンポーネントのカスタムタグがあると、コンポーネントのテンプレートからDOMを生成し、コンポーネントのデータとバインドする。
カスタムタグは生成したDOMに置き換えられる。
var d = { message: 'hello' };
// コンポーネント定義
// テンプレートとデータを記述する
// テンプレートから実際のDOMが生成され、データとバインドされる
Vue.component('my-message', {
template: '<span>{{ message }}</span>',
data: function() {
return d;
}
});
// `#app1`内のコンポーネントを有効化する
new Vue({
el: '#app1',
});
<div id="app1">
<my-message></my-message> <!-- ← カスタムタグ -->
</div>
コンポーネントはVue.component(...)
で定義する
template
: HTML要素を記述するコンポーネント名の要素がこの要素に置換されるdata
: バインドするデータを返す関数new Vue(...)
からdata
は消す
HTMLにはコンポーネント名の要素を記述する
template
は一つの要素でなければならない。子要素はいくつあっても良い。
Vue.component(...)
でコンポーネントを定義するとどこでも使えるグローバルなコンポーネントとなる。
それに対し、new Vue(...)
内でコンポーネントを定義すると、特定の要素内のみで使用できるローカルなコンポーネントとなる
var d = { message: 'hello' };
new Vue({
el: '#app1',
// ローカルなコンポーネントの定義
components: {
'my-message': {
template: '<span>{{ message }}</span>',
data: function() {
return d;
}
}
}
});
<div id="app1">
<my-message></my-message> <!-- ← カスタムタグ -->
</div>
my-message
コンポーネントは#app
要素内でしか使用できない
カスタムタグに付与した属性をコンポーネントから使用できる
<div id="app1">
<my-message name="alice"></my-message>
<my-message name="bob"></my-message>
</div>
name
属性をコンポーネントから使用する
new Vue({
el: '#app1',
// ローカルなコンポーネントの定義
components: {
'my-message': {
props: ['name'],
template: '<span>{{ message }}, {{ name }}</span>',
data: function() {
return d;
}
}
}
});
props
プロパティに使用する属性名を指定することで、template
に属性値を使用できる
new Vue(...)
でデータとカスタムタグの属性値をバインドする
<my-message v-bind:name="name[0]"></my-message>
<my-message v-bind:name="name[1]"></my-message>
v-bind:属性名="データ名"
で属性値とデータをバインドできる
この例ではカスタムタグのname
属性にname[0]
をバインドする
name
配列はnew Vue(...)
で記述する
new Vue({
el: '#app1',
data: {
name: [ 'alice', 'bob' ], // name[0] と name[1]
}
// ローカルなコンポーネントの定義
components: {
'my-message': {
props: ['name'],
template: '<span>{{ message }}, {{ name }}</span>',
data: function() {
return d;
}
}
}
});
v-for
で繰り返しできる
<my-message v-for="n in name" v-bind:name="n"></my-message>
name
配列の要素数分<my-message>
が繰り返される
<my-message>
のname
属性にはn in name
のn
が入るため、
一つ目の<my-message>
のname
属性にはalice
、
二つ目の<my-message>
のname
属性にはbob
が入ることになる
もしクライアントでテンプレートをコンパイルする必要がある (例えば、 template オプションに文字列を渡す、もしくは DOM 内の HTML をテンプレートとして利用し要素にマウントする) 場合は、コンパイラすなわち完全ビルドが必要です。
new Vue(...)
は一つのページで一つのみ?
Vue アプリケーションは、 new Vue で作成されたルート Vue インスタンス(root Vue instance)で構成され、必要に応じてネストされたツリーや再利用可能なコンポーネントで形成されます。
https://jp.vuejs.org/v2/guide/instance.html
コンポーネントへデータを渡すには明示的な記述が必要
item in list
のitem
も同じ
v-for
の動作とコンポーネントを分離するため
v-bind:ele="item"
とprops: ['ele']
ele
は任意の文字列で、これがv-for
とコンポーネントをつなぐ
しかしながら、これはいかなるデータもコンポーネントへ自動的に渡すことはありません。なぜなら、コンポーネントはコンポーネント自身の隔離されたスコープを持っているからです。反復してデータをコンポーネントに渡すためには、プロパティを使うべきです
https://jp.vuejs.org/v2/guide/list.html#コンポーネントと-v-for
コンポーネント
コンポーネントは Vue.js の最も強力な機能の 1 つです。基本的な HTML 要素を拡張して再利用可能なコードのカプセル化を助けます。高いレベルでは、コンポーネントは Vue.js のコンパイラが指定された振舞いを加えるカスタム要素です。
v-model
<input v-model="something">
は、以下の糖衣構文です
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
new Vue(...)
で指定するel
がルートコンポーネントと言える
しかしカスタムタグ名はつけられないのでIDでの指定が必要
Vue.component('my-component', {
template: '<div>私のコンポーネント</div>'
});
<div>私のコンポーネント</div>
をひとつのコンポーネントとしてmy-component
という名前で登録している
もちろん入れ子にもできる
Vue.component('my-component', {
template: '<ul><li>リスト1</li><li>リスト2</li></ul>'
});
ただしルート要素はひとつでなければならない
// ダメな例
Vue.component('my-component', {
template: '<div>ルート要素が</div><div>2つある</div>'
});
//これなら良い
Vue.component('my-component', {
template: '<div><div>ルート要素</div><div>で囲う</div></div>'
});
コンポーネント内に他コンポーネントを含むこともできる
Vue.component('my-inner', {
template: '<div>内部</div>'
});
// my-innerコンポーネントを使用する
Vue.component('my-outer', {
template: '<div><my-inner></my-inner></div>'
});
コンポーネントはデータも持てる
Vue.component('my-component', {
template: '<div>{{ message }}</div>'
data: function() {
return {
message: 'hello',
};
}
});
data
にはオブジェクトではなくオブジェクトを返す関数を指定することに注意
なぜかというと、コンポーネントが複数回使用された時、データへの参照が共有されるのを防ぐため
関数であればコンポーネント使用の度にデータを生成するため、共有される心配がなくなる
親から子へはプロパティでデータを渡す 子から親へはイベントでデータを渡す
すべてのプロパティは、子プロパティと親プロパティの間の単方向 (one-way-down) バインディングを形成します: 親プロパティが更新したとき、それは子プロパティに伝わり、その反対はありません。
<my-component v-bind:something="親が持つデータ変数名"></my-component>
Vue.component('my-component', {
props: ['something'],
}
<my-component v-on:someevent="onSomeevent"></my-component>
new Vue({
methods: {
onSomeevent(param) {
console.log(param);
}
}
});
Vue.component('my-component', {
created: function() {
this.$emit('someevent', 'aaaaa');
}
});
created
以外にもmethods
ないの関数などからも呼び出せる
素のjavascript構文で配列やオブジェクトに追加するとVue.jsが値の変更を検知できないため、Vue.jsの用意した構文を使用して追加及び変更をする必要がある
data: {
option: [],
}
// pushを使う
this.option.push(...);
data: {
option: {},
}
// $setを使う
this.$set(this.option, キー, 値);
push
はthis.option
、$set
はthis
から呼び出すことに注意
これで追加すれば配列内やオブジェクト内のデータを変更するとVue.jsが検知できるようになる
{{ val }}
やv-bind:value="val"
など、テンプレートに書いた変数へはいつアクセスするか
computed
プロパティが実行されるタイミングを調べる
<div id="app">
<my-app></my-app>
</div>
<script type="text/x-template" id="template-my-app">
<span>{{ val }}</span>
</script>
Vue.component('my-app', {
template: '#template-my-app',
beforeCreate: function() {
console.log('beforeCreate');
},
created: function() {
console.log('created');
},
beforeMount: function() {
console.log('beforeMount');
},
mounted: function() {
console.log('mounted');
},
computed: {
val: function() {
console.log('*** computed ***');
return 0;
}
},
});
new Vue({
el: '#app',
});
beforeCreate
created
beforeMount
*** computed ***
mounted
beforeMount
とmounted
の間でアクセスされる
変数に必要なものはbeforeMount
までに用意しておく
<script type="text/x-template" id="template-my-app">
<span>{{ option.val }}</span>
</script>
Vue.component('my-app', {
template: '#template-my-app',
data: function() {
return {
option: {},
};
},
created: function() {
this.$set(this.option, 'val', 100);
},
});
data
プロパティのoption
にはval
は存在しないが、created
の時点でval
を追加しているため、テンプレートの{{ option.val }}
はちゃんと表示される
<div id="app">
<my-app></my-app>
</div>
<script type="text/x-template" id="template-my-app">
<span></span>
</script>
Vue.component('my-app', {
template: '#template-my-app',
beforeCreate: function() {
console.log('beforeCreate - child');
},
created: function() {
console.log('created - child');
},
beforeMount: function() {
console.log('beforeMount - child');
},
mounted: function() {
console.log('mounted - child');
},
});
var vm = new Vue({
el: '#app',
beforeCreate: function() {
console.log('beforeCreate - parent');
},
created: function() {
console.log('created - parent');
},
beforeMount: function() {
console.log('beforeMount - parent');
},
mounted: function() {
console.log('mounted - parent');
},
});
beforeCreate - parent
created - parent
beforeMount - parent
beforeCreate - child
created - child
beforeMount - child
mounted - child
mounted - parent
Parent Child
[beforeCreate]
|
[created]
|
[beforeMount]
|
| [beforeCreate]
| |
| [created]
| |
| [beforeMount]
| |
| [mount]
|
[mount]
親は子を全てマウントしてから自らをマウントする
<div id="app">
<my-app :val="val"></my-app>
</div>
<script type="text/x-template" id="template-my-app">
<span>{{ val }}</span>
</script>
Vue.component('my-app', {
props: ['val'],
template: '#template-my-app',
beforeUpdate: function() {
console.log('beforeUpdate - child');
},
updated: function() {
console.log('updated - child');
}
});
vm.val = 1 // データを変更
beforeUpdate - parent
beforeUpdate - child
updated - child
updated - parent
Parent Child
[beforeUpdate]
|
| [beforeUpdate]
| |
| [updated]
|
[updated]
Vue.jsはデータバインディングを得意とするフレームワーク。 Vue.jsによって用意された変数はDOMにバインドされており、変数の値を書き換えるだけで自動的にDOMも書き換わる。
変数の値の書き換えからDOMの書き換えまで、素のJavascriptでは以下のステップを踏む必要がある
Vue.jsを使えば上記の1.を行うだけで自動的に2.,3.をVue.jsが行ってくれる。 書き換え対象のDOMの数が多くなるほどVue.jsのメリットを享受できる。
ライブラリではなくフレームワークというだけあって、Vue.jsの機能を使う際はVue.jsの用意した記法にのっとって記述する必要がある。
Vue.jsで作成したアプリケーションをVueアプリケーションという。 Vueアプリケーションは1つ以上のVueコンポーネントの組み合わせで構成される。
Vueコンポーネントの組み合わせは、ルートコンポーネントを元にしたツリー構造で構成される 全てのVueコンポーネントは複数の子を持つことができ、ルートコンポーネント以外のVueコンポーネントは必ず1つの親を持つ
ルートコンポーネントはnew Vue({...})
で作成する。
new Vue({
el: '#app'
})
引数のオブジェクトは複数のプロパティを持つことができる。 https://jp.vuejs.org/v2/api/#オプション-データ
el
プロパティはマウントするDOM要素を指定する。
マウントすることでデータバインディングが可能となる
<div id="app">
{{ message }}
</div>
new Vue({
el: '#app',
data: {
message: 'hello'
}
})
data.message
プロパティが{{ message }}
に出力される
双方向バインディングを行うディレクティブ フォームタイプ(テキストボックス、ラジオボタンなど)によって動作が異なる
<input type="text" v-model="something">
something
に代入するsomething
に代入された文字列をフォームに入れる
<input type="radio" value="apple" v-model="fruit">
<input type="radio" value="banana" v-model="fruit">
<input type="radio" value="lemon" v-model="fruit">
value
をfruit
に代入するfruit
に代入された値とvalue
が一致する項目をチェック状態にする
<input type="checkbox" value="apple" v-model="fruits">
<input type="checkbox" value="banana" v-model="fruits">
<input type="checkbox" value="lemon" v-model="fruits">
fruits
は配列
value
をfruits
配列に追加するfruits
に追加された値とvalue
が一致する項目をチェック状態にするfruits
から削除された値とvalue
が一致する項目を非チェック状態をする
<my-input v-model="something"></my-input>
と書いた場合、以下と同じ動きになる
<my-input v-bind:value="something" v-on:input="value => { something = value }"></my-input>
つまり、コンポーネントにはvalue
プロパティとinput
イベントが渡される
input
イベントは発火すると引数の値をsomething
に代入する
<div id="app">
<div>
<input type="checkbox" v-model="isActive">
</div>
<transition name="fade">
<p v-if="isActive">
hello
</p>
</transition>
</div>
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
var vm = new Vue({
el: '#app',
data: {
isActive: false,
}
});
以下のクラスがアニメーションの状態によって自動的に付与される
xxx-enter
: enterの開始状態xxx-enter-active
: enterのアニメーション中xxx-enter-to
: enterの終了状態xxx-leave
: leaveの開始状態xxx-leave-active
: leaveのアニメーション中xxx-leave-to
: leaveの終了状態xxx
は<transition>
に指定したname
属性値
UMD | CommonJS | ES Module | |
---|---|---|---|
完全 | vue.js | vue.common.js | vue.esm.js |
ランタイム限定 | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |
UMD | CommonJS | ES Module | |
---|---|---|---|
完全 | vue.min.js | - | - |
ランタイム限定 | vue.runtime.min.js | - | - |
<script>
タグで使用するスクリプトhttps://jp.vuejs.org/v2/guide/installation.html
$ yarn init -y
$ yarn add vue
$ yarn add webpack -D
$ vi webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './script',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'output.js',
},
resolve: {
alias: {
'vue': 'vue/dist/vue.esm.js',
},
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
},
}),
],
};
require('vue')
やimport Vue from 'vue'
した時のファイルの参照先をvue.esm.js
にするprocess.env.NODE_ENV
をproduction
に設定する$ vi script.js
import Vue from 'vue';
new Vue({
el: '#app'
});
https://jp.vuejs.org/v2/guide/render-function.html
https://qiita.com/kazupon/items/2cc9a3427f468866d6dd
template
の代わりにrender
を使うことでテンプレートのコンパイル処理を無くすことができ、全体的な処理速度が早くなる。
template
Vue.component('my-elem', {
data: function() {
return {
message: 'hello',
};
},
template: '<h1>{{ message }}</h1>',
});
'<h1>{{ message }}</h1>'
のコンパイル処理がオーバーヘッドとなるrender
Vue.component('my-elem', {
data: function() {
return {
message: 'hello',
};
},
render: function(createElement) {
return createElement('h2', this.message);
},
});
createElement
関数createElement(arg1, [arg2,] arg3)
引数 | 説明 |
---|---|
arg1 |
{String \| Object \| Function} タグ名、コンポーネントオプション |
arg2 |
{Object} データオブジェクト |
arg3 |
{String \| Array} 子のVNode |
<div>
<h1>Hello</h1>
</div>
render: function(createElement) {
return createElement('div', [
createElement('h1', 'Hello'),
]);
}
<ul>
<li>apple</li>
<li>banana</li>
<li>lemon</li>
</ul>
render: function(createElement) {
return createElement('ul', [
createElement('li', 'apple'),
createElement('li', 'banana'),
createElement('li', 'lemon'),
]);
}
<ul id="foo">
<li>apple</li>
<li>banana</li>
<li>lemon</li>
</ul>
render: function(createElement) {
return createElement('ul', {
attr: {
id: 'foo'
}
}, [
createElement('li', 'apple'),
createElement('li', 'banana'),
createElement('li', 'lemon'),
]);
}
class
とstyle
は特別
class
のBoolean値はデータとバインドが可能
<ul id="foo" class="bar">
<li>apple</li>
<li>banana</li>
<li>lemon</li>
</ul>
render: function(createElement) {
return createElement('ul', {
class: {
bar: true
},
attr: {
id: 'foo'
}
}, [
createElement('li', 'apple'),
createElement('li', 'banana'),
createElement('li', 'lemon'),
]);
}
'my-elem', {
attrs: {
test: 'hello'
}
}
<my-elem test="hello"></my-elem>
'my-elem', {
props: {
test: 'hello'
}
}
<my-elem :test="'hello'"></my-elem>
input
などのHTMLタグの場合、value
属性を指定するにはattrs
を使用する必要があるVue.js + Babel
$ yarn add -D rollup vue rollup-plugin-node-resolve rollup-plugin-replace rollup-plugin-vue vue-template-compiler rollup-plugin-babel @babel/core @babel/preset-env
import resolve from 'rollup-plugin-node-resolve'
import replace from 'rollup-plugin-replace'
import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel';
export default {
input: 'src/script.js',
output: {
file: 'script.js',
format: 'iife'
},
plugins: [
resolve(),
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
vue(),
babel({
'presets': [
'@babel/preset-env'
],
'extensions': ['.js', '.vue']
})
]
}
Rollup では process.env.NODE_ENV
を使わないので置換する必要がある。
import Vue from 'vue';
import App from './components/App.vue';
new Vue({
el: '#app',
render: (h) => h(App)
});
ランタイム限定版( vue.runtime.esm.js
)を使うため #app
への描画を render
でおこなう。
<template>
<div>Hello</div>
</template>
<!doctype html>
<html>
<head>
<script src="./script.js" defer></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
https://github.com/vitejs/vite Vue.js用の開発環境、ビルドツール
$ npm init vite-app vue-todo-list
$ cd vue-todo-list
$ npm install
$ npm run dev
Vue.js 3から導入されたコンポーネントの定義方法。 関数ベース。
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
count: 0,
})
const onCountUpClick = () => state.count++
return {
state,
onCountUpClick,
}
}
}
@vue/composition-api
プラグインを使用すれば Vue.js 2 でも使用できる。
https://www.npmjs.com/package/@vue/composition-api
https://composition-api.vuejs.org/ https://qiita.com/azukiazusa/items/1a7e5849a04c22951e97
Vue.js 3 以前から使用されていたコンポーネントの定義方法。 Vue.js 3 以降でも使用可能。 オブジェクトベース。
export default {
data() {
return {
state: {
count: 0,
}
}
},
methods: {
onCountUpClick() {
this.state.count++
}
}
}
data
プロパティの値は、Vueのreactivity system
として追加されるvar vm = new Vue({ data: d });
vm.a === d.a; // true
vm.a = 2 data.a // => 2
data.a = 3 vm.a // => 3
mounted
、updated
、destroyed
などがある 例からも分かる通りthis
はVueインスタンスを指すthis
がVueインスタンスではなく親オブジェクトを指してしまうので要注意{{ text }}
{{ text }}
でtextがHTML文だった場合、エスケープして出力される。 HTMLとして出力したい場合は<div v-html="raw-html"></div>
とする。raw-html
プロパティが<div>
の中に出力される しかしXSSの危険性があるので容易に使うべきではないタグの属性値として出力したい場合は
v-bind:id="dynamicId"とする id属性にdynamicId
プロパティの値が入る{{ ... }}
やv-bind:id="..."
などの...
の部分には式も記述できるv-on:click="doSomething"
でイベント時の処理を指定できるv-bind:id
は:id
と省略できるv-on:click
は@click
と省略できる{{ ... }}
にはcomputed property
を渡すこともできるcomputed property
はVueコンストラクタの引数で指定するcomputed
プロパティにcomputed property
を記述する テンプレート側では{{ reverseMessage }}
とするcomputed property
は関数ではなくプロパティのようにアクセスできるwatch
を指定するとデータ変更時の処理を追加できるfirstName
またはlastName
が変更されるとfullName
も変更されるcomputed property
でも代用できるのでそちらの方が良い上記の場合、
isActive
がtrue
なら<div>
にactive
クラスが付与されるfalse
ならクラスなしisActive
が変更されるとまた値にしたがって反映される