数据绑定

  数据绑定一般就是指的 将数据 展示到 视图上。目前前端的框架都是使用的mvvm模式实现双绑的。大体上有以下几种方式:

  1.  发布订阅
  2. ng的脏检查
  3. 数据劫持

  vue的话采用的是数据劫持和发布订阅相结合的方式。 而数据劫持用的是Object.defineProperty来实现的, 可以通过绑定get和set来在获取和设置数据的时候触发相应的函数。

实现

  所以我们需要一个监听器Observe来监听数据的变化。在数据发生变化时,我们需要一个Watcher订阅者来更新视图,我们还需要一个指令的解析器compile来解析指令和初始化视图。

    • Observe 监听器: 监听数据的变化, 通知订阅者
    • Watcher 订阅者: 收到数据的变化, 更新视图
    • Compile 解析器: 解析指令,初始化模板,绑定订阅者

  Observe

    监听数据的每一个属性, 由于可能会有多个watcher,所以需要一个容器来存储。

    function Sub() {
this.subs = [];
}
Sub.prototype = {
add(sub) {
this.subs.push(sub);
},
trigger() {
this.subs.forEach(sub => {
sub.update();
})
}
};
Sub.target = null; function observe(data) {
if (typeof data !== 'object' || !data) return;
Object.keys(data).forEach(item => {
let val = data[item];
let sub = new Sub();
Object.defineProperty(data, item, {
enumerable: true,
configurable: false,
get() {
if (Sub.target) {
sub.add(Sub.target);
}
return val;
},
set(newVal) {
val = newVal;
sub.trigger();
}
})
})
}

  

  Watcher

    将对应属性的watcher添加到sub容器中, 当属性变化时,执行更新函数。

    function Watcher(vm, prop, callback) {
this.vm = vm;
this.prop = prop;
this.callback = callback;
Sub.target = this;
let val = this.vm.$data[prop];
Sub.target = null;
this.vaule = val;
} Watcher.prototype.update = function () {
let newValue = this.vm.$data[this.prop];
if (this.value !== newValue) {
this.value = newValue;
this.callback.call(this.vm, newValue);
}
}

  

  Compile

    获取到dom中的指令和初始化模板, 添加watcher更新视图。

    function Compile(vm) {
this.vm = vm;
this.el = vm.$el;
this.init();
} Compile.prototype.init = function () {
let fragment = document.createDocumentFragment();
let child = this.el.firstChild;
while(child) {
fragment.append(child);
child = this.el.firstChild;
}
let childNodes = fragment.childNodes;
Array.from(childNodes).forEach(node => {
if (node.nodeType === 1) {
let attrs = node.attributes;
Array.from(attrs).forEach(attr => {
let name = attr.nodeName;
if (name === 'v-model') {
let prop = attr.nodeValue;
let value = this.vm.$data[prop];
node.value = value;
new Watcher(this.vm, prop, val => {
node.value = val;
});
node.addEventListener('input', e => {
let newVal = e.target.value;
if (value !== newVal) {
this.vm.$data[prop] = newVal;
}
})
}
})
} let reg = /\{\{(.*)\}\}/;
let text = node.textContent;
if (reg.test(text)) {
let prop = RegExp.$1;
let val = this.vm.$data[prop];
node.textContent = val;
new Watcher(this.vm, prop, val => {
node.textContent = val;
});
}
})
this.el.appendChild(fragment);
}

到这里, 基本的思路已经实现完毕, 这里只实现了v-model指令。

  最后,结合 Observe Watcher和 Compile, 就可以成为一个完整的mvvm了。

    <div id="app">
<div>{{val}}</div>
<input type="text" id="input" v-model="val">
</div> <script>
function MyVue(options) {
this.$options = options;
this.$el = options.el;
this.$data = options.data;
this.init();
} MyVue.prototype.init = function () {
observe(this.$data);
new Compile(this);
}; new MyVue({
el: document.getElementById('app'),
data: {
val: 123
}
})
</script>

 当然,这只是简单的实现,没考虑细节,主要是学习思路。

 查看效果 (代码直接在页面上)

    

  

  

