Migracja z wersji 0.2.x
Wersje v1.x i v2.x są w dużej mierze kompatybilne z API v0.2.x, jednak wprowadzono pewne zmiany powodujące brak kompatybilności wstecznej. Migracja do v2.x jest niemal identyczna z migracją do v1.x, dlatego poniższe uwagi odnoszą się do obu wersji.
Jeśli planujesz migrację, rozważ użycie narzędzia mithril-codemods, które może zautomatyzować najprostsze zadania związane z migracją.
Usunięto m.prop
W v2.x funkcja m.prop()
została zastąpiona bardziej zaawansowaną mikro-biblioteką strumieni (stream), która jednak nie jest już częścią rdzenia biblioteki. Informacje na temat korzystania z opcjonalnego modułu Streams można znaleźć w dokumentacji.
v0.2.x
var m = require('mithril');
var num = m.prop(1);
v2.x
var m = require('mithril');
var prop = require('mithril/stream');
var num = prop(1);
var doubled = num.map(function (n) {
return n * 2;
});
Usunięto m.component
W v0.2.x komponenty mogły być tworzone za pomocą m(Component)
lub m.component(Component)
. W v2.x obsługiwane jest tylko m(Component)
.
v0.2.x
// Te zapisy są równoważne
m.component(Component);
m(Component);
v2.x
m(Component);
Usunięto m.withAttr
W v0.2.x obsługi zdarzeń mogły używać oninput: m.withAttr("value", func)
i podobnych konstrukcji. W v2.x należy odczytywać wartości bezpośrednio z elementu docelowego zdarzenia. Funkcja m.withAttr
dobrze współgrała z m.prop
, ale została usunięta na rzecz rozwiązania spoza rdzenia. Ponieważ w v1.x nie zaobserwowano podobnego, szerokiego i idiomatycznego użycia strumieni, m.withAttr
straciło większość swojej użyteczności.
v0.2.x
var value = m.prop('');
// W twoim widoku
m('input[type=text]', {
value: value(),
oninput: m.withAttr('value', value),
});
v2.x
var value = '';
// W twoim widoku
m('input[type=text]', {
value: value,
oninput: function (ev) {
value = ev.target.value;
},
});
Usunięto m.version
Ogólnie rzecz biorąc, funkcja ta miała niewielkie zastosowanie. Zawsze możesz ją dodać samodzielnie. Zaleca się wykrywanie funkcjonalności, aby określić, jakie funkcje są dostępne. API v2.x zostało zaprojektowane tak, aby to ułatwić.
Funkcje cyklu życia komponentu (dawniej config
)
W v0.2.x Mithril.js udostępniał pojedynczą metodę cyklu życia, config
. W v2.x zapewnia znacznie bardziej szczegółową kontrolę nad cyklem życia vnode (węzła wirtualnego DOM).
v0.2.x
m('div', {
config: function (element, isInitialized) {
// uruchamia się przy każdym odświeżeniu
// isInitialized to wartość boolean, która reprezentuje, czy węzeł został dodany do DOM
},
});
v2.x
Szczegółowe informacje na temat nowych metod cyklu życia można znaleźć w lifecycle-methods.md.
m('div', {
// Wywoływane przed utworzeniem węzła DOM
oninit: function (vnode) {
/*...*/
},
// Wywoływane po utworzeniu węzła DOM
oncreate: function (vnode) {
/*...*/
},
// Wywoływane przed aktualizacją węzła, zwraca false, aby anulować aktualizację
onbeforeupdate: function (vnode, old) {
/*...*/
},
// Wywoływane po aktualizacji węzła
onupdate: function (vnode) {
/*...*/
},
// Wywoływane przed usunięciem węzła, zwraca Promise, który jest resolved, gdy
// węzeł jest gotowy do usunięcia z DOM
onbeforeremove: function (vnode) {
/*...*/
},
// Wywoływane po wywołaniu onbeforeremove i jego zakończeniu (Promise resolved), tuż przed usunięciem węzła
onremove: function (vnode) {
/*...*/
},
});
Jeśli jest dostępny, element DOM vnode można uzyskać poprzez właściwość vnode.dom
.
Zmiany w zachowaniu odświeżania (redraw behaviour)
Silnik renderowania Mithril.js nadal działa w oparciu o półautomatyczne globalne odświeżania, ale niektóre API i zachowania uległy zmianie:
Brak blokad odświeżania (redraw locks)
W v0.2.x Mithril.js pozwalał na "blokady odświeżania", które tymczasowo uniemożliwiały wykonywanie logiki rysowania: domyślnie, m.request
blokował pętlę renderowania podczas wykonywania i odblokowywał ją po zakończeniu wszystkich oczekujących żądań. To samo zachowanie można było wywołać ręcznie za pomocą m.startComputation()
i m.endComputation()
. Te API i powiązane zachowanie zostały usunięte w v2.x bez zamiennika. Blokowanie odświeżania może prowadzić do nieprawidłowego działania interfejsu użytkownika: problemy w jednej części aplikacji nie powinny uniemożliwiać aktualizacji innych części widoku i odzwierciedlania zmian.
Anulowanie odświeżania (redraw) z obsługi zdarzeń (event handlers)
m.mount()
i m.route()
nadal automatycznie odświeżają widok po uruchomieniu obsługi zdarzeń DOM. Anulowanie tych odświeżeń w obsłudze zdarzeń odbywa się teraz poprzez ustawienie właściwości redraw
na false
w obiekcie zdarzenia.
v0.2.x
m('div', {
onclick: function (e) {
m.redraw.strategy('none');
},
});
v2.x
m('div', {
onclick: function (e) {
e.redraw = false;
},
});
Zmieniono synchroniczne odświeżanie (synchronous redraw)
W v0.2.x można było wymusić na Mithril.js natychmiastowe odświeżenie poprzez przekazanie wartości true
do m.redraw()
. W v2.x ta funkcjonalność została podzielona na dwie różne metody dla większej przejrzystości.
v0.2.x
m.redraw(true); // odświeża natychmiast i synchronicznie
v2.x
m.redraw(); // planuje odświeżenie na następny tick requestAnimationFrame
m.redraw.sync(); // wywołuje odświeżenie natychmiast i czeka na jego zakończenie
Usunięto m.startComputation
/m.endComputation
Uznano je za złą praktykę i miały szereg problematycznych przypadków brzegowych, dlatego zostały usunięte w v2.x bez zamiennika.
Funkcja controller
komponentu
W v2.x właściwość controller
w komponentach została usunięta - zamiast niej należy używać oninit
.
v0.2.x
m.mount(document.body, {
controller: function () {
var ctrl = this;
ctrl.fooga = 1;
},
view: function (ctrl) {
return m('p', ctrl.fooga);
},
});
v2.x
m.mount(document.body, {
oninit: function (vnode) {
vnode.state.fooga = 1;
},
view: function (vnode) {
return m('p', vnode.state.fooga);
},
});
// LUB
m.mount(document.body, {
// `this` domyślnie wskazuje na vnode.state
oninit: function (vnode) {
this.fooga = 1;
},
view: function (vnode) {
return m('p', this.fooga);
},
});
Argumenty komponentu
W v2.x argumenty przekazywane do komponentu muszą być obiektem. Proste wartości, takie jak String
/Number
/Boolean
, będą traktowane jako tekstowe elementy potomne (text children). Dostęp do argumentów w komponencie uzyskuje się poprzez odczytywanie ich z obiektu 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 }));
v2.x
var Component = {
oninit: function (vnode) {
// vnode.attrs.fooga === 1
},
view: function (vnode) {
// vnode.attrs.fooga === 1
},
};
m('div', m(Component, { fooga: 1 }));
Elementy potomne vnode komponentu
W v2.x elementy potomne vnode komponentu są przekazywane przez vnode.children
jako przetworzona tablica elementów potomnych. Podobnie jak w v0.2.x, same elementy potomne nie są normalizowane, a tablica elementów potomnych nie jest spłaszczana.
v0.2.x
var Component = {
controller: function (value, renderProp) {
// value === "value"
// typeof renderProp === "function"
},
view: function (ctrl, value, renderProp) {
// value === "value"
// typeof renderProp === "function"
},
};
m(
'div',
m.component(Component, 'value', function (key) {
return 'child';
})
);
v2.x
var Component = {
oninit: function (vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
view: function (vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
};
m(
'div',
m(Component, 'value', function (key) {
return 'child';
})
);
Elementy potomne vnode DOM
W v0.2.x elementy potomne węzłów DOM były reprezentowane dosłownie, bez normalizacji, chyba że obecny był tylko jeden element potomny w tablicy - wtedy używano go bezpośrednio. Powodowało to powstawanie struktury bardziej zbliżonej do tej, z łańcuchami znaków reprezentowanymi dosłownie.
m("div", "value", ["nested"])
// Staje się:
{
tag: "div",
attrs: {},
children: [
"value",
["nested"],
]
}
W v2.x elementy potomne vnodes DOM są normalizowane do obiektów o jednolitej strukturze.
m("div", "value", ["nested"])
// Staje się z grubsza:
{
tag: "div",
attrs: null,
children: [
{tag: "#", children: "value"},
{tag: "[", children: [
{tag: "#", children: "nested"},
]},
]
}
Jeśli na vnode DOM znajduje się tylko jeden tekstowy element potomny, zamiast tego ustawia text
na tę wartość.
m("div", "value")
// Staje się z grubsza:
{
tag: "div",
attrs: null,
text: "",
children: undefined,
}
Szczegółowe informacje na temat struktury vnode v2.x i sposobu normalizacji można znaleźć w dokumentacji vnode.
Większość właściwości vnode v2.x jest tutaj pominięta dla zwięzłości.
Klucze (Keys)
W v0.2.x można było swobodnie mieszać vnodes z kluczami oraz bez kluczy.
W v2.x listy elementów potomnych zarówno fragmentów, jak i elementów muszą być albo wszystkie z kluczami, albo wszystkie bez kluczy. Puste miejsca są również uważane za elementy bez kluczy do celów tego sprawdzenia - nie są już ignorowane.
Jeśli musisz to obejść, użyj fragmentu zawierającego pojedynczy vnode.
Parametry view()
W v0.2.x funkcje widoku otrzymywały odniesienie do instancji controller
i (opcjonalnie) wszelkie opcje przekazane do komponentu. W v2.x otrzymują tylko vnode
, dokładnie tak samo jak funkcja oninit
.
v0.2.x
m.mount(document.body, {
controller: function () {},
view: function (ctrl, options) {
// ...
},
});
v2.x
m.mount(document.body, {
oninit: function (vnode) {
// ...
},
view: function (vnode) {
// Użyj vnode.state zamiast ctrl
// Użyj vnode.attrs zamiast options
},
});
Przekazywanie komponentów do m()
W v2.x, dla zachowania spójności, komponenty muszą być zawsze opakowane wywołaniem m()
.
v0.2.x
m('div', Component);
v2.x
m('div', m(Component));
Przekazywanie vnodes do m.mount()
i m.route()
W v0.2.x, m.mount(element, component)
tolerował vnodes jako drugi argument zamiast komponentów (mimo że nie było to udokumentowane). Podobnie, m.route(element, defaultRoute, routes)
akceptował vnodes jako wartości w obiekcie routes
.
W v2.x w obu przypadkach wymagane jest użycie komponentów.
v0.2.x
m.mount(element, m('i', 'hello'));
m.mount(element, m(Component, attrs));
m.route(element, '/', {
'/': m('b', 'bye'),
});
v2.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
W wersji 0.2.x można było ustawić tryb routingu, przypisując ciąg znaków "pathname"
, "hash"
lub "search"
do m.route.mode
. W wersji v.1.x
zostało to zastąpione przez m.route.prefix = prefix
, gdzie prefix
może być dowolnym prefiksem. Jeśli zaczyna się od #
, działa w trybie "hash"; jeśli od ?
, w trybie "search"; w przeciwnym razie (dowolny inny znak lub pusty ciąg znaków) w trybie "pathname". Obsługiwane są również kombinacje powyższych, takie jak m.route.prefix = "/path/#!"
lub ?#
.
Domyślne ustawienie zostało zmienione, aby używać prefiksu #!
(hashbang) zamiast tylko #
. Jeśli więc używałeś domyślnego zachowania i chcesz zachować istniejące adresy URL, ustaw m.route.prefix = "#"
przed zainicjowaniem tras.
v0.2.x
m.route.mode = 'hash';
m.route.mode = 'pathname';
m.route.mode = 'search';
v2.x
// Bezpośrednie odpowiedniki
m.route.prefix = '#';
m.route.prefix = '';
m.route.prefix = '?';
m.route()
i kotwice
Do obsługi linków z routingiem używany jest teraz specjalny, wbudowany komponent, zamiast atrybutu. Jeśli używałeś tego na elementach <button>
i tym podobnych, możesz określić nazwę tego tagu za pomocą atrybutu selector: "button"
.
v0.2.x
// Po kliknięciu ten link załaduje trasę "/path" zamiast nawigować
m('a', {
href: '/path',
config: m.route,
});
v2.x
// Po kliknięciu ten link załaduje trasę "/path" zamiast nawigować
m(m.route.Link, {
href: '/path',
});
Szablony tras
W wersji 1.x istniały trzy oddzielne składnie szablonów ścieżek, które, choć podobne, miały 2 oddzielnie zaprojektowane składnie i 3 różne implementacje. Zostało to zdefiniowane w sposób doraźny, a parametry zazwyczaj nie były eskejpowane. Teraz, jeśli użyjesz :key
, wszystko zostanie zakodowane, a jeśli :key...
, pozostanie surowe. Jeśli coś jest kodowane w sposób nieoczekiwany, użyj :path...
. To proste.
Konkretnie, oto jak wpływa to na każdą metodę:
Adresy URL m.request
W wersji 2.x, składniki ścieżki są automatycznie eskejpowane podczas interpolacji, a ich wartości odczytywane są z params
. W wersji 0.2.x, m.request({url: "/user/:name/photos/:id", data: {name: "a/b", id: "c/d"}})
wysłałoby żądanie z adresem URL ustawionym na /user/a%2Fb/photos/c/d
. W wersji 2.x, odpowiadające m.request({url: "/user/:name/photos/:id", params: {name: "a/b", id: "c/d"}})
wysłałoby żądanie do /user/a%2Fb/photos/c%2Fd
. Jeśli celowo chcesz interpolować klucz bez eskejpownia, użyj :key...
.
W wersji 2.x interpolacje w wbudowanych ciągach zapytania, takich jak /api/search?q=:query
, nie są wykonywane. Przekaż je przez params
z odpowiednimi nazwami kluczy, bez określania ich w ciągu zapytania.
Zauważ, że dotyczy to również m.jsonp
. Podczas migracji z m.request
+ dataType: "jsonp"
do m.jsonp
, musisz również o tym pamiętać.
Ścieżki m.route(route, params, shouldReplaceHistoryEntry)
Umożliwiają teraz interpolacje i działają identycznie jak w przypadku m.request
.
Wzorce tras m.route
Klucze ścieżki w postaci :key...
zwracały swój adres URL zdekodowany w wersji 1.x, ale zwracają surowy adres URL w wersji 2.x.
Wcześniej, elementy takie jak :key.md
były błędnie akceptowane, a wartość wynikowego parametru była ustawiana na keymd: "..."
. Tak już nie jest - .md
jest teraz częścią wzorca, a nie nazwą.
Odczyt/zapis bieżącej trasy
W wersji 0.2.x cała interakcja z bieżącą trasą odbywała się za pomocą m.route()
. W wersji 2.x zostało to podzielone na dwie funkcje.
v0.2.x
// Pobieranie bieżącej trasy
m.route();
// Ustawianie nowej trasy
m.route('/other/route');
v2.x
// Pobieranie bieżącej trasy
m.route.get();
// Ustawianie nowej trasy
m.route.set('/other/route');
Dostęp do parametrów trasy
W wersji 0.2.x odczytywanie parametrów trasy było w całości obsługiwane przez m.route.param()
. To API jest nadal dostępne w wersji 2.x. Dodatkowo, wszystkie parametry trasy są przekazywane jako właściwości w obiekcie attrs
węzła wirtualnego (vnode).
v0.2.x
m.route(document.body, '/booga', {
'/:attr': {
controller: function () {
m.route.param('attr'); // "booga"
},
view: function () {
m.route.param('attr'); // "booga"
},
},
});
v2.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"
},
},
});
Budowanie/Parsowanie ciągów zapytań
W wersji 0.2.x używano metod m.route.buildQueryString()
i m.route.parseQueryString()
, które były częścią obiektu m.route
. W wersji 2.x zostały one rozdzielone i przeniesione do korzenia m
.
v0.2.x
var qs = m.route.buildQueryString({ a: 1 });
var obj = m.route.parseQueryString('a=1');
v2.x
var qs = m.buildQueryString({ a: 1 });
var obj = m.parseQueryString('a=1');
Ponadto, w wersji 2.x, {key: undefined}
jest serializowane jako key=undefined
przez m.buildQueryString
i metody, które go używają, takie jak m.request
. W wersji 0.2.x klucz został pominięty i przeniesiono to do m.request
. Jeśli wcześniej na tym polegałeś, zmień swój kod, aby całkowicie pominąć klucze z obiektu. Jeśli nie możesz łatwo tego zrobić i musisz zachować zachowanie z wersji 0.2.x, rozważ użycie prostego narzędzia do usuwania wszystkich kluczy z obiektu, których wartości są undefined
.
// Wywołaj, gdy musisz pominąć parametry `undefined` z obiektu.
function omitUndefineds(object) {
var result = {};
for (var key in object) {
if ({}.hasOwnProperty.call(object, key)) {
var value = object[key];
if (Array.isArray(value)) {
result[key] = value.map(omitUndefineds);
} else if (value != null && typeof value === 'object') {
result[key] = omitUndefineds(value);
} else if (value !== undefined) {
result[key] = value;
}
}
}
return result;
}
Zapobieganie usuwaniu
Zapobieganie odmontowywaniu za pomocą e.preventDefault()
w funkcji onunload
nie jest już możliwe. Zamiast tego powinieneś jawnie wywołać m.route.set
, gdy zostaną spełnione oczekiwane warunki.
v0.2.x
var Component = {
controller: function () {
this.onunload = function (e) {
if (condition) e.preventDefault();
};
},
view: function () {
return m('a[href=/]', { config: m.route });
},
};
v2.x
var Component = {
view: function () {
return m('a', {
onclick: function () {
if (!condition) m.route.set('/');
},
});
},
};
Wykonywanie kodu przy usuwaniu komponentu
Gdy komponenty są usuwane, funkcja this.onunload
nie jest już wywoływana. Używają teraz standardowego haka cyklu życia onremove
.
v0.2.x
var Component = {
controller: function () {
this.onunload = function (e) {
// ...
};
},
view: function () {
// ...
},
};
v2.x
var Component = {
onremove: function() {
// ...
}
view: function() {
// ...
}
}
m.request
Obietnice zwracane przez m.request nie są już getterami i setterami typu m.prop
. Ponadto, initialValue
, unwrapSuccess
i unwrapError
nie są już obsługiwanymi opcjami.
Ponadto, żądania nie korzystają już z semantyki m.startComputation
/m.endComputation
. Zamiast tego, przerysowania są zawsze wyzwalane po zakończeniu łańcucha obietnic żądania (chyba że ustawiono background: true
).
Parametr data
został podzielony na params
(parametry zapytania interpolowane do adresu URL i dołączone do żądania) oraz body
(treść do wysłania w bazowym XHR).
W wersji 0.2.x do zainicjowania żądania JSONP używało się dataType: "jsonp"
. W wersji 2.x używane jest m.jsonp
, które ma API w większości identyczne z m.request
, ale bez części związanych z XHR.
v0.2.x
var data = m.request({
method: 'GET',
url: 'https://api.github.com/',
initialValue: [],
});
setTimeout(function () {
console.log(data());
}, 1000);
m.request({
method: 'POST',
url: 'https://api.github.com/',
data: someJson,
});
v2.x
var data = [];
m.request({
method: 'GET',
url: 'https://api.github.com/',
}).then(function (responseBody) {
data = responseBody;
});
setTimeout(function () {
console.log(data); // uwaga: nie jest to getter-setter
}, 1000);
m.request({
method: 'POST',
url: 'https://api.github.com/',
body: someJson,
});
// LUB
var data = [];
m.request('https://api.github.com/').then(function (responseBody) {
data = responseBody;
});
setTimeout(function () {
console.log(data); // uwaga: nie jest to getter-setter
}, 1000);
m.request('https://api.github.com/', {
method: 'POST',
body: someJson,
});
Dodatkowo, jeśli opcja extract
zostanie przekazana do m.request
, wartość zwracana przez dostarczoną funkcję zostanie użyta bezpośrednio do rozwiązania obietnicy żądania, a funkcja zwrotna deserialize
zostanie zignorowana.
Nagłówki m.request
W wersji 0.2.x Mithril.js domyślnie nie ustawiał żadnych nagłówków w żądaniach. Teraz ustawia do 2 nagłówków:
Content-Type: application/json; charset=utf-8
dla żądań z treścią JSON, która jest!= null
Accept: application/json, text/*
dla żądań oczekujących odpowiedzi JSON
Pierwszy z tych nagłówków, Content-Type
, spowoduje wykonanie preflight CORS, ponieważ ze względu na określony typ zawartości nie znajduje się na liście bezpiecznych nagłówków CORS, co może prowadzić do nowych błędów, w zależności od konfiguracji CORS na serwerze. W przypadku problemów, możesz zastąpić ten nagłówek, przekazując headers: {"Content-Type": "text/plain"}
. (Nagłówek Accept
nie wyzwala preflight, więc nie musisz go zastępować.)
Specyfikacja Fetch pozwala uniknąć sprawdzania preflight CORS tylko dla typów zawartości: application/x-www-form-urlencoded
, multipart/form-data
i text/plain
. Inne typy są niedozwolone, a JSON jest celowo zabroniony.
m.deferred
usunięte
W wersji 0.2.x używano niestandardowego obiektu kontraktu asynchronicznego m.deferred
, który stanowił podstawę dla m.request
. W wersji 2.x używane są obietnice, a w środowiskach, które ich nie obsługują, implementowany jest polyfill. Zamiast m.deferred
, używaj obietnic.
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);
}); //wyświetli "hello world" po 1 sekundzie
v2.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);
}); //wyświetli "hello world" po 1 sekundzie
m.sync
usunięte
m.sync
jest zbędne, ponieważ wersja 2.x używa obietnic zgodnych ze standardami. Użyj zamiast tego 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/dead-claudia',
}),
]).then(function (users) {
console.log('Contributors:', users[0].name, 'and', users[1].name);
});
v2.x
Promise.all([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({
method: 'GET',
url: 'https://api.github.com/users/dead-claudia',
}),
]).then(function (users) {
console.log('Contributors:', users[0].name, 'and', users[1].name);
});
Wymagana przestrzeń nazw xlink
W wersji 0.2.x xlink
była jedyną obsługiwaną przestrzenią nazw atrybutów i była obsługiwana w specjalny sposób. Obecnie parsowanie przestrzeni nazw jest w pełni obsługiwane, a atrybuty z przestrzenią nazw muszą jawnie deklarować swoją przestrzeń nazw.
v0.2.x
m(
'svg',
// przestrzeń nazw atrybutu `href` jest przypisywana automatycznie
m("image[href='image.gif']")
);
v2.x
m(
'svg',
// Przestrzeń nazw określona przez użytkownika na atrybucie `href`
m("image[xlink:href='image.gif']")
);
Zagnieżdżone tablice w widokach komponentów
Tablice reprezentują teraz fragmenty, które w wirtualnym DOM w wersji 2.x mają istotne znaczenie strukturalne. W wersji 0.2.x zagnieżdżone tablice były spłaszczane do jednej, ciągłej listy wirtualnych węzłów na potrzeby porównywania różnic. W wersji 2.x struktura tablicy jest zachowywana, a elementy potomne danej tablicy nie są traktowane jako elementy równorzędne z sąsiednich tablic.
Sprawdzanie równości vnode
Jeśli węzeł wirtualny (vnode) jest ściśle równy węzłowi zajmującemu jego miejsce w ostatnim renderowaniu, wersja 2.x pominie tę część drzewa, nie sprawdzając mutacji i nie wyzwalając żadnych metod cyklu życia w poddrzewie. Dokumentacja komponentu zawiera więcej szczegółów na ten temat.