v0.2.x에서 v2.x로 마이그레이션하기
v1.x와 v2.x는 v0.2.x와 대부분 API 호환성을 유지하지만, 몇 가지 호환성이 깨지는 변경 사항이 있습니다. v2.x로 마이그레이션하는 과정은 대체로 유사하므로 아래 내용은 대부분 두 버전에 공통적으로 적용됩니다.
마이그레이션 시에는 mithril-codemods 도구를 사용하여 가장 간단한 마이그레이션 단계를 자동화하는 것을 권장합니다.
m.prop
제거
v2.x에서 m.prop()
은 더 강력한 스트림 마이크로 라이브러리로 대체되었으며, 더 이상 코어에 포함되지 않습니다. 선택적인 스트림 모듈 사용 방법은 문서를 참고하십시오.
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;
});
m.component
제거
v0.2.x에서는 m(Component)
또는 m.component(Component)
를 사용하여 컴포넌트를 생성할 수 있었습니다. v2.x에서는 m(Component)
만 지원합니다.
v0.2.x
// 다음 두 코드는 동일합니다.
m.component(Component);
m(Component);
v2.x
m(Component);
m.withAttr
제거
v0.2.x에서는 이벤트 리스너를 oninput: m.withAttr("value", func)
와 같이 사용할 수 있었습니다. v2.x에서는 이벤트 대상에서 직접 값을 읽어야 합니다. m.withAttr
은 m.prop
과 함께 사용했을 때 유용했지만, 코어 외부 솔루션으로 대체되었고 v1.x에서 스트림의 광범위하고 일반적인 사용 사례를 찾기 어려워 대부분의 유용성을 잃었습니다.
v0.2.x
var value = m.prop('');
// 뷰에서
m('input[type=text]', {
value: value(),
oninput: m.withAttr('value', value),
});
v2.x
var value = '';
// 뷰에서
m('input[type=text]', {
value: value,
oninput: function (ev) {
value = ev.target.value;
},
});
m.version
제거
m.version
은 일반적으로 거의 사용되지 않았으며, 필요한 경우 직접 추가할 수 있습니다. 사용 가능한 기능을 파악하기 위해 기능 감지를 사용하는 것이 좋으며 v2.x API는 이를 더 잘 지원하도록 설계되었습니다.
config
함수
v0.2.x에서 Mithril.js는 config
라는 단일 라이프사이클 메서드를 제공했습니다. v2.x에서는 vnode의 라이프사이클을 훨씬 더 세밀하게 제어할 수 있습니다.
v0.2.x
m('div', {
config: function (element, isInitialized) {
// 매 리드로우(redraw) 시 실행됩니다.
// isInitialized는 노드가 DOM에 추가되었는지 여부를 나타내는 불리언 값입니다.
},
});
v2.x
이 새로운 메서드에 대한 자세한 내용은 lifecycle-methods.md에서 확인할 수 있습니다.
m('div', {
// DOM 노드가 생성되기 전에 호출됩니다.
oninit: function (vnode) {
/*...*/
},
// DOM 노드가 생성된 후에 호출됩니다.
oncreate: function (vnode) {
/*...*/
},
// 노드가 업데이트되기 전에 호출됩니다. false를 반환하면 업데이트가 취소됩니다.
onbeforeupdate: function (vnode, old) {
/*...*/
},
// 노드가 업데이트된 후에 호출됩니다.
onupdate: function (vnode) {
/*...*/
},
// 노드가 제거되기 직전에 호출됩니다. DOM에서 노드를 제거할 준비가 완료되면 resolve되는 Promise를 반환해야 합니다.
onbeforeremove: function (vnode) {
/*...*/
},
// 노드가 제거되기 전에 호출되지만 onbeforeremove가 done()을 호출한 후에 호출됩니다.
onremove: function (vnode) {
/*...*/
},
});
사용 가능한 경우 vnode의 DOM 요소는 vnode.dom
속성을 통해 접근할 수 있습니다.
다시 그리기(redraw) 동작 변경 사항
Mithril.js의 렌더링 엔진은 여전히 반자동 전역 다시 그리기를 기반으로 하지만, 일부 API와 동작 방식에 차이가 있습니다.
더 이상 다시 그리기 잠금(redraw lock)이 없습니다.
v0.2.x에서 Mithril.js는 일시적으로 그리기 로직을 차단하는 '다시 그리기 잠금'을 지원했습니다. 기본적으로 m.request
는 실행 시 그리기 루프를 잠그고 보류 중인 모든 요청이 완료되면 잠금을 해제합니다. m.startComputation()
및 m.endComputation()
을 사용하여 동일한 동작을 수동으로 호출할 수 있었습니다. 후자의 API와 관련된 동작은 대체 API 없이 v2.x에서 제거되었습니다. 다시 그리기 잠금은 버그가 있는 UI를 유발할 수 있습니다. 애플리케이션의 특정 부분에서 발생한 문제가 뷰의 다른 부분의 업데이트를 막아서는 안 됩니다.
이벤트 핸들러에서 다시 그리기(redraw) 취소
m.mount()
및 m.route()
는 DOM 이벤트 핸들러가 실행된 후 자동으로 다시 그립니다. 이벤트 핸들러에서 다시 그리기를 취소하려면, 전달된 이벤트 객체의 redraw
속성에 false
를 할당하십시오.
v0.2.x
m('div', {
onclick: function (e) {
m.redraw.strategy('none');
},
});
v2.x
m('div', {
onclick: function (e) {
e.redraw = false;
},
});
동기식 다시 그리기(redraw) 변경
v0.2.x에서는 m.redraw()
에 truthy 값을 전달하여 Mithril.js가 즉시 다시 그리도록 할 수 있었습니다. v2.x에서는 이 기능이 명확성을 위해 두 가지 다른 메서드로 분리되었습니다.
v0.2.x
m.redraw(true); // 즉시 & 동기적으로 다시 그립니다.
v2.x
m.redraw(); // 다음 requestAnimationFrame 틱에서 다시 그리기를 예약합니다.
m.redraw.sync(); // 즉시 다시 그리기를 호출하고 완료될 때까지 기다립니다.
m.startComputation
/m.endComputation
제거
이 메서드들은 안티 패턴으로 간주되며 여러 가지 문제가 있는 엣지 케이스가 있어 v2.x에서 대체 API 없이 제거되었습니다.
컴포넌트 controller
함수
v2.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);
},
});
v2.x
m.mount(document.body, {
oninit: function (vnode) {
vnode.state.fooga = 1;
},
view: function (vnode) {
return m('p', vnode.state.fooga);
},
});
// 또는
m.mount(document.body, {
// `this`는 기본적으로 vnode.state에 바인딩됩니다.
oninit: function (vnode) {
this.fooga = 1;
},
view: function (vnode) {
return m('p', this.fooga);
},
});
컴포넌트 인자
v2.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 }));
v2.x
var Component = {
oninit: function (vnode) {
// vnode.attrs.fooga === 1
},
view: function (vnode) {
// vnode.attrs.fooga === 1
},
};
m('div', m(Component, { fooga: 1 }));
컴포넌트 vnode 자식
v0.2.x에서 컴포넌트 vnode 자식은 정규화되지 않았고 추가 인수로 전달되었으며 평탄화되지도 않았습니다. (내부적으로는 부분적으로 적용된 컴포넌트를 반환하여 부분적으로 적용된 컴포넌트를 기반으로 차이를 비교했습니다.) v2.x에서 컴포넌트 vnode 자식은 처리된 자식 배열로 vnode.children
을 통해 전달되지만 v0.2.x와 마찬가지로 개별 자식 자체는 정규화되지 않으며 자식 배열도 평탄화되지 않습니다.
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';
})
);
DOM vnode 자식
v0.2.x에서 DOM 노드의 자식은 단일 배열 자식만 있는 경우 자식을 직접 사용하는 것을 제외하고는 정규화 없이 리터럴로 표현되었습니다. 문자열이 문자 그대로 표현된 다음과 같은 구조를 반환했습니다.
m("div", "value", ["nested"])
// 다음과 같이 됩니다.
{
tag: "div",
attrs: {},
children: [
"value",
["nested"],
]
}
v2.x에서 DOM vnode의 자식은 단일 일관된 구조의 객체로 정규화됩니다.
m("div", "value", ["nested"])
// 대략 다음과 같은 구조를 가집니다.
{
tag: "div",
attrs: null,
children: [
{tag: "#", children: "value"},
{tag: "[", children: [
{tag: "#", children: "nested"},
]},
]
}
DOM vnode에 단일 텍스트 자식만 있는 경우 대신 text
속성에 해당 값을 설정합니다.
m("div", "value")
// 대략 다음과 같이 됩니다.
{
tag: "div",
attrs: null,
text: "",
children: undefined,
}
v2.x vnode 구조 및 정규화 방법에 대한 자세한 내용은 vnode 문서를 참조하십시오.
여기서 대부분의 v2.x vnode 속성은 간결성을 위해 생략되었습니다.
Key
v0.2.x에서는 키(key)가 지정된 vnode와 키가 없는 vnode를 자유롭게 혼합할 수 있었습니다.
v2.x에서는 조각(fragment)과 요소(element)의 자식 목록이 모두 키가 있거나 모두 키가 없어야 합니다. 빈 자리(Hole)도 이 검사에서는 키가 없는 것으로 간주되며, 더 이상 무시되지 않습니다.
이 문제를 해결해야 하는 경우 [m("div", {key: whatever})]
와 같이 단일 vnode를 포함하는 조각을 사용하는 것이 좋습니다.
view()
매개변수
v0.2.x 뷰 함수에는 controller
인스턴스에 대한 참조와 (선택적으로) 컴포넌트에 전달된 모든 옵션이 전달됩니다. v2.x에서는 controller
함수와 마찬가지로 vnode
만 전달됩니다.
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) {
// ctrl 대신 vnode.state를 사용하십시오.
// options 대신 vnode.attrs를 사용하십시오.
},
});
m()
에 컴포넌트 전달
v0.2.x에서는 래핑 없이 m()
의 두 번째 인수로 컴포넌트를 전달할 수 있었습니다. v2.x에서는 일관성을 위해 항상 m()
함수로 감싸야 합니다.
v0.2.x
m('div', Component);
v2.x
m('div', m(Component));
m.mount()
및 m.route()
에 vnode 전달
v0.2.x에서 m.mount(element, component)
는 두 번째 인수로 컴포넌트 대신 vnode를 허용했습니다(공식 문서에는 없었지만). 마찬가지로, m.route(element, defaultRoute, routes)
에서 routes
객체의 값으로 vnode를 사용할 수 있었습니다.
v2.x에서는 두 경우 모두 컴포넌트가 필요합니다.
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
v0.2.x에서는 라우팅 모드를 m.route.mode
에 "pathname"
, "hash"
, "search"
중 하나의 문자열을 할당하여 설정할 수 있었습니다. v2.x
에서는 m.route.prefix = prefix
로 대체되었으며, 여기서 prefix
는 임의의 접두사가 될 수 있습니다. #
로 시작하면 "hash" 모드, ?
는 "search" 모드, 다른 문자(또는 빈 문자열)는 "pathname" 모드로 작동합니다. 또한 m.route.prefix = "/path/#!"
또는 ?#
와 같이 위의 조합도 지원합니다.
기본값은 단순히 #
대신 #!
(hashbang) 접두사를 사용하도록 변경되었습니다. 따라서 기본 동작을 사용하고 기존 URL을 유지하려면 라우트를 초기화하기 전에 m.route.prefix = "#"
를 지정하십시오.
v0.2.x
m.route.mode = 'hash';
m.route.mode = 'pathname';
m.route.mode = 'search';
v2.x
// Direct equivalents
m.route.prefix = '#';
m.route.prefix = '';
m.route.prefix = '?';
m.route()
및 앵커 태그
라우팅 가능한 링크를 처리할 때, 이제 속성 대신 특수한 내장 컴포넌트를 사용합니다. <button>
등에서 이를 사용하는 경우 selector: "button"
속성으로 태그 이름을 지정할 수 있습니다.
v0.2.x
// When clicked this link will load the "/path" route instead of navigating
m('a', {
href: '/path',
config: m.route,
});
v2.x
// When clicked this link will load the "/path" route instead of navigating
m(m.route.Link, {
href: '/path',
});
경로 템플릿
v1.x에는 유사하지만 별도로 설계된 2개의 구문과 3개의 다른 구현을 가진 세 가지 별도의 경로 템플릿 구문이 있었습니다. 이는 다소 임시방편으로 정의되었으며, 매개변수는 일반적으로 이스케이프되지 않았습니다. 이제 :key
인 경우 모든 것이 인코딩되고, :key...
인 경우 원시 상태입니다. 예기치 않게 인코딩되는 경우 :path...
를 사용하십시오. 간단합니다.
구체적으로, 이것이 각 메서드에 미치는 영향은 다음과 같습니다.
m.request
URL
v2.x의 경로 컴포넌트는 보간될 때 자동으로 이스케이프되며 params
에서 값을 읽습니다. v0.2.x에서 m.request({url: "/user/:name/photos/:id", data: {name: "a/b", id: "c/d"}})
는 URL이 /user/a%2Fb/photos/c/d
로 설정된 요청을 보냈습니다. v2.x에서 해당하는 m.request({url: "/user/:name/photos/:id", params: {name: "a/b", id: "c/d"}})
는 /user/a%2Fb/photos/c%2Fd
로 요청을 보냅니다. 만약 의도적으로 key를 이스케이프 처리 없이 보간하고 싶다면, :key...
를 대신 사용하십시오.
/api/search?q=:query
와 같은 인라인 쿼리 문자열의 보간은 v2.x에서 수행되지 않습니다. 쿼리 문자열에 지정하지 않고 적절한 key 이름으로 params
를 통해 전달하십시오.
이는 m.jsonp
에도 적용된다는 점에 유의하십시오. m.request
+ dataType: "jsonp"
에서 m.jsonp
로 마이그레이션할 때도 이를 알아야 합니다.
m.route(route, params, shouldReplaceHistoryEntry)
경로
이제 이 경로들은 보간을 허용하며, m.request
와 동일한 방식으로 작동합니다.
m.route
경로 패턴
:key...
형태의 경로 key는 v1.x에서는 URL 디코딩된 값을, v2.x에서는 원시 URL을 반환합니다.
이전에는 :key.md
와 같은 것이 잘못 허용되어 결과 매개변수의 값이 keymd: "..."
로 설정되었습니다. 더 이상 그렇지 않습니다. .md
는 이제 이름이 아닌 패턴의 일부입니다.
현재 경로 읽기/쓰기
v0.2.x에서는 현재 경로와 관련된 모든 상호 작용이 m.route()
를 통해 이루어졌습니다. v2.x에서는 이것이 두 개의 함수로 분리되었습니다.
v0.2.x
// Getting the current route
m.route();
// Setting a new route
m.route('/other/route');
v2.x
// Getting the current route
m.route.get();
// Setting a new route
m.route.set('/other/route');
경로 매개변수 액세스
이 API는 v2.x에서도 여전히 사용 가능하며, 추가적으로 모든 경로 매개변수는 vnode의 attrs 객체 내 속성으로 전달됩니다.
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"
},
},
});
쿼리 문자열 빌드/파싱
v0.2.x는 m.route
에서 파생된 메서드인 m.route.buildQueryString()
및 m.route.parseQueryString()
을 사용했습니다. v2.x에서는 이러한 메서드가 분리되어 루트 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');
또한 v2.x에서는 m.buildQueryString
및 이를 사용하는 m.request
와 같은 메서드에 의해 {key: undefined}
가 key=undefined
로 직렬화됩니다. v0.2.x에서는 key가 생략되었고 이는 m.request
로 이어졌습니다. 만약 이전에 이 동작에 의존했다면, 객체에서 key를 완전히 생략하도록 코드를 변경하십시오. 쉽게 할 수 없고 v0.2.x 동작을 유지해야 하는 경우 값이 undefined
인 객체에서 모든 key를 제거하는 간단한 유틸리티를 사용하는 것이 좋습니다.
// Call whenever you need to omit `undefined` parameters from an object.
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;
}
언마운트 방지
더 이상 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 });
},
};
v2.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 () {
// ...
},
};
v2.x
var Component = {
onremove: function() {
// ...
},
view: function() {
// ...
}
}
m.request
m.request에서 반환된 Promise는 더 이상 m.prop
getter-setter가 아닙니다. 또한 initialValue
, unwrapSuccess
및 unwrapError
는 더 이상 지원되는 옵션이 아닙니다.
또한, 요청은 더 이상 m.startComputation
/m.endComputation
의미 체계를 가지지 않습니다. 대신 요청 Promise 체인이 완료되면 항상 다시 그리기가 트리거됩니다 (background: true
가 설정되지 않은 경우).
data
매개변수는 이제 URL에 보간되어 요청에 추가되는 쿼리 매개변수인 params
와 기본 XHR에서 보낼 본문인 body
로 분할되었습니다.
v0.2.x에서는 dataType: "jsonp"
를 사용하여 JSONP 요청을 시작했습니다. v2.x에서는 이제 XHR 관련 부분이 없는 m.request
와 거의 동일한 API를 가진 m.jsonp
를 사용합니다.
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); // 주의: getter-setter가 아닙니다
}, 1000);
m.request({
method: 'POST',
url: 'https://api.github.com/',
body: someJson,
});
// OR
var data = [];
m.request('https://api.github.com/').then(function (responseBody) {
data = responseBody;
});
setTimeout(function () {
console.log(data); // 주의: getter-setter가 아닙니다
}, 1000);
m.request('https://api.github.com/', {
method: 'POST',
body: someJson,
});
또한 extract
옵션이 m.request
에 전달되면 제공된 함수의 반환 값이 요청 Promise를 해결하는 데 직접 사용되고 deserialize
콜백은 무시됩니다.
m.request
헤더
v0.2.x에서 Mithril.js는 기본적으로 요청에 헤더를 설정하지 않았습니다. 이제 최대 2개의 헤더를 설정합니다.
- JSON 본문이 있는 요청에 대해
Content-Type: application/json; charset=utf-8
(JSON 본문이!= null
인 경우) - JSON 응답을 예상하는 요청에 대해
Accept: application/json, text/*
두 헤더 중 첫 번째인 Content-Type
은 지정된 콘텐츠 유형으로 인해 CORS-safelisted 요청 헤더가 아니므로 CORS 프리페치를 트리거하고 서버에서 CORS가 구성된 방식에 따라 새로운 오류를 발생시킬 수 있습니다. 이로 인해 문제가 발생한다면, headers: {"Content-Type": "text/plain"}
을 전달하여 해당 헤더를 재정의해야 할 수도 있습니다. (Accept
헤더는 아무것도 트리거하지 않으므로 재정의할 필요가 없습니다.)
Fetch 사양에서 CORS 프리페치 검사를 피할 수 있는 유일한 콘텐츠 유형은 application/x-www-form-urlencoded
, multipart/form-data
및 text/plain
입니다. 다른 것은 허용하지 않으며 의도적으로 JSON을 허용하지 않습니다.
m.deferred
제거됨
v0.2.x는 m.deferred
로 노출된 자체 사용자 지정 비동기 계약 객체를 사용했으며, 이는 m.request
의 기반으로 사용되었습니다. v2.x는 대신 Promise를 사용하고 지원되지 않는 환경에서 폴리필을 구현합니다. 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);
}); //logs "hello world" after 1 second
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);
}); //logs "hello world" after 1 second
m.sync
제거됨
v2.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/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);
});
xlink
네임스페이스 필요
v0.2.x에서는 xlink
네임스페이스가 유일하게 지원되는 속성 네임스페이스였으며, 특수한 케이스 동작을 통해 지원되었습니다. 이제 네임스페이스 파싱이 완벽하게 지원되므로, 네임스페이스가 지정된 속성은 해당 네임스페이스를 명시적으로 선언해야 합니다.
v0.2.x
m(
'svg',
// the `href` attribute is namespaced automatically
m("image[href='image.gif']")
);
v2.x
m(
'svg',
// User-specified namespace on the `href` attribute
m("image[xlink:href='image.gif']")
);
뷰의 중첩 배열
이제 배열은 fragments를 나타내며, 이는 v2.x 가상 DOM에서 구조적으로 중요한 의미를 가집니다. v0.2.x의 중첩 배열은 diffing을 위해 가상 노드의 하나의 연속적인 목록으로 평면화되었지만, v2.x는 배열 구조를 유지합니다. 즉, 주어진 배열의 자식은 인접한 배열의 자식과 형제로 간주되지 않습니다.
vnode
동일성 검사
vnode가 마지막 그리기에 해당 위치를 차지하는 vnode와 엄격하게 동일한 경우 v2.x는 하위 트리의 변경 사항을 확인하거나 라이프사이클 메서드를 트리거하지 않고 해당 트리의 해당 부분을 건너뜁니다. 컴포넌트 문서에서 이 문제에 대한 자세한 내용을 확인할 수 있습니다.