Vue.js v0.11の変更点(予定)まとめ
2014/10/07 @koba04
Vue.js v0.11のrc版もリリースされて、v0.10からの変更点が多いのでchangesを参考にまとめてみました。
** rc3がリリースされたので修正・追記しました **
APIの変更も多いですが、data継承の仕組みが完全に変わっているのでその辺りは注意が必要ですね。
npm install vue@0.11.0-rc2まだ安定してなかったりドキュメントはv0.10のものしかなくてchangesとmergeしながら読む必要があったりするので、これから開発する人は今のタイミングはどのバージョンを使えばいいのかちょっと悩ましいですね。v0.11系を使っていった方がいいとは思いつつ。
今回の変更でも見えるのですが、Angular.js以外にもBackbone.jsやReact.jsなど様々なフレームワークからいいところを持ってきてるところがVue.jsの面白いところですね。
https://github.com/yyx990803/vue/blob/0.11.0-rc3/changes.md
Instantiation process
elオプションがインスタンス化する際に指定されていなかった場合、以前は空のdivを作成していましたが"unmounted"な状態となり新しく追加された$mountメソッドにquerySelectorを渡すことでViewと紐付けるようになりました。
var vm = new Vue({ data: {a:1} }) // only observes the data
vm.$mount('#app') // actually compile the DOM
// in comparison, this will compile instantly just like before.
var vm = new Vue({ el: '#app', data: {a: 1} })
$mount()を引数なしで呼ぶと空の<div>が作成されます。
New Scope Inheritance Model
以前のバージョンではprototypeなデータ継承の仕組みを持っていませんでした。にも関わらずthis.$parentやthis.$getを使って親scopeの値を参照することが出来ました。
新しいバージョンでは、Angular.jsに似た継承システムを持っていて、直接親scopeの値を参照することが出来ます。 大きな違いは子scopeで値を設定するとそれは親scopeにも影響することです。
この例がわかりやすいです。 http://jsfiddle.net/Px2n6/2/
デフォルトではtemplate内で入れ子にしても親scopeは継承しません。これは意図せず親scopeの値を書き換えないようにするためです。
もし親scopeを継承したい場合はinherit: trueオプションをつける必要があります。
v-repeatとv-ifは親scopeをデフォルトで継承します。
Instance Option changes
Vue.extend内でelとdataを使用する場合は関数定義にする必要があります。
var MyComponent = Vue.extend({
el: function () {
var el = document.createElement('p')
// you can initialize your element here.
// you can even return a documentFragment to create
// a block instance.
el.className = 'content'
return el
},
data: function () {
// similar to ReactComponent.getInitialState
return {
a: {
b: 123
}
}
}
})
eventsオプション追加されました
Backbone.jsのeventsみたいな感じですね。
$emitで発行する独自イベント以外にもhook:createdのようなライフサイクルイベントについても定義することが出来ます。
var vm = new Vue({
events: {
'hook:created': function () {
console.log('created!')
},
greeting: function (msg) {
console.log(msg)
},
// can also use a string for methods
bye: 'sayGoodbye'
},
methods: {
sayGoodbye: function () {
console.log('goodbye!')
}
}
})
// -> created!
vm.$emit('greeting', 'hi!')
// -> hi!
vm.$emit('bye')
// -> goodbye!
watchオプションが追加されました
eventsのようにwatchしたい対象の評価式とコールバックをオブジェクトの形式で定義することが出来ます。
わかりやすく書けるようになっていいですね。
inheritオプションが追加されました(デフォルトはfalse)
親scopeのdataを継承するかどうかの設定です。
継承することで
- 親scopeの値をtemplateで参照することが出来るようになります
- 親scopeの値をインスタンスから直接アクセス出来るようになります
mixinオプションが追加されました
いわゆるmixinってやつです。
var mixin = {
created: function () { console.log(2) }
}
var vm = new Vue({
created: function () { console.log(1) },
mixins: [mixin]
})
// -> 1
// -> 2
nameオプションが追加されました
デバッグしやすさのために名前をつけることが出来るようになりました。
var SubClass = Vue.extend({
name: 'MyComponent'
})
var instance = new SubClass()
console.log(instance) // -> MyComponent { $el: ... }
parentオプションが削除されました
かわりに$addChildを使うことが出来ます。
var child = parent.$addChild(options, [contructor])
lazyが削除されました
ViewModelに指定するのではなくて、v-model毎に設定すべきだからということでv-modelのoptionになりました。
id、tagName、className、attributesも削除されました
代わりにelに関数定義して指定するようにします
createdのhookの挙動が変更されました
データバインディングされた後に呼ばれるにようになったので、dataを追加する場合は$addや$removeを使わないとデータバインディングの対象にならなくなりました。
readyのhookの挙動が変更されました
documentに初めて追加されるときだけに呼ばれるようになりました。これまでと同じように使いたい場合はcompiledを使ってください。
beforeCompileのhookが追加されました
インスタンス化されてDOMのcompileが開始される前に呼ばれます。
compiledのhookが追加されました
これまでのreadyのタイミングで呼ばれて、初期のデータでのcompileが終了したタイミングで呼ばれます。
afterDestroyのhookがdestroyに変更されました
Instance methods change
$watchに評価式を渡せるようになりました
vm.$watch('a + b', function (newVal, oldVal) {
// do something
})
$watchでのdeep watchの挙動が変わりました
デフォルトではwatchに渡した値に対する変更しか監視しなくなったので、ネストしたオブジェクトの評価をしたい場合は、第三引数にtrueを渡す必要があります。
vm.$watch('someObject', callback, true)
vm.someObject.nestedValue = 123
// callback is fired
$watchの即時実行
第四引数にtrueを渡すことで初回に値をセットするときにもcallbackを実行させることが出来ます。
vm.$watch('a', callback, false, true)
// callback is fired immediately with current value of `a`
この辺、deepWatchと合わせて第三引数をoptionsなオブジェクトにしたほうがいいと思う。
$unwatchは削除されて、$watchの戻り値である関数を呼ぶことでunwatchされます
var unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()
vm.$getに評価式を渡せるようになりました
var value = vm.$get('a + b')
vm.$addとvm.$deleteが追加されました
ViewModelのプロパティを追加・削除するときに使います。 まぁでも、インスタンス化する際に全てのプロパティをnullなどで設定しておく方がいいです。
vm.$evalが追加されました
filterも適用することが出来ます。
var value = vm.$eval('msg | uppercase')
vm.$interpolateが追加されました
template文字列を評価することが出来ます。
var markup = vm.$interpolate('<p>{{msg | uppercase}}</p>')
vm.$logが追加されました
インスタンスのdataを生のオブジェクトとしてみることが出来ます(getter/setterなし)。
vm.$log() // logs entire ViewModel data
vm.$log('item') // logs vm.item
vm.$compileが追加されました
DOMをcompileすることが出来て、戻り値としてteardownするときに使うdecompileする関数を返します。 decompile関数ではDOMは削除されません。 主にカスタムdirectiveを書く人のためのメソッドです。
Computed Properties API Change
$get、$setはget、setになりました
computed: {
fullName: {
get: function () {},
set: function () {}
}
}
Directive API change
directiveに動的な値を指定出来るようになりました
こんな感じでv-viewみたいなことが出来るようになりました
{% raw %}
<div v-component="{{test}}"></div>
{% endraw %}
今サポートしているのはv-componentだけで、独自directiveを作る時はupdate関数を実装することでハンドリング出来ます。
v-modelにlazy属性とnumber属性が追加されました
lazyはこれまでインスタンスオプションにあった、enterキー押したときかフォーカス外れた時だけにchangeイベントが発行されるものがv-modelの属性になりました。
numberはmodelに反映されるときにNumber型にすることが出来ます。
select要素にv-modelとしてtextとvalueを含んだオブジェクトの配列を渡すとoption要素として評価してくれます
[
{ text: 'A', value: 'a' },
{ text: 'B', value: 'b' }
]
<select>
<option value="a">A</option>
<option value="b">B</option>
</select>
select要素にv-modelとしてlabelとoptionsを含んだオブジェクトの配列を渡すとoptgroup要素として評価してくれます
[
{ label: 'A', options: ['a', 'b']},
{ label: 'B', options: ['c', 'd']}
]
<select>
<optgroup label="A">
<option value="a">a</option>
<option value="b">b</option>
</optgroup>
<optgroup label="B">
<option value="c">c</option>
<option value="d">d</option>
</optgroup>
</select>
v-componentにkeep-alive属性を指定するとインスタンスを破棄せずにキャッシュしておいてくれるようになります
Viewの切り替えをv-componentで行うときに使うとよさそうです。使い方間違うとリークしそうですが...。
v-repeatにtrackbyを指定することで、配列の値を再利用することが出来るようになりました
配列のdataにAPIのレスポンスなどを適用してswapされた場合など、今までは全部の要素を作りなおしていたのですがtrackbyを指定することで既存の値は再利用してくれるようになりました。React.jsのkeyみたいな感じ。
items: [
{ _id: 1, ... },
{ _id: 2, ... },
{ _id: 3, ... }
]
<li v-repeat="items" trackby="_id">...</li>
v-withで親と子のインスタンスの間で2wayバインディングされないようになりました
v-withで作られた子のインスタンスの値を変更しても親には反映されなくなります。親のインスタンスの変更は子に反映されます。
v-elが追加されました
v-refで似た感じですが、こちらはvm.$$.xxxとすることでDOM Nodeを参照することが出来ます。
twoWayのオプションが追加されました
このオプションはdirectiveが2wayデータバインディングをするかどうかを指定します。
これを指定することでthis.set(value)をdirectiveの内部で使用することが出来ます。
- ちょっとどういう使われ方するのかよくわかってないです...
acceptStatementのオプションが追加されました
このオプションはdirectiveがv-onのようにインラインステートメントを受け付けるかどうかを指定します。
<a v-on="click: a++"></a>
指定したステートメントは関数としてラップされてdirectiveのupdate関数に渡されます。
isEmptyとisFnオプションが削除されました
Interpolation change
textのバインディング自動的にstringifyしなくなりました。
jsonfilterを使いましょう。
One time interpolationsが指定出来るようになりました
変更されない値に指定することでrenderingのパフォーマンスを向上させることが出来ます。
{% raw %}
<span>{{* hello }}</span>
{% endraw %}
Config API change
Vue.configがメソッド形式からpropertyアクセスに変更されました
// old
// Vue.config('debug', true)
// new
Vue.config.debug = true
config.prefixの値にハイフンが必須になりました
Vue.config.prefix = "data-"
config.delimitersが少し柔軟に指定出来るようになりました
これまでは['{','}']というような指定しか出来なかったのが['(%', '%)']という指定も出来るようになりました。
Vue.config.delimiters = ['(%', '%)']
// tags now are (% %) for text
// and ((% %)) for HTML
'proto'optionをfalseにすることでArrayの__proto__の書き換えを禁止することが出来ます
NativeのArrayのsubclassなどを作っている場合で、__proto__の書き換えされると困る場合にfalseにすることで__proto__の書き換えがされなくなります
(配列のオブジェクトに追加される)