自己手动实现简单的双向数据绑定 mvvm的更多相关文章

  1. 简单实现双向数据绑定mvvm。

  2. JavaScript实现简单的双向数据绑定

    什么是双向数据绑定 双向数据绑定简单来说就是UI视图(View)与数据(Model)相互绑定在一起,当数据改变之后相应的UI视图也同步改变.反之,当UI视图改变之后相应的数据也同步改变. 双向数据绑定 ...

  3. 五十行javascript代码实现简单的双向数据绑定

    五十行javascript代码实现简单的双向数据绑定 Vue框架想必从事前端开发的同学都使用过,它的双向数据绑定机制能给我们带来很大的方便.今天闲着没事,尝试着实现一下双向数据绑定,接下来给大家分享一 ...

  4. 利用ES6中的Proxy和Reflect 实现简单的双向数据绑定

    利用ES6中的Proxy (代理) 和 Reflect 实现一个简单的双向数据绑定demo. 好像vue3也把 obj.defineProperty()  换成了Proxy+Reflect. 话不多说 ...

  5. 实现双向数据绑定mvvm

    实现双向数据绑定mvvm  

  6. Angular双向数据绑定MVVM以及基本模式分析

    MVVM: angular的MVVM实现的是双向数据绑定,模型从服务器端抓取到数据,将数据通过控制器(controller)传递到视图(view)显示,视图数据发生变化时同样也会影响到模型数据的变化, ...

  7. React简单实现双向数据绑定

    import React, { Component } from 'react' import ReactDOM from 'react-dom' class App extends Componen ...

  8. 原生js简单实现双向数据绑定原理

    根据对象的访问器属性去监听对象属性的变化,访问器属性不能直接在对象中设置,而必须通过 defineProperty() 方法单独定义. 访问器属性的"值"比较特殊,读取或设置访问器 ...

  9. 深入vue源码,了解vue的双向数据绑定原理

    大家都知道vue是一种MVVM开发模式,数据驱动视图的前端框架,并且内部已经实现了双向数据绑定,那么双向数据绑定是怎么实现的呢? 先手动撸一个最最最简单的双向数据绑定 <div> < ...

随机推荐

  1. [ 剑指Offer ] Week2 学习笔记

    打印从1到最大的n位数 题解:由于未知n的大小,需要考虑大数问题.在这样的情况下,逐位地将字符串转换为数字输出,不会有溢出的可能.使用全排列的方式列出所有数字,省去了需要考虑进位的可能. 初始化数组, ...

  2. error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation

    遇到这个问题,请打开项目的Properties(属性)------->Configuration Properties(配置属性)------>C/C++ ------>Prepro ...

  3. java中内部类的实例化

  4. C语言程序设计-现代方法(笔记3)

    第十三章 字符串 1.字符串字面量(13.1) 字符串字面量:用一对双引号括起来的字符序列.字符串字面量可以像字符常量一样包含转义字序列. 在字符串字面量中小心使用八进制和十六进制的转义序列. 字符串 ...

  5. 用python发送qq邮件

    一.需要开启smtp服务,获取授权密码. 在qq邮箱的设置里开启smtp 二.代码 # -*- coding:utf-8 -*- import smtplib from email.mime.text ...

  6. win32下的命令行集合 (最优秀的工具)

    HIDECMD.rar下载:以隐藏窗口的方式运行批处理. curl.exe 7.12.2 文件传输 593,670 curl是一个利用URL语法在命令行方式下工作的的文件传输工具 E6ED60CDA8 ...

  7. 搜索栏UISearchBar的使用

    本文结构: 1.首先是对UISearchBar的简介文字 2.初始化展现UISearchBar,并解析它的结构 3.属性.方法.代理等的一一介绍 4.日常的使用,包括单独对UISearchBar的配置 ...

  8. 微软结合Veritas,使用Azure云中的混合存储

    导读 在与Symantec(Symantec)脱离10年(通常令人不愉快的关系)一周年之际,Veritas Technologies在2月22日宣布与Microsoft Corp. 建立了多年战略全球 ...

  9. day14-Python运维开发基础(内置函数、pickle序列化模块、math数学模块)

    1. 内置函数 # ### 内置函数 # abs 绝对值函数 res = abs(-10) print(res) # round 四舍五入 (n.5 n为偶数则舍去 n.5 n为奇数,则进一!) 奇进 ...

  10. 「NOI2009」二叉查找树

    传送门 Luogu 解题思路 看一眼题面,显然这是一颗 treap ,考虑到这棵 treap 的中序遍历总是不变的,所以我们就先把所有点按照数据值排序,求出 treap 的中序遍历,然后还可以观察到, ...