vue双向绑定原理
vuejs双向绑定的实现:
双向绑定是指数据到视图的绑定和视图到数据的绑定。
简单回答:
========================================================================================
稍微复杂的回答:
视图变化引发数据变化,
直接处理对应的event handler,该更改数据变化就可以了,
主要是通过vue的指令,v-on @来注册事件,在模板解析的时候,事件会保存在VNode对象中,
虚拟DOM在patch的过程中,会根据不同的时机触发不同的钩子函数。
事件绑定updateDOMListeners的处理逻辑设置在了create与update钩子函数,也就是说在patch的过程中,当一个DOM元素被创建或更新时,都会触发事件绑定相关处理逻辑。
create与update钩子都执行updateDOMListeners函数,
调用updateListeners方法,对比on与oldOn,然后根据结果调用add或remove方法,
add方法内部就是浏览器的node.addEventListener
数据变化引发视图变化,首先我们要侦测数据的变化,
vue内部有一个observe函数,用于将数据处理成响应式数据,
(会把data属性中的数据处理成响应式数据)
对于对象的每个key,都会使用defineReactive函数处理,每个key都会有一个
Dep类实例dep(内部有一个数组)来保存他们的依赖watcher,
使用Object.defineProperty设置这些key值上数据的getter和setter,
对于对象上的一个key,
当getter被触发的时候
会向dep中添加依赖, 依赖是指Watcher类的实例watcher,当setter被触发的时候,会向watcher发送通知触发watcher内部的内容。
对于对象,使用Object.defineProperty来设置getter和setter
(在defineReactive函数内部,参数是obj,key,value…会在内部先实例化一个new Dep然后调用Object.defineProperty,会引用到初始化的dep实例,在get的时候调用dep.depend(),在set的时候调用dep.notify())
对于数组,使用数组拦截器覆盖原型上方法来获取对数组的操作。
数组收集依赖的方式和对象一样,都是在getter中收集,因为数组的位置一定也是在一个对象上,window.arr, this.arr … …
与对象用setter监测变化不同,数组使用拦截器监测了变化,
我们必须把dep保存在拦截器能触发的地方,所以保存在了observer实例上,
(所以,数组的dep在observer实例上) (!对象的key的dep是在Observer类对某一个key执行defineReactive函数时创建的一个dep, 是在 闭包中的,不能直接拿到的。 20220306补 https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js 这里observer内部的this.dep是为数组预留的 )
初始化observer实例的时候,即Observer类的内部的constructor(value)中,新增了def(value,’__ob__’,this),
此后,value的__ob__属性就对应了其observer实例, 在数组应用拦截器时候,直接调用this.__ob__ 就能获取到observer实例,
然后调用ob.dep.notify() 来通知依赖向依赖发送消息。
====================================================================
当执行let vm = new Vue(options)的时候,
1.会执行内部的_init方法,其中执行到initState方法,
会使用observe函数处理vm.data,将data处理成响应式数据。
2.如果options中有el选项,会执行$mount方法,执行mountComponent函数
在mountComponent函数内部 new Watcher(vm, updateComponent,noop..
updateComponent是一个函数,
watcher的第二个参数可以是一个表达式也可以是一个函数
如果是一个函数,会将getter设置为这个函数,并将watcher实例value初始值设置为函数执行的结果