タグ:Vue.js
とりあえずここからCDNを取得
jQueryのCDNと同様に、htmlに貼り付けるだけでVue.jsが利用できます。
<!-- 一度だけ変更可能なメッセージテンプレート --> <span v-once>{{ msg }}</span> <!-- href属性を動的に --> <a v-bind:href="url"> ... </a>
{{ ○○ }}
で単純なテンプレート構文を使って宣言
v-○○
から始まるVueの特別な属性で、属性値に設定した式が変化したときに、DOMに適用されます。
// Vueインスタンスの記述 var 変数名 = new Vue({ // elementオプション el : "#要素", // dataコンポーネント data: { プロパティ名: 値, プロパティ名: 値, }, // methodsコンポーネント methods: { プロパティ名: 値 }, // computedコンポーネント computed: { プロパティ名: 値 }, // watchコンポーネント watch: { プロパティ名: 値 }, })
基本的には変数としてVueインスタンスの宣言をして、
el
には紐付けする要素。
data
に連動させる詳細なプロパティを指定させます。
この他にもmethods
、computed
、watch
などがあります
Vue.jsを本格的に使う場合はコンポーネント書式などはありますが、初歩的な記述は大体はel
&data
+アルファをまとめるだけでHTMLとJS部分は基本的なレンダリングは設定できます
機能 | 書式 |
---|---|
データを表示 | {{ データ }} / v-text / v-html |
データ(オブジェクト構造)を表示 | {{ $data }} |
属性データの指定 | v-bind |
入力フォームとデータの紐づけ | v-model |
イベントとメソッドをつなぐ | v-on |
条件表示 | v-if |
繰り返し表示 | v-for |
データの計算 | computed |
データの監視 | watch |
部品にまとめる | component |
他にもたくさんありますが、今回はメインどころであろうディレクティブとオプションを取り上げます。
<div id="app"> <!-- ①マスタッシュタグの場合 --> <p>{{ myText }}</p> <!-- 文字列で"<h1>Hello!</h1>"が表示される --> <!-- ②「v-text」の場合 --> <p v-text="myText"></p> <!-- 文字列で"<h1>Hello!</h1>"が表示される --> <!-- ③「v-html」の場合 --> <p v-html="myText"></p> <!-- h1タグの"Hello!"で表示 --> </div>
new Vue({ el: "#app", data: { myText: '<h1>Hello!</h1>'} })
タグ内にデータをそのまま出力する場合は二重波括弧(マスタッシュタグ)内に記述。
v-text
テキストとして出力、v-html
はhtmlとして出力。
この辺はjQueryのtext()
やhtml()
と同じですね。
※マスタッシュタグはHTMLタグ中の属性などに直接記述はできません
<div id="bind" v-bind:style="{background:style}"> <h1 v-bind:align="align"> <a v-bind:href="href" target="_blank"> <img v-bind:src="src" alt="ときどきWEB" /> </a> </h1> </div>
var myBind = new Vue({ el : "#bind", data: { href: "http://tokidoki-web.com/", src: "http://tokidoki-web.com/wp-content/themes/yanyo/images/logo.png", align: "center", style: "red" } })
v-text
ディレクティブやv-html
ディレクティブだとあまりフレームワークを使ってる感は無いけど、
v-bind
で要素内のurlやパス属性以外にやスタイルもまるごと動的に設定できるので、こういう風にまとめられるとワンライナーで書くスクリプトと違ってテンプレート管理してるなって感じがします
完全な構文 | <a v-bind:href="url"> ... </a> |
---|---|
省略記法 | <a :href="url"> ... </a> |
動的引数の省略記法 | <a :[key]="url"> ... </a> |
<div id="myModel"> <form name="myModelForm"> <p> ①<label><input v-model="myInput" placeholder="名前を入力"></input> 私の名前は「{{ myInput }}」です(現在の文字数{{ myInput.length }})</label> </p> <p> ②<label><input type="radio" v-model="myRadio" value="①">男</label> <label><input type="radio" v-model="myRadio" value="②">女</label> <br><span>性別は{{ myRadio }}</span> </p> <p> ③<select v-model="myOption"> <option disabled value="">色を選択</option> <option value="red">赤</option> <option value="blue">青</option> <option value="green">緑</option> <option value="yellow">黄</option> </select> <span v-bind:style="{color: myOption}">選択した色</span> </p> <label><input type="checkbox" v-model="myAgree" >同意します</label> <p><button v-bind:disabled="myAgree==false">送信</button></p> </form> </div>
var myModel = new Vue({ el: "#myModel", data: { myInput: '', myRadio: '', myOption: '', myAgree: false } })
v-model
ディレクティブを使うとフォームなどのユーザーの入力値を即反映させることができる双方向データバインディングとして構成することができます
①では入力されたテキストを反映&その入力文字列数を反映させています
②ではradioボタンで選択したvalue値を反映させています
③ではoptionタグで選択されたvalue値を文字色としてstyleに反映させています
最後はmyAgree
キーを指定したinputタグにv-model:disabled
オプションを付けることで、同じくmyAgree
キーを指定したbuttonタグと紐付けられ、チェックが付いたらbuttonタグがクリック可能になります
<div id="myOn"> <p>{{count}}回</p> <button v-on:click="countUp">カウントアップ</button> <button v-on:click="countDown">カウントダウン</button> <button v-bind:disabled="isClick" v-on:click="oneClick">いいね {{ good }}</button> </div>
var myOn = new Vue({ el: "#myOn", data: { count: 0, good: 0, isClick: false }, methods: { countUp: function(){ this.count++; }, countDown: function(){ this.count--; }, oneClick: function(){ this.good++; this.isClick = true; alert("1いいねしました") } } })
v-on
ディレクティブではクリックイベントに独自のメソッドを繋げることができます。v-on
はよく使われるディレクティブなので@
で省略して書くことも可能です
dataで初期値0に設定しているcount
プロパティに++
や--
をmethods
プロパティに紐付ける事で「カウントアップ」「カウントダウン」をクリックすると加算・減算できます
カウントアップと同じ様に「いいね」ボタンをクッリクしたら1カウントを追加しますが、SNSのように1度しかクリックできない様に制限することもできます。
完全な構文 | <a v-on:click="doSomething"> ... </a> |
---|---|
省略記法 | <a @click="doSomething"> ... </a> |
動的引数の省略記法 | <a @[event]="doSomething"> ... </a> |
<div id="myIf"> <label><input type="checkbox" v-model="visible" v-on:click="show=!show">どっち?</label> <p> <button v-if="visible" v-on:click="doubt">そうだよボタン</button> <button v-else v-on:click="doubt">そうじゃないボタン</button> </p> <transition name="fade"> <p v-if="show">本当にそうか・・・?</p> </transition> </div>
var myIf = new Vue({ el: "#myIf", data: { visible: false, show: false }, methods: { doubt: function(){ this.show = true } } })
v-if
ディレクティブは名前通り、指定タグを条件によって表示させたり非表示させたりを設定できます
チェックボックスのON/OFFによってvisibleのtrue/falseが切り替わり、その条件によって「そうだよボタン」と「そうじゃないボタン」の表示・非表示が切り替わります
ふたたびチェックボックスのチェックを切り替えるとコメントが非表示に更新されます。
ついでにv-on
と紐付けてボタンをクリックするとdoubtメソッドが発動して「本当にそうか・・・?」コメントがフェードインします。
ここではアニメーションを適用させるためにtransition
を使用しています。(下記参照)
transition(変化・移行)の意味を持つVue.jsの組み込み型コンポーネントのひとつで<transition>
で囲った要素に対してアニメーションの管理ができます。
従来のフルスクラッチでは開発ではクリック操作などでフラグ管理としてのクラスを付与して、CSSやjQueryでフェードイン・フェードアウトを付けて
①変わる対象HTML要素、
②変える対象要素の変化(CSSまたはアニメーションスクリプト)、
③切り替えるイベント管理など
外部ファイルで書いた場合はそれぞれ別々の場所の関係性を意識しなければならなかった部分も<transition>
を利用することで直感的にわかりやすくまとめることができます
<!-- ON/OFFのフラグ管理担当 --> <button v-on:click="show=!show">どっち?</button> <transition> <!-- transitionの対象要素&ON時の役割 --> <p v-if="show">本当にそうか・・・?</p> </transition>
/* transitionがどうするかの指定 */ .v-enter-active, .v-enter-to { transition: opacity .5s; } .v-enter { opacity: 0; }
CSSの指定は初見はわかりにくいですが、Chromeの検証ツールで監視すると<transition>
は見えませんが、動かすと一瞬、<transition>
内に設定した要素にenter-active v-enter-to
クラスが付くのが確認できます。
この操作したい対象要素のON・OFFに対する物理的変化を補ってくれてるんですね。
v-if
は単品というよりはv-model
とv-on
などのディレクティブ(構成)と<transition>
の様なコンポーネント(役割)と組み合わせることでユーザー操作の導線を誘導・制限する様なUI構築に能力を発揮する様です。
<div id="myName"> <ul> <li v-for="item in myArray" v-if="item % 2 == 0">{{ item }}</li> </ul> <ul> <li v-for="(item, key) in myName">私の名前は「{{ item.lastName }} {{ item.firstName }}」です</li> </ul> <!-- データ構造をすべて出力 --> <pre>{{ $data }}</pre> </div>
var nameList = [ { lastName: "やんよ", firstName: "太郎" }, { lastName: "ヤンヨ", firstName: "タロウ" }, { lastName: "YANYO", firstName: "TARO" } ] var myName = new Vue({ el : "#myName", data: { myArray: [1,2,3,4,5,6], myName: nameList } })
v-for
ディレクティブを使う事でマークアップの段階で配列やオブジェクトにhtmlタグから直接干渉できるようになるので、いよいよjQueryとの差別化も感じてきます。
v-for
で展開対象にv-if
を組み合わせてループ中に条件一致したものだけ出力(デモでは偶数のみ出力)ということもできます。
Vueインスタンス内で指定されたオブジェクトや配列は{{ $data }}
を使う事でphpのvar_dump
の様なオブジェクト構造を全て出力できます。
<div id="app"> <!-- データ構造をすべて出力 --> <pre>{{ $data }}</pre> </div>
<div id="myComputed"> <p><input type="number" v-model.number="price">円 × <input type="number" v-model.number="count">個</p> <p>商品価格:{{ price }}円</p> <p>合計価格(税別):{{ total }}円</p> <p>合計価格(税込):{{ taxin }}円</p> <div> <p>{{ error() }}</p> <textarea v-model="comment" placeholder="コメントを入力"></textarea> <p v-if="!over">入力可能 <span v-bind:style="{color:color}">{{ leng }}</span> 文字({{ max }}文字制限)</p> <p v-else><span v-bind:style="{color:color}">現在 {{ leng }} 文字オーバーです</span>({{ max }}文字制限)</p> </div> </div>
var myComputed = new Vue({ el: "#myComputed", data: { price: 0, count: 1, comment: '', max: 30, over: false }, methods: { error: function(){ if(this.comment.length > this.max){ this.color = 'red' this.over = true }else{ this.color = 'blue' this.over = false } } }, computed: { total: function(){ return (this.price * this.count); }, taxin: function(){ return Math.round((this.price * this.count) * 1.08); }, leng: function(){ return Math.abs(this.max - this.comment.length); } } })
computed
オプションはタグ内に算出プロパティを設定できます
ひとつは入力された単品価格と個数によって合計価格と消費税8%を加算した税込価格を反映させます。
もうひとつはtextareaタグに入力された文字数のカウンター(leng
)を反映させながらerror
プロパティで制限数(max
)をオーバーしたらカウンター表記を条件分岐で切り替えます。
<div id="myWatch"> <p>お題:「{{ matchText }}」</p> <p>入力文字に「{{ forbidden }}」は{{ count }}個含まれています</p> <textarea v-model="watchText"></textarea> <p v-if="clear"><button v-on:click="startTimer">START</button>残り{{ restSec }}秒</p> <p v-else>入力成功!</p> </div>
var myWatch = new Vue({ el: "#myWatch", data: { count: 0, watchText: '', forbidden: "ロバ", matchText: "ラバかロバかロバかラバか分からないので ラバとロバを比べたらロバかラバか分からなかった", restSec: 10, timerObj: null, clear: true }, methods: { // カウントダウン startTimer: function(){ this.timerObj = setInterval(()=> {this.restSec-- }, 1000) } }, watch: { // 入力文字の正解チェック&存在チェック watchText: function(val){ if(val == this.matchText){ this.clear = false clearInterval(this.timerObj) } this.count = (this.watchText.match(new RegExp(this.forbidden,"g")) || []).length; }, // 時間切れアラート restSec: function(){ if(this.restSec <= 0){ alert("時間切れです"); clearInterval(this.timerObj) this.watchText = "" this.restSec = 10 } } } })
watch
オプションではデータや変数の値に変化があった場合の処理に使います
デモでは監視対象をtextareaに設定し、お題文字列に沿ったテキストを入力するとその値を監視して"ロバ"というキーワードがテキスト内に何個含まれているかが反映されます。
オマケの機能として「スタート」ボタンをクリックすると10秒間のカウントダウン処理が走り(ここはmethods
オプションで設定)時間制限内にお題の入力の成功or失敗によって分岐出力が変わります
Vue.jsの一番の醍醐味的な機能としてこのcomponent
があります。
コンポーネントは「名前付きの再利用可能なVueインスタンス」という説明と、よく"パーツ化する"や"機能の再利用化"といった表現がされますが、どうも理屈っぽすぎてしっくり来ない・・・
機能・効果としての"メソッド"と、名称や着想としての"コンセプト"、
"悪魔合体"させた「ボクの考えた最強オリジナルタグを作る!」というイメージが個人的には一番しっくりくる表現ですw
コンポーネントをボクの大好きな漫画「ダイの大冒険」で例えるならば、
主人公ダイの必殺技「アバンストラッシュ」は剣を専門に使うダイが習得しましたが、師匠であるアバン先生いわく
「アバン流殺法の基本を身につけた者は、剣・槍・弓・鞭・斧・拳でも発動が可能である」・・・と。
このアバン流殺法(力、速度、闘気の3つ)をコントロールし、三位一体となることで完成する強力な剣技である奥義をオリジナルアイテムの「アバンのしるし」にして作って、毎回アバン流殺法の基本をすべて習得し直さなくても
装備した人なら"誰"でも、"どんな武器"でも「アバンストラッシュ」が使えるようにしちゃうよ!って感じです。
<div id="yanyo"> <yanyo-component></yanyo-component> </div>
Vue.component('yanyo-component', { template: '<h1>jQueryからVue.jsへ移行事始めしてやんよ!!!</h1>' }) var yanyo = new Vue({ el: "#yanyo" })
var yanyoComponent = { template: '<h1>jQueryからVue.jsへ移行事始めしてやんよ!!!</h1>' } var yanyo = new Vue({ el: "#yanyo", components: { 'yanyo-component': yanyoComponent } })
コンポーネント名は好きに付けられる訳でなく、既に存在する(将来定義されるかもしれない)従来のHTMLのタグ名は単語で命名されている規則性と衝突させないためにVueコンポーネント用の命名規則が存在します。
<div id="yanyo"> <ul> <li v-for="member in members">{{ member }}:<vote-component></vote-component></li> </ul> </div>
Vue.component('vote-component', { data: function() { return {count:0} }, methods: { countUp: function(){ this.count++; }, countDown: function(){ this.count--; }, reset: function(){ this.count = 0 } }, template: ` <div> <button v-on:click="countUp">{{count}}票</button> <button v-on:click="countDown">取り消し</button> <button v-on:click="reset">リセット</button> </div> `, }) var yanyo = new Vue({ el: "#yanyo", data: { members: ["Aさん", "Bさん", "Cさん"] } })
これまでのディレクティブ機能と組み合わせてコンポーネント化するメリットの具体的なサンプルを作ります
v-on
を使ってクリックでカウントアップするボタンを作成v-on
を使ってクリックでカウント1を取り消すボタンを作成v-on
を使ってクリックでカウントをリセットするボタンを作成v-for
で展開するリストとmembers
配列を作成div
タグでまとめてtemplate
に指定してvote-component
と名付けたパーツ(コンポーネント)をv-for
の展開中に設置たったこれだけのコード量で3人のメンバーから学級委員長を選ぶ投票ツールが完成しました
投票機能はメンバーが増えても減ってもv-for
で展開される分しか出力されない&投票機能の各ボタンは個別のVueインスタンスで管理されているのでお互いのボタン操作は干渉しません
従来のjQueryでは「各ボタンを押したらどのメンバーに紐付けたボタンなのか?」や「現在のカウントはいくつか?そっからどう変化(+/-)するか?」という様なロジックで管理する必要があるため、この2〜3倍は記述しないといけないと思います
こうして見れば"再利用可能なパーツ"というイメージもコンポーネント管理できる事の効率化もハッキリわかるのではないでしょうか?
今回はjQueryからVue.jsへの移行を考えてる人の「ちょっと体験してみよっかな」の方を対象にしました。
各ディレクティブやオプションの機能だけならば、jQueryやプレーンのJavaScriptでも書き方によってはもっと綺麗にまとめられたり、コントロールできる部分も多いと思いますが、
今回自分でコンポーネント管理までを含めて初めてVue.jsを使う事のメリットを他人にプレゼンできるなという感想です
現段階では「computed
でできる事ってmethods
でできる事だからいらなくね?」といった初学者モヤモヤももちろん抱えてしまったので、今後もっと掘り下げて行こうと思います