当前位置 : 主页 > 编程语言 > 其它开发 >

Vue 2.x 响应式原理(一)

来源:互联网 收集:自由互联 发布时间:2022-06-06
前期准备工作 数据驱动 响应式的核心原理 发布订阅者模式和观察者模式 数据驱动 数据响应式 数据模型仅仅是普通的 JavaScript 对象,而当我们修改数据时,视图会进行更新,避免了繁
前期准备工作
  1. 数据驱动
  2. 响应式的核心原理
  3. 发布订阅者模式和观察者模式
数据驱动
  • 数据响应式
    • 数据模型仅仅是普通的 JavaScript 对象,而当我们修改数据时,视图会进行更新,避免了繁琐的 DOM 操作,提高开发效率
  • 双向绑定
    • 数据改变,视图改变;视图改变,数据也随之改变
    • 我们可以使用 v-model 在表单元素上创建双向数据绑定
  • 数据驱动是 Vue 最独特的特性之一
    • 开发过程仅需要关注数据本身,不需要关心数据是如何渲染到视图
Vue 2.x 简单响应式例子

主要是利用 ES5 Object.defineProperty,看代码

// 模拟 Vue 中的 data 选项
let data = { msg: 'hello' }

// 模拟 Vue 的实例
let vm = {}

// 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作

Object.defineProperty(vm, 'msg', {
	// 可枚举(可遍历)
	enumerable: true,
	// 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
	configurable: true,
	// 当获取值的时候执行
	get() {
		console.log('get: ', data.msg)
		return data.msg
	},
	// 当设置值的时候执行
	set(newValue) {
		console.log('set: ', newValue)
		if (newValue === data.msg) {
			return
		}
		data.msg = newValue
		// 数据更改,更新 DOM 的值
		document.querySelector('#app').textContent = data.msg
	}
})

肯定会有多个属性,所以只需要循环遍历一下 key

// 模拟 Vue 中的 data 选项
let data = { msg: 'hello', name: 'zhang' }

// 模拟 Vue 的实例
let vm = {}

proxyData(data)

function proxyData(data) {
	// 遍历 data 对象中的所有属性
	Object.keys(data).forEach((key) => {
		// 把 data 中的属性,转换成 vm 的 setter/setter
		Object.defineProperty(vm, key, {
			enumerable: true,
			configurable: true,
			get() {
				console.log('get: ', key, data[key])
				return data[key]
			},
			set(newValue) {
				console.log('set: ', newValue)
				if (newValue === data[key]) {
					return
				}
				data[key] = newValue
				// 数据更改,更新 DOM 的值
				document.querySelector('#app').textContent = data[key]
			}
		})
	})
}
Vue 3.x 简单响应式例子

主要是 ES6 Proxy,直接监听对象,而非属性

// 模拟 Vue 中的 data 选项
let data = { msg: 'hello', count: 0 }
// 模拟 Vue 实例
let vm = new Proxy(data, {
	// 当访问 vm 的成员会执行
	get(target, key) {
		console.log('get, key: ', key, target[key])
		return target[key]
	},
	// 当设置 vm 的成员会执行
	set(target, key, newValue) {
		console.log('set, key: ', key, newValue)
		if (target[key] === newValue) return
		target[key] = newValue
		document.querySelector('#app').textContent = target[key]
	}
})
发布/订阅模式

主要是发布者、订阅者、消息中心

class EventEmitter {
	constructor() {
		// { eventType: [ handler1, handler2 ] }
		this.subs = {}
	}
	// 订阅通知
	$on(eventType, handler) {
		this.subs[eventType] = this.subs[eventType] || []
		this.subs[eventType].push(handler)
	}
	// 发布通知
	$emit(eventType) {
		if (this.subs[eventType]) {
			this.subs[eventType].forEach((handler) => {
				handler()
			})
		}
	}
}
// 测试
var bus = new EventEmitter()

// 注册事件
bus.$on('click', function () {
	console.log('click')
})
bus.$on('click', function () {
	console.log('click1')
})

// 触发事件
bus.$emit('click')
观察者
  • 观察者(订阅者) -- Watcher
    • update(): 当事件发生时,具体要做的事
  • 目标(发布者) -- Dep
    • subs 数组:存储所有的观察者
    • addSub():添加观察者
    • notify():当事件发生,调用所有观察者的 update() 方法
// 发布者-目标
class Dep {
    constructor () {
        // 记录所有的订阅者
        this.subs = []
    }
    // 添加订阅者
    addSub (sub) {
        if(sub && sub.update) {
            this.subs.push(sub)
        }
    }
    // 发布通知
    notify () {
        this.subs.forEach(sub => {
            sub.update()
        })
    }
}
// 订阅者-观察者
class Watcher {
    update () {
        console.log('update')
    }
}

// 测试
let dep = new Dep()
let watcher = new Watcher()

dep.addSub(watcher)
dep.notify()

来到第二步

网友评论