JavaScript 데이터 바인딩은 간단한 MVVM library_javascript 기술을 구현합니다.

WBOY
풀어 주다: 2016-05-16 15:06:02
원래의
1557명이 탐색했습니다.

추천 도서:

아주 간단한 js 양방향 데이터 바인딩 구현

MVVM은 웹 프런트엔드에 매우 인기 있는 개발 모델입니다. MVVM을 사용하면 DOM 작업에 신경쓰는 대신 비즈니스 로직 처리에 코드를 더 집중할 수 있습니다. 현재 유명한 MVVM 프레임워크로는 vue, avalon, React 등이 있습니다. 이러한 각 프레임워크에는 고유한 장점이 있지만 구현 아이디어는 데이터 바인딩 + 뷰 새로 고침이라는 거의 동일합니다. 호기심과 수정하려는 의지로 이 방향을 따라 가장 간단한 MVVM 라이브러리(mvvm.js)도 작성했는데, 총 2,000줄이 넘는 코드 이름과 사용법이 vue와 유사합니다. 여기서 공유해 보세요. 구현 원리와 코드 구성 아이디어에 대해 이야기해 보겠습니다.

아이디어 정리

MVVM은 개념적으로 뷰와 데이터 로직을 완전히 분리하는 패턴이며, ViewModel은 전체 패턴의 핵심입니다. ViewModel을 구현하려면 데이터 모델(Model)을 뷰(View)와 연결해야 합니다. 전체 구현 아이디어는 간단히 5가지로 요약됩니다.

컴파일러를 구현하여 요소의 각 노드에 대한 명령을 검색하고 추출합니다.

요소에 대한 명령어를 구문 분석하고 노드 구문 분석과 같이 새로 고침 기능을 통해 명령어의 의도를 DOM에 업데이트하는 파서를 구현합니다(중간에는 뷰 새로 고침을 담당하는 모듈이 필요할 수 있음). p v-show=" isShow">

먼저 모델에서 isShow 값을 얻은 다음 isShow에 따라 node.style.display를 변경하여 요소의 표시 및 숨기기를 제어합니다.

Parser의 각 명령어의 새로 고침 기능을 해당 모델의 필드와 연결하는 Watcher를 구현합니다.

객체의 모든 필드 값 변경을 모니터링하는 관찰자를 구현합니다. 변경이 발생하면 최신 값을 가져오고 알림 콜백을 트리거할 수 있습니다.

감시자에서 모델의 모니터링을 설정하려면 관찰자가 새 값을 얻은 후 데이터를 실현하기 위해 2단계에서 새로 고침 기능을 호출합니다. .변경 시 뷰를 새로 고치는 목적입니다.

효과예

먼저 다른 MVVM 프레임워크의 인스턴스화와 유사한 최종 사용 예를 간단히 살펴보세요.

<div id="mobile-list">
<h1 v-text="title"></h1>
<ul>
<li v-for="item in brands">
<b v-text="item.name"></b>
<span v-show="showRank">Rank: {{item.rank}}</span>
</li>
</ul>
</div>
var element = document.querySelector('#mobile-list');
var vm = new MVVM(element, {
'title' : 'Mobile List',
'showRank': true,
'brands' : [
{'name': 'Apple', 'rank': 1},
{'name': 'Galaxy', 'rank': 2},
{'name': 'OPPO', 'rank': 3}
]
});
vm.set('title', 'Top 3 Mobile Rank List'); // => <h1>Top 3 Mobile Rank List</h1>
로그인 후 복사

모듈분할

MVVM을 5개의 모듈로 나누어 구현했습니다. 컴파일 모듈 Compiler, 구문 분석 모듈 Parser, 뷰 새로 고침 모듈 Updater, 데이터 구독 모듈 Watcher 및 데이터 청취 모듈 Observer. 프로세스는 다음과 같이 간략하게 설명할 수 있습니다. 컴파일러는 명령어를 컴파일한 후 구문 분석을 위해 명령어 정보를 파서로 전달합니다. 파서는 초기 값을 업데이트하고 데이터 변경을 위해 감시자를 구독합니다. 그런 다음 Watcher에 다시 피드백합니다. 그런 다음 Updater는 해당 새로 고침 기능을 찾아 뷰를 새로 고칩니다.

