vue2.x数据响应式的实现

1.响应式的实现(Object.defineProperty)

在javascript中实现数据响应式一般有两种方案, 正好也对应了vue2.x和vue3.x使用的方式
1.对象属性拦截(vue2.x)=> Object.defineProperty
2.对象整体代理(vue3.x) => Proxy

  • 对象属性拦截的实现是通过Object.defineProperty定义对象,通过get,set方法实现的
// 栗子
let data = {
    
    
	name: '我是栗子'
	age: 18
}
// 遍历每个属性
// Object.keys()将对象转化为数组形式
Object.keys(data).forEach((key) => {
    
    
	defineReactive(data, key, data[key])
})
//响应式转化
// 使用闭包特性, 使value变量不被销毁
function defineReactive(data, name, value) {
    
    
	Object.defineProperty(data, name, {
    
    
		get() {
    
    
			return value
		}
		set(newVal) {
    
    
			value = newVal
		}
	})
}

2.指令的实现(v-model)

<div>
	<input v-model="name"></input>
</div>
<script>
	let data = {
      
      
		name: '栗子',
		age: 18
	}
	Object.keys(data).forEach((key) => {
      
      
		defineReactive(data, key, data[key])
	})
	function defineReactive(data, key, value ) {
      
      
		Object.defineProperty(data, key, {
      
      
			get() {
      
      
				return value
			},
			set(newVal) {
      
      
				if(newVal === value) {
      
      
					return
				}
				value = newVal
				// 直接全部执行存在问题,会导致没有发生更改得数据也进行执行, 可以通过发布订阅模式(自定义事件)进行优化
				compile()
			}
		})
	}
	function compile() {
      
      
		let app = document.getElementById('app')
		// 拿到app的所有子元素
		const nodes = app.childNodes
		// 遍历所有子元素
		nodes.forEach(node => {
      
      
			if(node.nodeType === 1) {
      
      
				// node.attributes找到标签的属性
				const attrs = node.attributes
				Array.from(attrs).forEach(attr => {
      
      
					const dirName = attr.nodeName
					const dataProp = attr.nodeValue
					// v-model
					if(dirName === 'v-model') {
      
      
						// M->V
						node.value = data[dataProp]
						// V->M
						node.addEventListener('input', (e) => {
      
      
							data[dataProp] = e.target.value
						})
					}
					// v-text
					if(dirName === 'v-text') {
      
      
						node.innerText = data[dataProp]
					}
				})
			}
		})
	}
	// 首次渲染
	compile()
</script>

3.简单实现发布订阅模式

简单版发布订阅模式

// dep对象
const dep = {
    
    
	// map事件对象存在里面
	map: Object.create(null),
	// 收集事件
	collect(dataProp, updateFn) {
    
    
		// 判断map中是否存在该事件
		if(!this.map[dataProp]) {
    
    
			this.map[dataProp] = []
		}
		this.map[dataProp].push(updateFn)
	},
	// 触发事件
	trigger(dataProp) {
    
    
		this.map[dataProp] && this.map[dataProp].forEach(updateFn => {
    
    
			updateFn()
		})
	}
}
  • 优化指令实现

进行收集事件

function compile() {
    
    
	let app = document.getElementById('app')
	const nodes = app.childNodes
	nodes.forEach(node => {
    
    
		if(node.nodeType === 1) {
    
    
			const attrs = node.attributes
			Array.from(attrs).forEach(attr => {
    
    
				const dirName = attr.nodeName
				const dateProp = attr.nodeValue
				if(dirName === 'v-text') {
    
    
					// 第一次赋值
					node.innerText = data[dataProp]
					// 收集跟新函数
					dep.collect(dateProp, () => {
    
    
						node.innerText = data[dataProp]
					})
				}
			})
		}
	})
}	

触发收集函数

function defineReactive(data, key, value) {
    
    
	Object.defineProperty(data, key, {
    
    
		get() {
    
    
			return value	
		},
		set(newVal) {
    
    
			if(newValue === value) return
			value = newValue
			// 触发对应事件即可
			dep.trigger(key)
		}
	})
}

猜你喜欢

转载自blog.csdn.net/weixin_47979372/article/details/124319130