如何实现双向绑定?底层原理是什么?

如何实现双向绑定?底层原理是什么?1 如何实现双向绑定 以用户提交表单为例 其原理是我们对 input 进行 value 的属性绑定 v bind value 将 Model 中的变量绑定到 View 上 Model gt View 以及当用户对 input 进行操作时 进行事件监听 v on input

大家好,我是讯享网,很高兴认识大家。
1、如何实现双向绑定?

以用户提交表单为例,其原理是我们对input进行value的属性绑定(v-bind:value="…"),将Model中的变量绑定到View上(Model -> View),以及当用户对input进行操作时,进行事件监听(v-on: input =" … "),从而实现双向数据绑定。v-model实际上是语法糖,结合了上述两个操作。

2、底层原理是什么?

参考:Vue的MVVM是如何实现的
如何追踪数据变化?
Vue将遍历传入Vue实例data选项中的js对象的所有property,并使用Object.defineProperty 把这些 property 全部转为getter/setter。这些getter/setter可使property在被访问和修改时通知变更。每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据property记为依赖,之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。

(1) 通过Observer来监听自己Model的数据变化: Obeject.defineProperty()来监听属性变动,将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter,同时创建一个消息订阅器Dep用来收集订阅者,数据变动之后触发notify,再调用订阅者的update方法
(2) 通过Complie来解析编译模板指令: 对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。(每次找到一个数据替换,都要重新渲染一遍,可能会造成页面的回流和重绘,那么我们最好的办法就是把以上的元素放在内存中,在内存中操作完成之后,再替换掉.)
(3) 通过Watcher搭起Observer 和Compile 之间的通信桥梁:能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图,达到 数据变化 -> 视图更新,视图交互变化 -> 数据model变更。什么时候添加绑定watcher? 当订阅数据变化时,来绑定更新函数,从而让watcher更新视图
在这里插入图片描述
讯享网

(1)观察者模式和发布订阅者模式的区别

在这里插入图片描述
观察者模式:在观察者模式里,被观察者subject只需要维护一套观察者observer的集合,这些observer实现相同的接口,subject只需要知道,通知observer时,需要调用哪个方法就好了。观察者模式是由具体目标调度的,其订阅者与发布者之间存在依赖。
发布订阅者模式: :发布订阅模式里,不仅仅只有发布者和订阅者两个角色,还存在一个调度中心,当事件触发时,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。发布/订阅模式是统一由调度中心调的,其订阅者与发布者之间不存在依赖

(2)Object.defineProperty可以对数组对象数据进行劫持吗?数组对象如何实现数据的响应更新?

参考:深入理解 Object.defineProperty 及实现数据双向绑定

Object.defineProperty(obj, prop, descriptor); 

讯享网

descriptor由两部分组成:数据描述符(configurable,enumerable,value 及 writable 配置项)和访问器描述符(configurable,enumerable,get以及set),即使用访问器描述符中 getter或 setter方法的话,不允许使用 writable 和 value 这两个配置项。

ans:

  1. 当我们使用 Object.defineProperty 对数组赋值有一个新对象的时候,会执行set方法,但是当我们改变数组中的某一项值的时候,或者使用数组中的push等其他的方法,或者改变数组的长度,都不会执行set方法
  2. 通过重写 Array.property.push方法,并且生成一个新的数组赋值给数据,这样数据双向绑定就触发了
    重新编写数组的方法:
讯享网const arrPush = {}; // 如下是 数组的常用方法 const arrayMethods = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]; // 对数组的方法进行重写 arrayMethods.forEach((method) => { const original = Array.prototype[method]; arrPush[method] = function() { console.log(this); return original.apply(this, arguments); } }); const testPush = []; // 对 testPush 的原型 指向 arrPush,因此testPush也有重写后的方法 testPush.__proto__ = arrPush; testPush.push(1); // 打印 [], this指向了 testPush testPush.push(2); // 打印 [1], this指向了 testPush 
(3)Object.defineProperty和Proxy的区别
let p = new Proxy(target, handler) 
  1. Object.defineProperty()的主要问题
    • 不能监听数组的变化
    • 必须遍历对象的每个属性
    • 必须深层遍历嵌套的对象
  2. Proxy
    • 针对对象:针对整个对象,而不是对象的某个属性。相比于Object.defineProperty(),省了一个 Object.keys() 的遍历
    • 支持数组:不需要对数组的方法进行重载
    • 嵌套支持:和 Object.defineProperty() 是一样的,也需要通过逐层遍历来解决。Proxy 的写法是在 get 里面递归调用 Proxy 并返回
  3. Proxy的优劣势
    • 优势:Proxy 的第二个参数可以有 13 种拦截方法,比 Object.defineProperty() 要更加丰富
    • 劣势:Proxy 的兼容性不如 Object.defineProperty(), 不能使用 polyfill 来处理兼容性
小讯
上一篇 2025-03-25 07:10
下一篇 2025-02-26 11:51

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/116131.html