위의 과정을 그림으로 나타내면

다음은 이 5개 모듈 구현의 기본 원칙을 소개합니다. (코드의 핵심 부분만 게시되어 있습니다. 전체 구현을 읽으려면 내 Github에 가세요.)

1. 컴파일러 모듈 컴파일러

컴파일러의 주요 역할은 요소의 각 노드에 대한 명령을 검색하고 추출하는 것입니다. 컴파일 및 구문 분석 프로세스는 전체 노드 트리를 여러 번 통과하므로 컴파일 효율성을 높이기 위해 요소는 먼저 MVVM 생성자 내부의 문서 조각 형태로 복사본 조각으로 변환됩니다. 모든 노드가 컴파일된 후 문서 조각이 원래 실제 노드에 다시 추가되어서는 안 됩니다.

vm.complieElement는 요소의 모든 노드에 대한 검색 및 명령어 추출을 구현합니다.

vm.complieElement = function(fragment, root) {
var node, childNodes = fragment.childNodes;
// 扫描子节点
for (var i = 0; i < childNodes.length; i++) {
node = childNodes[i];
if (this.hasDirective(node)) {
this.$unCompileNodes.push(node);
}
// 递归扫描子节点的子节点
if (node.childNodes.length) {
this.complieElement(node, false);
}
}
// 扫描完成,编译所有含有指令的节点
if (root) {
this.compileAllNodes();
}
}
로그인 후 복사

vm.compileAllNodes 메소드는 this.$unCompileNodes의 각 노드를 컴파일합니다(명령 정보를 Parser에 전달). 노드를 컴파일한 후 캐시 대기열에서 제거하고 this.$unCompileNodes를 확인합니다. = 0이면 모든 컴파일이 완료되고 문서 조각이 실제 노드에 추가될 수 있음을 의미합니다.

2. 명령어 파싱 모듈 파서

컴파일러는 각 노드의 명령어를 추출하면 파서로 분석할 수 있습니다. 각 명령어에는 서로 다른 구문 분석 방법이 있습니다. 모든 명령어의 구문 분석 방법은 두 가지 작업만 수행하면 됩니다. 하나는 뷰(초기 상태)에 대한 데이터 값을 업데이트하는 것이고, 다른 하나는 새로 고침 기능을 변경 사항 모니터링에 등록하는 것입니다. 모델. 여기서는 명령어의 일반적인 구문 분석 방법을 설명하기 위해 v-text 구문 분석을 예로 들어 보겠습니다.

parser.parseVText = function(node, model) {
// 取得 Model 中定义的初始值 
var text = this.$model[model];
// 更新节点的文本
node.textContent = text;
// 对应的刷新函数:
// updater.updateNodeTextContent(node, text);
// 在 watcher 中订阅 model 的变化
watcher.watch(model, function(last, old) {
node.textContent = last;
// updater.updateNodeTextContent(node, text);
});
}
로그인 후 복사

3. 数据订阅模块 Watcher

上个例子,Watcher 提供了一个 watch 方法来对数据变化进行订阅,一个参数是模型字段 model 另一个是回调函数,回调函数是要通过 Observer 来触发的,参数传入新值 last 和 旧值 old , Watcher 拿到新值后就可以找到 model 对应的回调(刷新函数)进行更新视图了。model 和 刷新函数是一对多的关系,即一个 model 可以有任意多个处理它的回调函数(刷新函数),比如: v-text="title" 和 v-html="title" 两个指令共用一个数据模型字段。

添加数据订阅 watcher.watch 实现方式为:

watcher.watch = function(field, callback, context) {
var callbacks = this.$watchCallbacks;
if (!Object.hasOwnProperty.call(this.$model, field)) {
console.warn('The field: ' + field + ' does not exist in model!');
return;
}
// 建立缓存回调函数的数组
if (!callbacks[field]) {
callbacks[field] = [];
}
// 缓存回调函数
callbacks[field].push([callback, context]);
}
로그인 후 복사

