変更履歴
- チュートリアル
- リソース
- 重要なコンセプト
- ソーシャル
- その他
v1.1.0
ニュース
- ES6クラスによるコンポーネントをサポート
- クロージャによるコンポーネントをサポート
バグ修正
- IE11 input[type] のエラーを修正 - #1610
- #1609をキーのない子供のケースに適用 to unkeyed children case
- abort検知を修正 #1612
- 古い値とほぼ一致している時の入力値のフォーカスの問題を修正 #1593
v1.0.1
ニュース
- IEでのパフォーマンス向上: #1598
バグ修正
v0.2.xからの移行
v1.x
はv0.2.x
と幅広くAPIの互換性がありますが、破壊的変更もいくつか行われています。
もし移行を考えている時はmithril-codemodsの使用を検討してください。このツールはほとんどのシンプルな移行を自動化するツールです。
m.prop
が削除されたm.component
が削除されたconfig
関数- 再描画時の動作の変更
- コンポーネントの
controller
関数 - コンポーネントの引数
view()
パラメータm()
にコンポーネントを渡すm.mount()
とm.route()
にVノードを渡すm.route.mode
m.route
とアンカータグ- 現在のラウトの読み込み/書き込み
- ラウトパラメータへのアクセス
- クエリー文字列の組み立てとパース
- アンマウントを停止させる
- コンポーネント削除時にコードを実行
m.request
m.deferred
が削除されたm.sync
が削除されたxlink
名前空間が必要になった- ビューのネストされた配列
vnode
同値性チェック
m.prop
が削除された
v1.x
では、m.prop()
はより強力でコンパクトなストリームとしてライブラリ化されました。Mithrilのコアからは外れました。オプションであるストリームモジュールの使用方法は、こちらのドキュメントで読むことができます。
v0.2.x
var m = require("mithril")
var num = m.prop(1)
v1.x
var m = require("mithril")
var prop = require("mithril/stream")
var num = prop(1)
var doubled = num.map(function(n) {return n * 2})
m.component
が削除された
v0.2.x
では、m(component)
かm.component(component)
のどちらをつかってもコンポーネントを作ることができました。v1.x
ではm(component)
だけがサポートされます。
v0.2.x
// 次の行は同じ
m.component(component)
m(component)
v1.x
m(component)
config
関数
v0.2.x
では、Mithrilはライフサイクルメソッドとしてconfig
だけを提供していました。v1.x
ではVノードのライフサイクルに対して適切な粒度の制御方法を提供しています。
v0.2.x
m("div", {
config : function(element, isInitialized) {
// 毎回の再描画時に呼ばれる
// isInitializedはブール値で、ノードがDOMに追加されるときにtrueになる
}
})
v1.x
これらの新しいメソッドについては、ライフサイクルメソッドのドキュメントで紹介されています。
m("div", {
// DOMノードが作成される前に呼ばれる
oninit : function(vnode) { /*...*/ },
// DOMノードが作成された後に呼ばれる
oncreate : function(vnode) { /*...*/ },
// DOMノードが更新前に呼ばれ、falseを返すと更新をキャンセルする
onbeforeupdate : function(vnode, old) { /*...*/ },
// ノードが更新された後に呼ばれる
onupdate : function(vnode) { /*...*/ },
// ノードが削除されたときに呼ばれる。DOMからノードを削除
// する準備が整ったときに解決されるPromiseを返す
onbeforeremove : function(vnode) { /*...*/ },
// ノードが削除される前で、onbeforeremoveが完了した後に呼ばれる。
onremove : function(vnode) { /*...*/ }
})
もしDOMエレメントにアクセス可能であれば、vnode.dom
プロパティを使って参照できます。
再描画時の動作の変更
Mithrilのレンダリングエンジンは基本的に半自動グローバル再描画を行いますが、いくつかのAPIと挙動が変わりました。
再描画のロックはなくなった
v0.2.xでは、Mithrilはデフォルトで一時的な描画ロジックのブロックを行っていました。m.request()
は実行時に描画ループをロックし、すべての大気中のリクエストが完了したときにアンロックしていました。同様のことは手動でm.startComputation()
とm.endComputation()
を呼び出すことで実現できました。これらのAPIと、関連する動作はv1.xで削除されました。再描画のロックはUIのバグを引き起こすことがありました。アプリケーションの一部がビューの一部の更新を停止することで、他の部分で変更が反映されないことがありました。
イベントハンドラから再描画をキャンセルする
m.mount()
とm.route()
は、引き続きDOMイベントハンドラの実行後に再描画を行います。イベントハンドラ内からこれらの再描画をキャンセルするには、イベントオブジェクトのredraw
プロパティにfalse
を設定してください。
v0.2.x
m("div", {
onclick : function(e) {
m.redraw.strategy("none")
}
})
v1.x
m("div", {
onclick : function(e) {
e.redraw = false
}
})
同期再描画の削除
v0.2.xでは、m.redraw()
にtrueを渡すことで即座にMithrilに再描画を行わせることができました。このm.redraw()
メソッドを使った時の挙動は難しく、追跡が難しい問題を引き起こすことがあったので削除されました。
v0.2.x
m.redraw(true) // 即座に再描画を行って同期する
v1.x
m.redraw() // 次のrequestAnimationFrame呼び出し時に再描画が行われるように予約する
m.startComputation
/m.endComputation
の削除
これらの関数はさまざまなエッジケースの問題を発生させてきたため、アンチパターンと判断されました。v1.xからは除外されました。
コンポーネントのcontroller
関数
v1.x
のコンポーネントにはcontroller
プロパティはありません。代わりにoninit
を使ってください。
v0.2.x
m.mount(document.body, {
controller : function() {
var ctrl = this
ctrl.fooga = 1
},
view : function(ctrl) {
return m("p", ctrl.fooga)
}
})
v1.x
m.mount(document.body, {
oninit : function(vnode) {
vnode.state.fooga = 1
},
view : function(vnode) {
return m("p", vnode.state.fooga)
}
})
// OR
m.mount(document.body, {
oninit : function(vnode) {
var state = this // thisはデフォルトでvnode.stateがバインドされています
state.fooga = 1
},
view : function(vnode) {
var state = this // thisはデフォルトでvnode.stateがバインドされています
return m("p", state.fooga)
}
})
コンポーネントの引数
v1.x
のコンポーネントはオブジェクトでなければなりません。String
、Number
、Boolean
は子供のテキストノードとして扱われます。コンポーネント内部では、引数はvnode.attrs
オブジェクトとして参照できます。
v0.2.x
var component = {
controller : function(options) {
// options.fooga === 1
},
view : function(ctrl, options) {
// options.fooga == 1
}
}
m("div", m.component(component, { fooga : 1 }))
v1.x
var component = {
oninit : function(vnode) {
// vnode.attrs.fooga === 1
},
view : function(vnode) {
// vnode.attrs.fooga == 1
}
}
m("div", m(component, { fooga : 1 }))
view()
パラメータ
v0.2.x
ではビュー関数はcontroller
インスタンスの参照と、オプションのコンポーネントの引数を受け取りました。v1.x
ではcontroller
と同じように、vnode
のみを受け取ります。
v0.2.x
m.mount(document.body, {
controller : function() {},
view : function(ctrl, options) {
// ...
}
})
v1.x
m.mount(document.body, {
oninit : function(vnode) {
// ...
},
view : function(vnode) {
// ctrlの代わりにvnode.stateを使います
// optionsの代わりにvnode.attrsを使います
}
})
m()
にコンポーネントを渡す
v0.2.x
では、コンポーネントをm()
の第二引数に直接渡すことができました。v1.x
では一貫性のために、m()
呼び出しでラップする必要があります。
v0.2.x
m("div", component)
v1.x
m("div", m(component))
m.mount()
とm.route()
にVノードを渡す
v0.2.x
では、ドキュメントには書かれてませんでしたが、m.mount(element, component)
はcomponentsの代わりにvnodesを第二引数として渡すことができました。また、m.route(element, defaultRoute, routes)
もroutes
の値としてvnodeを受け入れることができました。
v1.x
ではどちらの場合もコンポーネントが必要となりました。
v0.2.x
m.mount(element, m('i', 'hello'))
m.mount(element, m(Component, attrs))
m.route(element, '/', {
'/': m('b', 'bye')
})
v1.x
m.mount(element, {view: function () {return m('i', 'hello')}})
m.mount(element, {view: function () {return m(Component, attrs)}})
m.route(element, '/', {
'/': {view: function () {return m('b', 'bye')}}
})
m.route.mode
v0.2.x
では、"pathname"
、"hash"
、"search"
をm.route.mode
に設定することでラウティングモードを変更できました。v.1.x
ではm.route.prefix(prefix)
に変更されました。prefix
としては、#
、?
あるいは空文字列("pathname")が使用できます。新APIはハッシュバング(#!
)もサポートしており、これがデフォルトモードとなっています。このAPIはルート以外のパス名や、クエリーバング(?!
)などの任意の文字列が設定できます。
v0.2.x
m.route.mode = "pathname"
m.route.mode = "search"
v1.x
m.route.prefix("")
m.route.prefix("?")
m.route()
とアンカータグ
アンカータグのクリックをハンドリングしてMithrilラウターと一緒に扱う方法はv0.2.x
と似ていますが、新しいライフサイクルメソッドとAPIを使います。
v0.2.x
// リンクをクリックしたときに、ナビゲーションを行うのではなく、"/path"のラウターをロードする
m("a", {
href : "/path",
config : m.route
})
v1.x
// リンクをクリックしたときに、ナビゲーションを行うのではなく、"/path"のラウターをロードする
m("a", {
href : "/path",
oncreate : m.route.link
})
現在のラウトの読み込み/書き込み
v0.2.x
では現在のラウトに関することはすべてm.route()
メソッド経由で行っていました。v1.x
ではこれは2つの関数に分離されました。
v0.2.x
// 現在のラウトを取得
m.route()
// 新しいラウトを設定
m.route("/other/route")
v1.x
// 現在のラウトを取得
m.route.get()
// 新しいラウトを設定
m.route.set("/other/route")
ラウトパラメータへのアクセス
v0.2.x
ではラウトパラメータの読み込みはm.route.param()
を使って行っていました。このAPIはv1.x
でも利用可能ですが、これに加えて、ラウトパラメータはVノードのattrs
プロパティを通じて取得することができます。
v0.2.x
m.route(document.body, "/booga", {
"/:attr" : {
controller : function() {
m.route.param("attr") // "booga"
},
view : function() {
m.route.param("attr") // "booga"
}
}
})
v1.x
m.route(document.body, "/booga", {
"/:attr" : {
oninit : function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "booga"
},
view : function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "booga"
}
}
})
クエリー文字列の組み立てとパース
v0.2.x
ではm.route
にぶら下がっていた関数m.route.buildQueryString()
とm.route.parseQueryString()
を使っていました。v1.x
では、名前が変わってm
配下に移動しました。
v0.2.x
var qs = m.route.buildQueryString({ a : 1 });
var obj = m.route.parseQueryString("a=1");
v1.x
var qs = m.buildQueryString({ a : 1 });
var obj = m.parseQueryString("a=1");
アンマウントを停止させる
onunload
のe.preventDefault()
を使ってアンマウントを中断させることはもうできません。その代わりに、中断条件にマッチしたときはm.route.set
を呼んでください。
v0.2.x
var Component = {
controller: function() {
this.onunload = function(e) {
if (condition) e.preventDefault()
}
},
view: function() {
return m("a[href=/]", {config: m.route})
}
}
v1.x
var Component = {
view: function() {
return m("a", {onclick: function() {if (!condition) m.route.set("/")}})
}
}
コンポーネント削除時にコードを実行
コンポーネントは削除時にthis.onunload
を呼ばなくなりました。標準化されたライフサイクルフックのonremove
を使用します。
v0.2.x
var Component = {
controller: function() {
this.onunload = function(e) {
// ...
}
},
view: function() {
// ...
}
}
v1.x
var Component = {
onremove : function() {
// ...
}
view: function() {
// ...
}
}
m.request
m.requestが返すPromiseはm.prop()
ゲッター・セッターではなくなります。これに加えて、initialValue
、unwrapSuccess
、unwrapError
はオプションからはサポートされなくなります。
さらに、m.startComputation
/m.endComputation
セマンティクスはサポートされなくなります。これらはサポートされませんが、Promiseのチェーンが完了するときには、background:true
がセットされていない時は再描画が実行されます。
v0.2.x
var data = m.request({
method: "GET",
url: "https://api.github.com/",
initialValue: [],
})
setTimeout(function() {
console.log(data())
}, 1000)
v1.x
var data = []
m.request({
method: "GET",
url: "https://api.github.com/",
})
.then(function (responseBody) {
data = responseBody
})
setTimeout(function() {
console.log(data) // note: これはゲッター・セッターではありません
}, 1000)
これに加えて、extract
オプションがm.request
に渡されると、指定された関数の返り値がPromiseの解決にそのまま使われ、deserialize
コールバックは無視されます。
m.deferred
が削除された
v0.2.x
では、m.deferred
という名前でMithril自身が提供する非同期の契約オブジェクトが使われてきました。また、これはm.request
からも使用されていました。v1.x
ではこれの代わりにPromiseが使われます。非サポートの環境のためにpolyfillを使った実装になっています。m.deferred
を使用していたところではPromiseを使用してください。
v0.2.x
var greetAsync = function() {
var deferred = m.deferred()
setTimeout(function() {
deferred.resolve("hello")
}, 1000)
return deferred.promise
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) // 1秒後に "hello world" のログ出力
v1.x
var greetAsync = function() {
return new Promise(function(resolve){
setTimeout(function() {
resolve("hello")
}, 1000)
})
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) // 1秒後に "hello world" のログ出力
m.sync
が削除された
v1.x
では標準準拠のPromiseが使われるようになったため、m.sync
は削除されました。Promise.all
を使用してください。
v0.2.x
m.sync([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("コントリビュータ:", users[0].name, "and", users[1].name)
})
v1.x
Promise.all([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("コントリビュータ:", users[0].name, "and", users[1].name)
})
xlink
名前空間が必要になった
v0.2.x
では、xlink
名前空間は属性の名前空間として唯一サポートされていて、特別な場合だけサポートしていました。現在は名前空間のパースは完全にサポートされました。名前空間の属性は、その名前空間の中で明示的に定義されなければなりません。
v0.2.x
m("svg",
// `href`属性は自動で名前空間が付与される
m("image[href='image.gif']")
)
v1.x
m("svg",
// `href`属性にユーザー指定の名前空間を付与する
m("image[xlink:href='image.gif']")
)
ビューのネストされた配列
配列はフラグメントを表現するのに使われるようになりました。これはv1.xの仮想DOMの主要な構造です。v0.2.xではネストされた配列は差分検知のためにフラット化され、1つのリストにされていました。v1.xでは配列構造をそのまま維持します。別の配列の子要素が、京大の要素として扱われることはありません。
vnode
同値性チェック
もしvnodeが前回の描画時のものと厳格に一致していたときは、v1.xはその部分のサブツリーのチェックや、ライフサイクルメソッドの呼び出しをスキップします。コンポーネントのドキュメントにこの問題に関する詳細情報が含まれています。
License: MIT. © Leo Horie.