またrc2からオブジェクトの場合に__proto__の書き換えがされることはなくなりました。ただObject.prototypeに$addと$delete、Array.prototypeに$removeと$setが追加されています。
dataに生のオブジェクトを使っている場合には影響無いですが、Constructorから作ったオブジェクトを使っている場合にはprototypeが残るようになります。
var Hoge = function () {
this.name = "foo";
};
Hoge.prototype.foo = function() { console.log(this.name) };

async optionをfalseにすることで即時にDOMが更新することが出来ます
通常は、batch方式によってDOMの更新はまとめて行われるのですが、このオプションをfalseにすることで即時にDOMに反映することが出来るようになります。
当初は「trueにすると」と書いていたので修正しました(2015/02/26)
Transition API change
v-transitionとv-animationとv-effectの違いはなくなりました
どれかに統一されるのかな?
Vue.configでenter/leaveの指定が設定出来なくなりました
Vue.effectはVue.transitionに変更されました。effectsオプションもtransitionsに変更されました。
v-transition="my-transition"とした場合、
Vue.transition(id, def)で登録されているオブジェクトまたは、transitionsオプションを"my-transition"をkeyとして探します。- 上記で見つからなかった場合、CSS transitionsまたはCSS animationsを適用します。
- 上記でもアニメーションしなかった場合、DOM操作を即時に行われます。
JavaScript transitionsのAPIがAngular.jsっぽく変更されました
Vue.transition('fade', {
beforeEnter: function (el) {
// a synchronous function called right before the
// element is inserted into the document.
// you can do some pre-styling here to avoid FOC.
},
enter: function (el, done) {
// element is already inserted into the DOM
// call done when animation finishes.
$(el)
.css('opacity', 0)
.animate({ opacity: 1 }, 1000, done)
// optionally return a "cancel" function
// to clean up if the animation is cancelled
return function () {
$(el).stop()
}
},
leave: function (el, done) {
// same as enter
$(el)
.animate({ opacity: 0 }, 1000, done)
return function () {
$(el).stop()
}
}
})
Events API change
$dispatchとbroadcastで発行されるイベントのコールバックでfalseを返すと、伝播を止めることが出来るようになりました。
var a = new Vue()
var b = new Vue({
parent: a
})
var c = new Vue({
parent: b
})
a.$on('test', function () {
console.log('a')
})
b.$on('test', function () {
console.log('b')
return false
})
c.$on('test', function () {
console.log('c')
})
c.$dispatch('test')
// -> 'c'
// -> 'b'
Two Way filters
filterに関数を渡すとreadのfilterとして扱われますが、v-modelのような2wayバインディングのdirectiveと組み合わせることでwriteのfilterも定義出来るようになりました
Vue.filter('format', {
read: function (val) {
return val + '!'
},
write: function (val, oldVal) {
return val.match(/ok/) ? val : oldVal
}
})
Block logic control
template要素を制御ブロックとして扱うことが出来るようになりました
items: [
{
title: 'title-1',
subtitle: 'subtitle-1',
content: 'content-1'
},
{
title: 'title-2',
subtitle: 'subtitle-2',
content: 'content-2'
}
]
<template v-repeat="item:items">
<h2>{{item.title}}</h2>
<p>{{item.subtitle}}</p>
<p>{{item.content}}</p>
</template>
<!--v-block-start-->
<h2>title-1</h2>
<p>subtitle-1</p>
<p>content-1</p>
<!--v-block-end-->
<!--v-block-start-->
<h2>title-2</h2>
<p>subtitle-2</p>
<p>content-2</p>
<!--v-block-end-->
v-partialにtemplateと一緒に使うことが出来ますし、下記のようにすることでpartialを動的に選択することが出来ます
<template v-partial="{{partialId}}"></template>
Misc
$destroy()はdefaultだと$elはそのまま残すので、$elも削除したい場合は$destroy(true)としてください
v-modelと一緒にvalue属性を指定するとvmの値を上書きして初期値として設定されます
vue.js bookもv0.11対応して続きを書かないと....