当数据模型的 field 字段发生改变时,Watcher 就会触发缓存数组中订阅了 field 的所有回调。

4. 数据监听模块 Observer

Observer 是整个 mvvm 实现的核心基础,看过有一篇文章说 O.o (Object.observe) 将会引爆数据绑定革命,给前端带来巨大影响力,不过很可惜,ES7 草案已经将 O.o 给废弃了!目前也没有浏览器支持!所幸的是还有 Object.defineProperty 通过拦截对象属性的存取描述符(get 和 set) 可以模拟一个简单的 Observer :

// 拦截 object 的 prop 属性的 get 和 set 方法
Object.defineProperty(object, prop, {
get: function() {
return this.getValue(object, prop);
},
set: function(newValue) {
var oldValue = this.getValue(object, prop);
if (newValue !== oldValue) {
this.setValue(object, newValue, prop);
// 触发变化回调
this.triggerChange(prop, newValue, oldValue);
}
}
});
로그인 후 복사

然后还有个问题就是数组操作 ( push, shift 等) 该如何监测?所有的 MVVM 框架都是通过重写该数组的原型来实现的:

observer.rewriteArrayMethods = function(array) {
var self = this;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methods = 'push|pop|shift|unshift|splice|sort|reverse'.split('|');
methods.forEach(function(method) {
Object.defineProperty(arrayMethods, method, function() {
var i = arguments.length;
var original = arrayProto[method];
var args = new Array(i);
while (i--) {
args[i] = arguments[i];
}
var result = original.apply(this, args);
// 触发回调
self.triggerChange(this, method);
return result;
});
});
array.__proto__ = arrayMethods;
}
로그인 후 복사

这个实现方式是从 vue 中参考来的,觉得用的很妙,不过数组的 length 属性是不能够被监听到的,所以在 MVVM 中应避免操作 array.length

5. 视图刷新模块 Updater

Updater 在五个模块中是最简单的,只需要负责每个指令对应的刷新函数即可。其他四个模块经过一系列的折腾,把最后的成果交给到 Updater 进行视图或者事件的更新,比如 v-text 的刷新函数为:

updater.updateNodeTextContent = function(node, text) {
node.textContent = text;
}
로그인 후 복사

v-bind:style 的刷新函数:

updater.updateNodeStyle = function(node, propperty, value) {
node.style[propperty] = value;
}
로그인 후 복사

双向数据绑定的实现

表单元素的双向数据绑定是 MVVM 的一个最大特点之一:

其实这个神奇的功能实现原理也很简单,要做的只有两件事:一是数据变化的时候更新表单值,二是反过来表单值变化的时候更新数据,这样数据的值就和表单的值绑在了一起。

数据变化更新表单值利用前面说的 Watcher 模块很容易就可以做到:

watcher.watch(model, function(last, old) {
input.value = last;
});'
로그인 후 복사

表单变化更新数据只需要实时监听表单的值得变化事件并更新数据模型对应字段即可:

var model = this.$model;
input.addEventListenr('change', function() {
model[field] = this.value;
});‘
로그인 후 복사

其他表单 radio, checkbox 和 select 都是一样的原理。

以上,整个流程以及每个模块的基本实现思路都讲完了,第一次在社区发文章,语言表达能力不太好,如有说的不对写的不好的地方,希望大家能够批评指正!

结语

折腾这个简单的 mvvm.js 是因为原来自己的框架项目中用的是 vue.js 但是只是用到了它的指令系统,一大堆功能只用到四分之一左右,就想着只是实现 data-binding 和 view-refresh 就够了,结果没找这样的 javascript 库,所以我自己就造了这么一个轮子。

虽说功能和稳定性远不如 vue 等流行 MVVM 框架,代码实现可能也比较粗糙,但是通过造这个轮子还是增长了很多知识的 ~ 进步在于折腾嘛!

目前我的 mvvm.js 只是实现了最本的功能,以后我会继续完善、健壮它,如有兴趣欢迎一起探讨和改进~

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