MVVM
MVVM (Model View ViewModel)은 MVC 및 MVP를 기반으로 한 아키텍처 패턴으로, 사용자 인터페이스 (UI) 개발을 비즈니스 논리 및 응용 프로그램에서의 동작과 더 명확하게 구분합니다. 이를 위해이 패턴의 많은 구현은 선언적 데이터 바인딩을 사용하여 다른 레이어의 뷰에서 작업을 분리 할 수 있습니다.
이는 동일한 코드베이스 내에서 거의 동시에 발생하는 UI 및 개발 작업을 용이하게합니다. UI 개발자는 자신의 문서 마크 업 (HTML) 내에서 ViewModel에 바인딩을 작성합니다.이 모델에서는 모델 및 ViewModel이 응용 프로그램의 논리에서 작업하는 개발자에 의해 유지 관리됩니다.
History
MVVM (이름으로)은 원래 Microsoft에서 Windows Presentation Foundation (WPF) 및 Silverlight와 함께 사용하도록 정의되었으며 2005 년 John Grossman이 Avalon (WPF의 코드 명)에 대한 블로그 게시물에서 공식적으로 발표했습니다. 또한 MVC를 단순히 사용하는 대신 Adobe Flex 커뮤니티에서 인기를 얻었습니다.
Microsoft가 MVVM 이름을 채택하기 전에는 커뮤니티에서 MVP에서 MVPM으로 이동하는 움직임이있었습니다. Model View PresentationModel. 마틴 파울러 (Martin Fowler)는 2004 년에 PresentationModels에 대한 기사를 썼습니다. PresentationModel의 아이디어는이 기사보다 훨씬 오래되었지만 아이디어에서 큰 틈을 준 것으로 간주되어 널리 보급되었습니다.
마이크로 소프트가 MVPM 대신 MVVM을 발표 한 이후 "alt.net"서클에 꽤 많은 소란이있었습니다. 많은 사람들은 GUI 세계에서 회사의 지배력이 커뮤니티 전체를 인수 할 수있는 기회를 주면서 마케팅 목적으로 기쁘게 생각하는 기존 개념의 이름을 바꿨다고 주장했습니다. 점진적인 군중은 MVVM과 MVPM이 실제로 똑같은 아이디어 였지만 약간 다른 패키지가 있음을 인정했습니다.
최근 MVVM은 KnockoutJS, Kendo MVVM 및 Knockback.js와 같은 구조적 프레임 워크의 형태로 JavaScript에서 구현되었으며 커뮤니티에서 전반적으로 긍정적 인 반응을 보였습니다.
이제 MVVM을 구성하는 세 가지 구성 요소를 살펴 보겠습니다.
Model
MV * 제품군의 다른 구성원과 마찬가지로 MVVM의 모델은 응용 프로그램에서 사용할 도메인 특정 데이터 또는 정보를 나타냅니다. 도메인 특정 데이터의 전형적인 예는 사용자 계정 (예 : 이름, 아바타, 전자 메일) 또는 음악 트랙 (예 : 제목, 연도, 앨범) 일 수 있습니다.
모델은 정보를 보유하지만 일반적으로 동작을 처리하지 않습니다. 이들은 정보의 형식을 지정하지 않거나 데이터가 브라우저에 표시되는 방식에 영향을 미치지 않습니다. 책임이 없기 때문입니다. 대신, 데이터의 형식은 View에 의해 처리되는 반면, 비헤이비어는 Model과 상호 작용하는 다른 계층 인 ViewModel에 캡슐화되어야하는 비즈니스 논리로 간주됩니다.
이 규칙에 대한 유일한 예외는 유효성 검사 인 경향이 있으며 Models가 기존 모델을 정의하거나 업데이트하는 데 사용되는 데이터의 유효성을 검사하는 것이 허용됩니다 (예 : 특정 정규 표현식의 요구 사항을 충족하는 전자 메일 주소 입력).
KnockoutJS에서 모델은 위의 정의에 해당되지만 모델 데이터를 읽고 쓸 수있는 서버 측 서비스로 Ajax 호출을 수행하는 경우가 많습니다.
간단한 Todo 응용 프로그램을 만들었다면 단일 Todo 항목을 나타내는 KnockoutJS 모델은 다음과 같이 보일 수 있습니다.
var Todo = function ( content, done ) {
this.content = ko.observable(content);
this.done = ko.observable(done);
this.editing = ko.observable(false);
};
주의 : 위의 스 니펫에서는 KnockoutJS 네임 스페이스 ko
에서 observable()
메서드를 호출하고 있음을 알 수 있습니다. KnockoutJS에서 observables는 구독자에게 변경 사항을 알리고 종속성을 자동으로 감지 할 수있는 특수 JavaScript 객체입니다. Model 속성의 값이 수정 될 때 모델과 ViewModel을 동기화 할 수 있습니다.
View
MVC와 마찬가지로 뷰는 사용자가 실제로 상호 작용하는 응용 프로그램의 유일한 부분입니다. 이것들은 ViewModel의 상태를 나타내는 대화 형 UI입니다. 이러한 관점에서 뷰는 수동이 아닌 액티브로 간주되지만 MVC 및 MVP의 뷰에서도 마찬가지입니다. MVC, MVP 및 MVVM에서 뷰는 수동 일 수도 있지만 이것이 의미하는 바는 무엇입니까?
패시브 뷰는 디스플레이 만 출력하고 사용자 입력을받지 않습니다.
그러한 견해는 우리의 응용 프로그램에있는 모델에 대한 실제 지식이 없으며 발표자가 조작 할 수 있습니다. MVVM의 활성보기에는 ViewModel을 이해해야하는 데이터 바인딩, 이벤트 및 동작이 포함됩니다. 이러한 동작을 속성에 매핑 할 수 있지만 View는 ViewModel에서 이벤트를 처리해야합니다.
View가 상태를 처리 할 책임이 없다는 것을 기억하는 것이 중요합니다.이 작업은 ViewModel과 동기화됩니다.
KnockoutJS View는 단순히 ViewModel에 연결하는 선언적 바인딩이있는 HTML 문서입니다. KnockoutJS View는 ViewModel의 정보를 표시하고 명령을 전달하며 (예 : 요소를 클릭하는 사용자) ViewModel의 상태가 변경 될 때 업데이트합니다. 그러나 ViewModel의 데이터를 사용하여 마크 업을 생성하는 템플릿을이 용도로 사용할 수도 있습니다.
간단한 초기 예제를 제공하기 위해 JavaScript MVVM 프레임 워크 KnockoutJS에서 마크 업에서 ViewModel과 관련 바인딩을 정의하는 방법을 살펴볼 수 있습니다.
ViewModel :
var aViewModel = {
contactName: ko.observable("John")
};
ko.applyBindings(aViewModel);
View:
<p><input id="source" data-bind="value: contactName, valueUpdate: 'keyup'" /></p>
<div data-bind="visible: contactName().length > 10">
You have a really long name!
</div>
<p>Contact name: <strong data-bind="text: contactName"></strong></p>
입력 텍스트 상자 (소스)는 contactName
의 초기 값을 가져오고, contactName
이 변경 될 때마다이 값을 자동으로 업데이트합니다. 데이터 바인딩이 양방향이기 때문에 텍스트 상자에 입력하면 그에 따라 contactName
이 업데이트되므로 값은 항상 동기화됩니다.
KnockoutJS에 고유 한 구현이지만, "정말 긴 이름이 있습니다!"라는 <div>
가 포함되어 있습니다. 텍스트에는 간단한 유효성 검사 (데이터 바인딩의 형태로 다시 한번)가 포함되어 있습니다. 입력이 10자를 초과하면 표시되고 그렇지 않으면 숨겨집니다.
보다 고급 예제로 넘어 가면 Todo 애플리케이션으로 돌아갈 수 있습니다. 필요한 모든 데이터 바인딩을 포함하여이를 위해 KnockoutJS 뷰를 정리하면 다음과 같이 보일 수 있습니다.
<div id="todoapp">
<header>
<h1>Todos</h1>
<input id="new-todo" type="text" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add"
placeholder="What needs to be done?"/>
</header>
<section id="main" data-bind="block: todos().length">
<input id="toggle-all" type="checkbox" data-bind="checked: allCompleted">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: todos">
<!-- item -->
<li data-bind="css: { done: done, editing: editing }">
<div class="view" data-bind="event: { dblclick: $root.editItem }">
<input class="toggle" type="checkbox" data-bind="checked: done">
<label data-bind="text: content"></label>
<a class="destroy" href="#" data-bind="click: $root.remove"></a>
</div>
<input class="edit" type="text"
data-bind="value: content, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selectAndFocus: editing, event: { blur: $root.stopEditing }"/>
</li>
</ul>
</section>
</div>
마크 업의 기본 레이아웃은 새 항목을 추가하기위한 입력 텍스트 상자 (new-todo
), 항목을 완료로 표시하는 토글러 및 Todo에 대한 템플리트가있는 목록 (todo-list
)을 포함하는 비교적 간단합니다. 항목은 li
의 형식입니다.
위의 마크 업에서 데이터 바인딩은 다음과 같이 세분화 할 수 있습니다.
- 입력 텍스트 상자
new-todo
에는current
속성에 대한 데이터 바인딩이 있습니다.이 데이터 바인딩은 추가되는 현재 항목의 값이 저장되는 곳입니다. ViewModel (곧 표시)은current
속성을 관찰하고add
이벤트에 대한 바인딩을가집니다. Enter 키를 누르면add
이벤트가 트리거되고 ViewModel은 현재 값을 자르고 필요에 따라 Todo 목록에 추가 할 수 있습니다 - 입력 체크 박스
toggle-all
는 클릭하면 현재 항목을 모두 완료로 표시 할 수 있습니다. 이 옵션을 선택하면 ViewModel에서 볼 수있는allCompleted
이벤트가 트리거됩니다. - 항목
li
에는 클래스done
을 가지고 있습니다. 작업이 완료로 표시되면 CSS 클래스editing
이 그에 따라 표시됩니다. 항목을 두 번 클릭하면$root.editItem
콜백이 실행됩니다. - 클래스
toggle
확인란을 선택하면done
속성의 상태가 표시됩니다. - 레이블에는 Todo 항목 (
content
)의 텍스트 값이 포함됩니다. - 클릭 할 때
$root.remove
콜백을 호출하는 제거 버튼도 있습니다. - 편집 모드에 사용되는 입력 텍스트 상자는 Todo 항목
content
의 값도 보유합니다.enterKey
이벤트는editing
속성을 true 또는 false로 설정합니다.
ViewModel
ViewModel은 데이터 변환기 역할을하는 특수 컨트롤러로 간주 될 수 있습니다. 모델 정보를 뷰 정보로 변경하고 뷰에서 모델로 명령을 전달합니다.
예를 들어 유닉스 형식의 날짜 속성이 포함 된 모델이 있다고 가정 해 보겠습니다 (예 : 1333832407). Google 모델은 사용자의 속성 (예 : 04/07/2012 @ 오후 5시)에 대한 표시를 인식하지 않고 속성을 표시 형식으로 변환해야하는 경우가 아니라 단순히 데이터의 원시 형식 . 뷰에는 형식이 지정된 날짜가 포함되어 있으며 ViewModel은 둘 사이의 중개자 역할을합니다.
이러한 의미에서, ViewModel은 View보다는 Model의 모델로 보일 수 있지만 View의 대부분의 디스플레이 로직을 처리합니다. ViewModel은 뷰의 상태를 유지하고, 뷰의 액션을 기반으로 모델을 업데이트하고, 뷰의 이벤트를 트리거하는 데 도움이되는 메소드를 노출 할 수도 있습니다.
요약하면 ViewModel은 UI 레이어 뒤에 위치합니다. 뷰 (모델에서)가 필요로하는 데이터를 노출하며 뷰가 데이터 및 동작 모두에 대해 사용하는 소스로 볼 수 있습니다.
KnockoutJS는 ViewModel을 UI에서 수행 할 수있는 데이터 및 작업의 표현으로 해석합니다. 이것은 UI 자체 나 지속되는 데이터 모델이 아니라 사용자가 작업하고있는 저장 데이터를 아직 보유 할 수있는 계층입니다. Knockout의 ViewModels는 HTML 마크 업에 대한 지식이없는 JavaScript 객체로 구현됩니다. 구현에 대한 이러한 추상적 인 접근 방식을 통해 단순한 상태를 유지할 수 있으므로 필요에 따라 더 복잡한 동작을 쉽게 관리 할 수 있습니다.
Todo 응용 프로그램을위한 부분적인 KnockoutJS ViewModel은 다음과 같이 보일 수 있습니다 :
// our main ViewModel
var ViewModel = function ( todos ) {
var self = this;
// map array of passed in todos to an observableArray of Todo objects
self.todos = ko.observableArray(
ko.utils.arrayMap( todos, function ( todo ) {
return new Todo( todo.content, todo.done );
}));
// store the new todo value being entered
self.current = ko.observable();
// add a new todo, when enter key is pressed
self.add = function ( data, event ) {
var newTodo, current = self.current().trim();
if ( current ) {
newTodo = new Todo( current );
self.todos.push( newTodo );
self.current("");
}
};
// remove a single todo
self.remove = function ( todo ) {
self.todos.remove( todo );
};
// remove all completed todos
self.removeCompleted = function () {
self.todos.remove(function (todo) {
return todo.done();
});
};
// writeable computed observable to handle marking all complete/incomplete
self.allCompleted = ko.computed({
// always return true/false based on the done flag of all todos
read:function () {
return !self.remainingCount();
},
// set all todos to the written value (true/false)
write:function ( newValue ) {
ko.utils.arrayForEach( self.todos(), function ( todo ) {
//set even if value is the same, as subscribers are not notified in that case
todo.done( newValue );
});
}
});
// edit an item
self.editItem = function( item ) {
item.editing( true );
};
..
위에는 기본적으로 항목을 추가, 편집 또는 제거하는 데 필요한 메소드와 나머지 모든 항목을 완료로 표시하는 로직을 제공합니다. 참고 : ViewModel의 이전 예제에서 주목할 가치가있는 진정한 차이점은 관찰 가능한 배열입니다. KnockoutJS에서는 단일 객체에 대한 변경 사항을 감지하고 이에 응답하기를 원할 경우 observables
를 사용합니다. 그러나 우리가 컬렉션의 변화를 감지하고 이에 응답하고자한다면 observableArray
를 대신 사용할 수 있습니다. observables 배열을 사용하는 방법에 대한 간단한 예제는 다음과 같습니다.
// Define an initially an empty array
var myObservableArray = ko.observableArray();
// Add a value to the array and notify our observers
myObservableArray.push( 'A new todo item' );
참고 : 위에서 검토 한 완전한 Knockout.js Todo 애플리케이션은 관심이 있다면 TodoMVC에서 얻을 수 있습니다.
요약: The View and the ViewModel
뷰 및 ViewModel은 데이터 바인딩 및 이벤트를 사용하여 통신합니다. 초기 ViewModel 예제에서 보았 듯이 ViewModel은 모델 속성을 노출하는 것이 아니라 유효성 검사와 같은 다른 메소드 및 기능에 대한 액세스도 제공합니다.
뷰는 자신의 사용자 인터페이스 이벤트를 처리하여 필요한 경우 ViewModel에 매핑합니다. ViewModel의 모델과 속성은 양방향 데이터 바인딩을 통해 동기화되고 업데이트됩니다.
트리거 (데이터 트리거)를 사용하면 모델 속성의 상태 변화에 대해 추가로 대응할 수 있습니다.
요약: The ViewModel and the Model
ViewModel은 MVVM의 모델을 완전히 책임지고있는 것처럼 보일 수 있지만 주목할 가치가있는 관계가있는 약간의 미묘한 점이 있습니다. ViewModel은 데이터 바인딩을 위해 모델 또는 모델 속성을 노출 할 수 있으며 뷰에 노출 된 속성을 페치하고 조작하기위한 인터페이스를 포함 할 수도 있습니다.
장점과 단점
이제 MVVM이 무엇이며 어떻게 작동하는지 더 잘 이해하게되었습니다. 패턴을 적용 할 때의 장단점을 살펴 보겠습니다.
장점
- MVVM UI 및 UI를 구성하는 빌딩 블록을보다 쉽게 병렬 개발할 수 있습니다.
- 뷰를 추상화하여 코드 뒤에 필요한 비즈니스 로직 (또는 접착제)의 양을 줄입니다.
- ViewModel은 이벤트 중심 코드보다 단위 테스트가 쉬울 수 있습니다.
- ViewModel (View보다 Model 임)은 UI 자동화 및 상호 작용에 대한 걱정없이 테스트 할 수 있습니다.
단점
- 더 단순한 UI를 위해 MVVM은 과도 할 수 있습니다.
- 데이터 바인딩은 선언적이며 작업하기에 좋지만, 중단 점을 설정하는 명령형 코드보다 디버깅하기가 더 어려울 수 있습니다
- 중요하지 않은 응용 프로그램의 데이터 바인딩은 많은 책 관리를 만들 수 있습니다. 바인딩이 바인딩 된 객체보다 무거운 상황에서도 끝내기를 원하지 않습니다.
- 대규모 응용 프로그램에서는 필요한 양의 일반화를 얻으려면 ViewModel을 앞면으로 설계하는 것이 더 어려울 수 있습니다
느슨한 데이터 바인딩이있는 MVVM
MVC 또는 MVP 배경의 JavaScript 개발자가 MVVM을 검토하고 진정한 관심의 분리에 대해 불평하는 것은 흔한 일입니다. 즉, 뷰의 HTML 마크 업에서 유지되는 인라인 데이터 바인딩의 양입니다.
MVVM 구현 (예 : KnockoutJS, Knockback)을 처음 검토했을 때 개발자는 로직 (자바 스크립트)과 마크 업을 섞어 놓은 시대로 돌아가고 싶었고 빠르게 유지 보수 할 수 없다는 사실을 알고 놀랐습니다. 그러나 현실은 MVVM이 디자이너가 자신의 마크 업에서 로직에보다 쉽게 바인딩 할 수 있도록하는 등 여러 가지 좋은 이유 때문에이 작업을 수행한다는 것입니다.
우리 중 순수 주의자들에게는 KnockoutJS 1.3에서 소개되고 이후 모든 버전에서 사용할 수있는 맞춤 바인딩 공급자로 알려진 기능 덕분에 우리가 데이터 바인딩에 얼마나 의존하고 있는지를 크게 줄일 수 있다는 것을 알게되어 기쁩니다.
KnockoutJS는 기본적으로 아래 예제처럼 data-bind
특성이 있는 요소를 검색하는 데이터 바인딩 공급자를 가지고 있습니다.
<input id="new-todo" type="text" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?"/>
공급자가이 특성이있는 요소를 찾으면이를 분석하여 현재 데이터 컨텍스트를 사용하여 바인딩 개체로 변환합니다. 이것은 KnockoutJS가 항상 작업 한 방식으로, KnockoutJS가 해당 레이어의 데이터에 바인딩하는 요소에 바인딩을 선언적으로 추가 할 수 있습니다.
더 이상 중요하지 않은 뷰를 빌드하고 나면 마크 업에서의 바인딩이 관리하기 어려울 수있는 많은 수의 요소와 속성으로 끝날 수 있습니다. 그러나 사용자 지정 바인딩 공급자를 사용하면 더 이상 문제가되지 않습니다.
바인딩 공급자는 주로 다음 두 가지 사항에 관심이 있습니다.
- DOM 노드가 주어지면 모든 데이터 바인딩이 포함되어 있습니까?
- 노드가이 첫 번째 질문을 통과하면 현재 데이터 컨텍스트에서 바인딩 객체는 어떻게 보이나요?
바인딩 공급자는 두 가지 기능을 구현합니다.
nodeHasBindings
: 요소 일 필요는없는 DOM 노드를 사용합니다.getBindings
: 현재 데이터 컨텍스트에 적용된 바인딩을 나타내는 객체를 반환합니다.
따라서 skeleton 바인딩 공급자는 다음과 같이 보일 수 있습니다.
var ourBindingProvider = {
nodeHasBindings: function( node ) {
// returns true/false
},
getBindings: function( node, bindingContext ) {
// returns a binding object
}
};
이 공급자를 구현하기 전에 데이터 바인딩 특성의 논리를 간단히 살펴 보겠습니다.
Knockout의 MVVM을 사용할 때 응용 프로그램 논리가 뷰에 지나치게 묶여 있다는 생각에 불만족 스러울 때이를 변경할 수 있습니다. 요소에 이름으로 바인딩을 할당하기 위해 CSS 클래스와 비슷한 것을 구현할 수 있습니다. Ryan Niemeyer (knockmeout.net)는 데이터 클래스와 프레젠테이션 클래스를 혼동하는 것을 피하기 위해 이전에는 data-class
를 사용하도록 제안 했으므로 nodeHasBindings
함수에서이를 지원하도록 하자.
// does an element have any bindings?
function nodeHasBindings( node ) {
return node.getAttribute ? node.getAttribute("data-class") : false;
};
다음으로, 현명한 getBindings()
함수가 필요합니다. 우리가 CSS 클래스에 대한 아이디어를 고수하면서, 다른 요소들 사이에 바인딩 스펙을 공유 할 수 있도록 공간 분리 클래스를 지원하는 것도 고려해 보지 않으시겠습니까?
먼저 바인딩이 어떻게 생겼는지 검토해 보겠습니다. 우리는 우리의 데이터 클래스에서 사용하고자하는 키와 속성 이름이 일치해야하는 곳에 객체를 저장합니다.
참고 : KnockoutJS 응용 프로그램에서 기존의 데이터 바인딩을 사용하지 않고 사용자 지정 바인딩 공급자와 눈에 띄지 않는 바인딩을 변환하는 데 필요한 많은 작업이 없습니다. 우리는 단순히 데이터 바인딩 속성을 모두 가져 와서 데이터 클래스 속성으로 바꾸고 바인딩을 다음과 같이 바인딩 객체에 배치합니다.
var viewModel = new ViewModel( todos || [] ),
bindings = {
newTodo: {
value: viewModel.current,
valueUpdate: "afterkeydown",
enterKey: viewModel.add
},
taskTooltip: {
visible: viewModel.showTooltip
},
checkAllContainer: {
visible: viewModel.todos().length
},
checkAll: {
checked: viewModel.allCompleted
},
todos: {
foreach: viewModel.todos
},
todoListItem: function() {
return {
css: {
editing: this.editing
}
};
},
todoListItemWrapper: function() {
return {
css: {
done: this.done
}
};
},
todoCheckBox: function() {
return {
checked: this.done
};
},
todoContent: function() {
return {
text: this.content,
event: {
dblclick: this.edit
}
};
},
todoDestroy: function() {
return {
click: viewModel.remove
};
},
todoEdit: function() {
return {
value: this.content,
valueUpdate: "afterkeydown",
enterKey: this.stopEditing,
event: {
blur: this.stopEditing
}
};
},
todoCount: {
visible: viewModel.remainingCount
},
remainingCount: {
text: viewModel.remainingCount
},
remainingCountWord: function() {
return {
text: viewModel.getLabel(viewModel.remainingCount)
};
},
todoClear: {
visible: viewModel.completedCount
},
todoClearAll: {
click: viewModel.removeCompleted
},
completedCount: {
text: viewModel.completedCount
},
completedCountWord: function() {
return {
text: viewModel.getLabel(viewModel.completedCount)
};
},
todoInstructions: {
visible: viewModel.todos().length
}
};
....
그러나 위의 스 니펫에서 두 행이 누락되었습니다. 우리는 여전히 우리의 데이터 클래스 속성에있는 각 키를 순환하여 각각의 결과 객체를 빌드하는 getBindings
함수가 필요합니다. 바인딩 객체가 함수임을 감지하면 this
문맥을 사용하여 현재 데이터로 호출합니다. 우리의 완전한 맞춤 바인딩 공급자는 다음과 같이 보일 것입니다 :
// We can now create a bindingProvider that uses
// something different than data-bind attributes
ko.customBindingProvider = function( bindingObject ) {
this.bindingObject = bindingObject;
// determine if an element has any bindings
this.nodeHasBindings = function( node ) {
return node.getAttribute ? node.getAttribute( "data-class" ) : false;
};
};
// return the bindings given a node and the bindingContext
this.getBindings = function( node, bindingContext ) {
var result = {},
classes = node.getAttribute( "data-class" );
if ( classes ) {
classes = classes.split( "" );
//evaluate each class, build a single object to return
for ( var i = 0, j = classes.length; i < j; i++ ) {
var bindingAccessor = this.bindingObject[classes[i]];
if ( bindingAccessor ) {
var binding = typeof bindingAccessor === "function" ? bindingAccessor.call(bindingContext.$data) : bindingAccessor;
ko.utils.extend(result, binding);
}
}
}
return result;
};
};
따라서 bindings
객체의 마지막 몇 줄을 다음과 같이 정의 할 수 있습니다.
// set ko's current bindingProvider equal to our new binding provider
ko.bindingProvider.instance = new ko.customBindingProvider( bindings );
// bind a new instance of our ViewModel to the page
ko.applyBindings( viewModel );
})();
여기에서 우리가하는 일은 바인딩을 찾기 위해 사용하는 객체 (바인딩)를 받아들이는 바인딩 핸들러의 생성자를 효과적으로 정의하는 것입니다. 그러면 다음과 같이 데이터 클래스를 사용하여 애플리케이션 뷰에 대한 마크 업을 다시 작성할 수 있습니다.
<div id="create-todo">
<input id="new-todo" data-class="newTodo" placeholder="What needs to be done?" />
<span class="ui-tooltip-top" data-class="taskTooltip" style="display: none;">Press Enter to save this task</span>
</div>
<div id="todos">
<div data-class="checkAllContainer" >
<input id="check-all" class="check" type="checkbox" data-class="checkAll" />
<label for="check-all">Mark all as complete</label>
</div>
<ul id="todo-list" data-class="todos" >
<li data-class="todoListItem" >
<div class="todo" data-class="todoListItemWrapper" >
<div class="display">
<input class="check" type="checkbox" data-class="todoCheckBox" />
<div class="todo-content" data-class="todoContent" style="cursor: pointer;"></div>
<span class="todo-destroy" data-class="todoDestroy"></span>
</div>
<div class="edit'>
<input class="todo-input" data-class="todoEdit'/>
</div>
</div>
</li>
</ul>
</div>
Neil Kerkin은 위에 언급 된 것을 사용하여 완전한 TodoMVC 데모 응용 프로그램을 만들었습니다.이 데모 응용 프로그램은 여기에서 액세스하여 사용할 수 있습니다.
위의 설명에서 꽤 많은 작업처럼 보일지 모르지만 이제는 일반적인 getBindings
메소드가 작성되었으므로 간단하게 재사용하고 엄격한 데이터 바인딩보다는 데이터 클래스를 사용하여 훨씬 간단하게 대신 KnockoutJS 응용 프로그램. 최종 결과는 우리의 데이터 바인딩이 뷰에서 바인딩 객체로 옮겨지면 더 깨끗한 마크 업이 될 것입니다.
MVC Vs. MVP Vs. MVVM
MVP와 MVVM은 모두 MVC의 파생물입니다. 그것과 그것의 유래 물 사이 중요한 다름은 각 층이 다른 층에 가지고있는 의존성뿐만 아니라 어떻게 서로에 단단히 묶인 지이다.
MVC에서 뷰는 컨트롤러 옆에있는 아키텍처 위에 위치합니다. 모델은 컨트롤러 아래에 앉아 있으므로 컨트롤러와 컨트롤러가 모델에 대해 잘 알고 있습니다. 여기에서 뷰는 모델에 직접 액세스 할 수 있습니다. 그러나 전체 모델을보기에 표시하면 응용 프로그램의 복잡성에 따라 보안 및 성능 비용이 발생할 수 있습니다. MVVM은 이러한 문제를 피하려고 시도합니다.
MVP에서는 컨트롤러의 역할이 발표자로 바뀝니다. 발표자는보기와 동일한 수준에 앉아보기 및 모델의 이벤트를 듣고 둘 사이의 작업을 중재합니다. MVVM과 달리 View를 ViewModel에 바인딩하는 메커니즘이 없으므로 대신 각 View에 의존하여 Presenter가 View와 상호 작용할 수있는 인터페이스를 구현합니다.
MVVM을 사용하면 상태 및 논리 정보를 포함 할 수있는 모델의 뷰별 하위 집합을 만들 수 있으므로 전체 모델을 뷰에 표시 할 필요가 없습니다. MVP의 Presenter와 달리 ViewModel은 뷰를 참조 할 필요가 없습니다. 보기는 모델에 포함 된 데이터를 뷰에 표시하는 ViewModel의 특성에 바인딩 할 수 있습니다. 앞서 언급했듯이 뷰의 추상화는 코드 뒤에있는 로직에 필요한 로직이 적다는 것을 의미합니다.
그러나 이것에 대한 단점 중 하나는 ViewModel과 View간에 해석의 수준이 필요하며 이는 성능 비용을 가질 수 있다는 것입니다. 이 해석의 복잡성은 다양 할 수 있습니다. 데이터를 복사하는 것처럼 간단 할 수도 있고,보기를 보려는 형식으로 데이터를 조작하는 것처럼 복잡 할 수도 있습니다. MVC는 전체 모델을 쉽게 사용할 수 있고 이러한 조작을 피할 수 있으므로이 문제가 없습니다.
Backbone.js Vs. KnockoutJS
MVC, MVP 및 MVVM의 미묘한 차이점을 이해하는 것이 중요하지만 개발자는 궁극적으로 우리가 배운 것에 기반하여 KnockoutJS를 백본을 기반으로 사용해야하는지 여부를 묻습니다. 다음 메모가 도움이 될 수 있습니다.
- 두 라이브러리는 서로 다른 목표를 염두에두고 설계되었으며 종종 MVC 또는 MVVM을 선택하는 것만 큼 간단하지 않습니다.
- 데이터 바인딩과 양방향 통신이 주요 관심사 인 경우, KnockoutJS가 분명히 방법입니다. 실제로 DOM 노드에 저장된 모든 속성이나 값을이 접근 방식으로 JavaScript 객체에 매핑 할 수 있습니다.
- Backbone은 RESTful 서비스와의 통합이 용이하며, KnockoutJS 모델은 JavaScript 객체이며 모델 업데이트에 필요한 코드는 개발자가 작성해야합니다.
- KnockoutJS는 UI 바인딩 자동화에 중점을두고 있습니다. UI 바인딩을 사용하려면 Backbone을 사용하여이 작업을 수행 할 때 훨씬 더 자세한 사용자 지정 코드가 필요합니다. 이것은 의도적으로 UI에서 벗어나려고 시도 할 때 백본 자체와 관련하여 문제가되지 않습니다. 그러나 넉백은이 문제를 돕기 위해 시도합니다.
- KnockoutJS를 사용하면 우리 고유의 함수를 ViewModel observables에 바인딩 할 수 있습니다.이 함수는 관찰 가능 변경 사항이 언제든지 실행됩니다. 이를 통해 백본에서 볼 수있는 수준의 유연성을 얻을 수 있습니다.
- 백본에는 견고한 라우팅 솔루션이 내장되어 있으며, KnockoutJS는 라우팅 옵션을 제공하지 않습니다. 그러나 Ben Alman의 BBQ 플러그인 또는 Miller Medeiros의 우수한 Crossroads와 같은 독립형 라우팅 시스템을 사용하여 필요한 경우이 동작을 쉽게 채울 수 있습니다.
결론적으로, 개인적으로 KnockoutJS는 소규모 응용 프로그램에 더 적합하지만 백본의 기능 세트는 사소한 것들을 만들 때 정말로 빛난다. 즉, 많은 개발자들이 두 가지 프레임 워크를 사용하여 다양한 복잡성을 가진 응용 프로그램을 작성했으며 어느 것이 프로젝트에 가장 적합한 지 결정하기 전에 작은 규모로 시도해 보는 것이 좋습니다.
MVVM 또는 Knockout에 대한 자세한 내용은 다음 기사를 권장합니다.