vue实现双向数据绑定
本文实例为大家分享了vue实现双向数据绑定的具体代码,供大家参考,具体内容如下
vue中数组与对象采用了不同的绑定方式
1.vue对象数据绑定
(1)数据侦测
在js中,我们使用Object.defineProperty()和ES6的proxy来对对象进行侦测
在vue2.x中使用的是Object.defineProperty()来对对象进行数据侦测,我们首先对Object.defineProperty进行封装,有如下的代码:
function defineReactive(data, key, val){
if(typeof val === 'object') new Observer(val)
let dep = new Dep();
Object.defineProperty(data, key, val) {
enumerable: true,
configurable: true,
get: function () {
dep.depend();
return val;
},
set: function() {
if(val === newVal) {
return ;
}
val = newVal;
dep.notify()
}
}
}
需要传递的参数只有data, key, val,每当从data中的key中读取数据的时候触发get,修改的数据的时候触发set
(2)依赖收集
先收集依赖,等到属性发生改变的时候,再把收集到的依赖循环触发一遍就好了,在getter中收集依赖,在setter中触发依赖
依赖类Dep
export default class Dep {
constructor() {
this.subs = []
}
addSub() {
this.subs.push(sub)
}
removeSub(sub) {
remove(this.subs, sub)
}
depend() {
if(window.target) {
this.addSub(window.target)
}
}
notify() {
const subs = this.subs.slice()
for(let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
function remove(arr, item) {
if(arr.length) {
const index = arr.indexOf(item)
if(index > -1) {
return arr.splice(index, 1)
}
}
}
watcher类是我们收集的依赖
export default class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
window.target = this
let value = this.getter.call(this.vm, this.vm)
window.target = undefined
return value
}
update() {
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
(3)递归侦测所有key (Observer)
export class Observer {
constructor(value) {
this.value = value;
if(!Array.isArray(value)) {
this.walk(value)
}
}
walk(obj) {
const keys = Object.keys(obj)
for(let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
Observer类就是将对象的所有属性变为响应式的
2.Array的变化侦测
(1)数组中追踪变化用的是拦截器覆盖原型方法
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto);
//和数组原型一样的对象作为拦截器
['push','pop','shift','unshift','splice','sort','reverse'].forEach(function (method) {
const original = arrayProto[method]
Object.defineProperty(arrayMethods, method, {
value: function mutator(...args) {
return original.apply(this, args)
},
enumerable: false,
writable: true,
configurable: true
})
})
拦截器覆盖原型只有一句话
value.__proto__ = arrayMethods
如果没有__proto__属性,vue将会把这些arrayMethods挂载到被侦测的数组上
数组与对象类似,都是在getter中收集依赖,而数组触发依赖实在拦截器里面
数组的依赖保存在Observer实例上,依赖必须是getter和拦截器中都可以访问到的
__ob__是不可枚举属性,这个属性的值就是当前Observer实例
把Dep实例保存在Observer的属性上,如果value已经有__ob__属性,则不需要重复创建Observer实例,避免重复侦测value的变化
像数组的依赖发送通知
this.__ob__.dep.notify();
(2).侦测数据变化的具体方法
循环数组中的每一项,执行observe函数来侦测变化
observeArray(items) {
for(let i = 0; l = items.length; i < l; i++) {
observe(items[i]);
}
}
数组需要侦测新的元素
通过拦截push,unshift,splice等方法,并且把args存储在inserted中
if(inserted) ob.observeArray(inserted)
总结:
Array追踪变化的方式和Object不一样,所以我们通过创建拦截器去覆盖数组原型的方式来追踪变化,为了不污染全局Array.prototype,我们在Observer中只针对需要侦测变化的数组使用__proto__来覆盖原型。
Array收集依赖的方式和Object一样,都在getter中收集,在拦截器中触发,依赖保存在Observer实例上。在Observer上,我们每个侦测的数据都标记上了__ob__,并把this(Observer)保存在__ob__上,主要是为了保证同一数据只被侦测一次,另外可以很方便的通过__ob__从而拿到Observer实例上保存的依赖。数组需要循环把每一个数组项都变成响应式的,当数组中新增元素的时候,我们把参数提取出来,然后使用observeArray对新增的数据进行变化侦测,对于数组,只能拦截原型方法,对于一些特有的方法就无法拦截。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
vue中watch监听器用法之deep、immediate、flush
Vue是可以监听到多层级数据改变的,且可以在页面上做出对应展示,下面这篇文章主要给大家介绍了关于vue中watch监听器用法之deep、immediate、flush的相关资料,需要的朋友可以参考下2022-09-09
Vue2 特殊符号让人傻傻分不清 “:”、“.”、“@”、“#” 、“{{}}“ 、“$“,‘$bus‘,‘$e
:”是指令 “v-bind”的缩写“.”是修饰符 “@”是指令“v-on”的缩写 ,它用于监听 DOM 事件 “#”是v-slot的缩写,这篇文章主要介绍了Vue2 新手上路无处不在的特殊符号让人傻傻分不清“:”、“.”、“@”、“#” 、“{{}}“ 、“$“,$bus,$event,需要的朋友可以参考下2024-08-08
vue Element UI扩展内容过长使用tooltip显示
这篇文章主要为大家介绍了vue Element UI扩展内容过长使用tooltip展示鼠标hover时的提示信息,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2022-06-06
uniapp实现省市区三级级联选择功能(含地区json文件)
这篇文章主要给大家介绍了关于uniapp实现省市区三级级联选择功能(含地区json文件)的相关资料,级级联是一种常见的网页交互设计,用于省市区选择,它的目的是方便用户在一系列选项中进行选择,并且确保所选选项的正确性和完整性,需要的朋友可以参考下2024-06-06
this.$router.push携带参数跳转页面的实现代码
这篇文章主要介绍了this.$router.push携带参数跳转页面,this.$router.push进行页面跳转时,携带参数有params和query两种方式,本文结合实例代码给大家详细讲解,需要的朋友可以参考下2023-04-04


最新评论