js实现数据单向绑定

若是你在学习一种前端框架,如vue、angular等,那么你必定不会对数据的单向绑定陌生。html

何为数据的单向绑定?

传统开发模式下,如使用jQuery开发,咱们想将一个变量显示到html中,首先要定义一个变量name,而后经过jq代码操做dom将变量放到HTML中,若是name发生修改,还要再次经过jq代码操做dom将新的变量值放到HTML中。这就是传统的MVC框架,其中的Model和View是咱们经过代码联系在一块儿的。
在MVVM框架中,咱们再也不过多的关注数据与视图间的操做,而是使用一种新的机制,数据的单/双向绑定。
我在刚接触angular的时候感受这个绑定的机制简直不要再神奇,可是当慢慢深刻学习后发现其实原理很是简单,经过简短的js代码就能够实现一个简单的数据单向绑定。前端

Proxy对象

Proxy能够当作在目标对象以前架设一层拦截,或者说是代理。任何对该对象的访问都要先通过这个代理。那么也就是说咱们能够经过Proxy对象拦截到外界对一个对象的访问。
ES6中将Proxy标准化了,提供了Proxy构造函数,用来生成Proxy实例。下面就是官方的定义:vue

let p = new Proxy(target, handler);
  • target:用Proxy包装的目标对象(能够是任何类型的对象,包括原生数组,函数,甚至另外一个代理)
  • handler:一个对象,其属性是当执行一个操做时定义代理的行为的函数。

再看一个栗子:数组

let p = new Proxy({}, {
    get: function(target, name){
        return name in target ? target[name] : 37;
    }
});
p.a = 1;
p.b = undefined;
console.log(p.a, p.b);    // 1, undefined
console.log('c' in p, p.c);    // false, 37

栗子中经过Proxy对象拦截了p对象的访问,当对象中不存在属性名时返回37,若是有就返回属性值。
到这已经了解了Proxy对象的工做方式,咱们就要用Proxy来作点事了。前端框架

单向绑定

首先
若是你接触过一些mvvm框架,那么必定会对下面的代码很是熟悉app

<div id="app">
  姓名:{{name}}
  <br> 年龄:{{age}}
</div>

“{{}}”叫作插值表达式,vue、angular中都是使用这种方式进行数据的单向绑定。在这咱们也使用这种格式绑定数据。
而后框架

let el = document.getElementById('app');
let template = el.innerHTML;

上面两步,首先获取到根元素,而后将根元素下的html保存下来。这里为何要保存原始的html呢?
由于接下来的数据变化会从新编译标签,既然要从新编译,那么就要保留最初的状态,不然编译一次后,第二次就没法正常编译了。
再而后dom

let _data = {
  name: '_BuzzLy',
  age: 25
};

定义一个_data对象,这个对象是给咱们接下来建立的Proxy对象用的,并非暴露出去供访问的。
接下来mvvm

let data = new Proxy(_data, {
  // 试图设置数据时调用
  // 参数:_data,属性名,值
  set(obj, name, value) {
    obj[name] = value;

    // 数据变了
    console.log(`数据变了,设置 ${name}=>${value}`);
    // 数据改变后从新渲染
    render();
  },
  // 试图获取数据的时调用,默认要什么就返回什么
  // get() {}
});

这步咱们建立一个data,这个data就是对外的,是一个Proxy对象。
Proxy是原生的对象,能够将真正的数据对象隐藏,咱们修改的是代理对象。
至关于咱们想修改_data必定要通过一步代理,告诉代理咱们要修改的对象及修改的值,而后代理去帮咱们操做。
在修改完成后调用render函数去从新渲染视图。
最后
上面已经完成了最重要的部分,当一个数据发生改变后调用了render函数,这个函数就是将改变后的数据从新渲染到视图中去。函数

function render() {
    el.innerHTML = template.replace(/\{\{w+\}\}/g, str => {
        console.log(str); // 匹配出来的 {{name}}  {{age}} 
        // 截取字符串,获得属性key值
        str = str.substring(2, str.length - 2);
        // 从真实数据中拿到对应属性的值返回,替换{{key}}
        return _data[str];
    })
}

这里的实现很简单,就是匹配到html中{{key}},而后拿到key,再去_data中找到值便可。
注意这里的template,这就是咱们在上面为何要保存一份原始的html缘由。
如今你能够去尝试手动修改data的值看看效果了。

data.name = 'halo wode';
data.age = 18;

到这一个最简单的数据单向绑定就实现了。


完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <div id="app">
    姓名:{{name}}
    <br> 年龄:{{age}}
  </div>
</body>
<script>
  let el = document.getElementById('app');
  let template = el.innerHTML;
  let _data = {
    name: '_BuzzLy',
    age: 25
  };

  let data = new Proxy(_data, {
    set(obj, name, value) {
      obj[name] = value;
      render();
    }
  });
  
  render();

  function render() {
    el.innerHTML = template.replace(/\{\{\w+\}\}/g, str => {
      str = str.substring(2, str.length - 2);
      return _data[str];
    });
  }
</script>
</html>
相关文章
相关标签/搜索