微信小程序> Vue双向绑定实现原理(一)数据劫持

Vue双向绑定实现原理(一)数据劫持

浏览量:1608 时间: 来源:苏轻

 

1.1 数据劫持

1.1.1 如何监控一个数据

vue可以直接通过v-model这个指令来实现双向绑定,这是react和小程序都没有,小程序是单向绑定,只能将data中的对象和基本数据类型展示在视图上,却没有办法通过视图来控制data中的数据,需要通过this.setData({})给出一个对象,重新设置数据,达到视图更新。

要达到如图1-1的效果,就要对数据进行监控,只有监控了数据的变化,在数据变化之后,通知视图去自主更新,这就是双向绑定的思路。这个思路很明显涉及到“监控” “更新”两个关键词,就可以联想到观察者模式。

观察一个数据,一旦数据变化,就通知视图执行更新操作。

思路一下子就明了,数据变化还好说,就是拿出一个变量存储旧值,一旦获取到新值,新值与旧值不同时,数据就发生了变化。可问题在于,不可能随时随地对数据进行监控,每分每秒都在取得数据的值去与旧值做对比。

只有当这个数据在被使用时,我们才监控他,拿旧值与新值做对比。

这个过程叫做让数据变为可观察,是通过Object.defineProperty() 来实现。

1.1.2 如何使用Object的静态方法定义属性

Object.defineProperty(obj, prop, descriptor)
  • obj 要在其上定义属性的对象。

  • prop 要定义或修改的属性的名称。

  • descriptor 将被定义或修改的属性描述符。

被这样定义的属性,所有的数据描述符默认为false,也就是不可删除,不可写,不可枚举

属性描述符

MDN文档上有提到

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
let obj = {            name:1        }        Object.defineProperty(obj,'school',{            configurable:true,//表示configurable可以被删除            writable:true,//为true之后,便可以修改            // enumerable:true,//修改之后才可以被枚举,在遍历时被访问到            value:'zfpx'        })        // delete obj.school;        obj.school = "修改值"        console.log(obj)        for(var key in obj){            console.log(key)        }

只有开启数据描述符为true之后,属性才可被删除,被写入,被枚举打印

getter-setter

这就是数据可监控的关键,使用Object.defineProperty(obj, prop, descriptor)定义的属性,一旦属性被使用,就会被读取,就会调用get函数,一旦属性被写入,就会调用set函数,即可以知道数据一旦发生写入,变化,就可以在set函数中通知视图更新。

由于,

存储描述符get set参数和数据描述符的writeable value存在冲突,二选其一

 Object.defineProperty(obj, "school", {        configurable: true, //表示configurable可以被删除        // writable: true,         enumerable: true, //修改之后才可以被枚举,在遍历时被访问到        // value: "zfpx",        get() {          console.log("调用了get方法");          return value;        },        set(newVal) {          console.log("调用了set方法");          value = newVal;        }      });

如图 1-2

1.1.3 数据劫持

知道了get和set的妙用,就可以对数据进行劫持了。

劫持的概念

说白了,就是拿到某数据,持有这个数据,可以操作增删改,也可以不操作,重点在持有他

监听

一旦数据被传入Vue实例就需要对data整个对象实行监听,

这里需要对data中的数据类型进行判断

如果是data中的属性是基本数据类型,只需要监控就好了

如果data中的属性是对象,则需要遍历对象下的所有属性,进行监控

可又有一个疑问,data的属性是对象A,A的属性还包含对象B,B有对象C,所以不能是遍历,而是递归,递归整个对象属性树

​body    div id="app"      p姓名是{{ name.firstName }}/p      div年龄是{{ age }}/div      {{ name }}    /div    script type="vue.js"/script    script type="text/javascript"      let vm = new Vue({        el: "#app",        data: {          name: {            firstName: "姓氏章",            lastName: "名字"          },          age: 12 //通过Obj.defineProperty实现()或者Obj.defineProperties()实现        }      });    /script /body​

 数据绑定(传入{ 对象的data挂载在vue实例上)

/***Vue入口*@{options} 限定为一个对象,接受这个{}对象* */function Vue(options = {}) {this.$options = options; // 将所有属性挂载在vue实例$options上var data = (this._data = this.$options.data); //将{}对象的data挂载vue实例上observe(data);}
  /**   *观察对象变化,如果最开始传入的data是基本数据类型,已经被劫持了,不需要递归再去对属性进行监控   *@{data} 被观察的对象或属性   */  function observe(data) {    if (typeof data !== "object") return null;    return new Observe(data);  }
 class Observe {        constructor(data) {          this.start(data);        }        start(data) {          for (let key in data) {            let val = data[key];            // 如果data中包含属性是对象,则需要递归对象的中属性,进行数据劫持            // 如果data中的属性就是普通数据类型,递归退出 -- 递归出口            Object.defineProperty(data, key, {              enumrable: true,              get() {                console.log("调用get方法");                return val;              },              // 会在数据改变的时候直接设置              set(newVal) {                console.log("调用set方法");                //数据并没有改变                if (newVal === val) {                  return;                }                val = newVal;              }            });          }        }      }

上述代码实现了数据劫持和监控数据的功能

接下来是

数据代理(this代理传入的{ }对象去调用data)

编译模板(读取文本节点中的字符串,抽成属性名,通过字面量的形式访问到属性值,去掉双大括号,显示到节点上)

到编译模板这一步,是实现了单向绑定,也就是data中的数据被显示在网页上,如同又通过视图,譬如input输入框,改变data的值。

往后看,谢谢。

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