vue 发布订阅模式

vue 发布订阅模式vue发布订阅模式为什么要使用发布订阅模式vue中数据反映到视图中的方式主要是采取声明式渲染+模板编译声明式渲染:例如v-mdoe等指令的形式渲染模板编译原理:简单来讲就是获取app下所有的childNodes通过循环找到符合的元素节点,node.nodeType===1拿到所有元素节点的attributes属性,遍历attributes其中包括了两个关键属性nodeName、nodeValue其中nodeName就是指令:例如v-mode,nodeValue就是v-mo

大家好,欢迎来到IT知识分享网。

vue 发布订阅模式

为什么要使用发布订阅模式

  • vue 中数据反映到视图中的方式主要是采取声明式渲染+模板编译
    声明式渲染: 例如v-mdoe等指令的形式渲染
    模板编译原理:
  • 简单来讲就是获取app下所有的childNodes
  • 通过循环找到符合的元素节点,node.nodeType === 1
  • 拿到所有元素节点的attributes属性,遍历attributes
  • 其中包括了两个关键属性nodeName、nodeValue
  • 其中nodeName 就是指令:例如v-mode, nodeValue就是v-mode绑定的值如 v-mode=“name” 中的name
  • nodeValue刚好就是data被绑定对象的key,如此 node.innerText = data[arrt.nodeValue] 就建立了连接。
  • 以上有个缺点:多个标签都是用v-mode就会导致,暴力更新的问题,就是不管值有没有改变,统一都更新。
  • 代码
	<div id="app">
			<!-- <input id="input" v-mode="inputVal" /> -->
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span><br />
			<span v-mode="age"></span><br />
		</div>
			let data = { 
   
			inputVal: 'vue发布订阅模式',
			age: 15,
		}
	// 数据代理
		const proxyReative = (data) => { 
   
			observe(data)
			return new Proxy(data, { 
   
				get(target, key) { 
   
					return target[key]
				},
				set(target, key, newVal) { 
   
					if (target[key] === newVal) return
					target[key] = newVal
					 // 执行更新
					  compile()
				},
			})
		}
		const observe = (data) => { 
   
			Object.keys(data).forEach((key) => { 
   
				if (typeof data[key] === 'object') { 
   
					data[key] = proxyReative(data[key])
				}
			})
		}

		const vm = proxyReative(data)
	// 模板编译
		const compile = () => { 
   
			const appRef = document.getElementById('app')
			const childNodes = appRef.childNodes
			childNodes.forEach((node) => { 
   
				// 1 元素节点 3 文本节点
				if (node.nodeType === 1) { 
   
					const arrts = node.attributes
					Array.from(arrts).forEach((arrt) => { 
   
						if (arrt.nodeName === 'v-mode') { 
   
							// 初次渲染赋值
							node.innerText = data[arrt.nodeValue]
						}
					})
				}
			})
		 
		}

问题展示

  • 虽然我们只是更新了age的值但由于元素绑定的都是v-mode下面代码会执行,所以都会执行更新操作
if (arrt.nodeName === 'v-mode') {
		// 初次渲染赋值
			node.innerText = data[arrt.nodeValue]
		}

在这里插入图片描述

解决暴力更新的问题 — 发布订阅模式

  • 发布订阅模式的核心就是一对多的关系,一个发布者发起事件,所有的订阅者都会执行。
  • 利用这个特性,给每一个v-mode在进行细化,只有v-mode且v-mode=“age”的才进行更新操作
  • 代码
// 发布订阅模式
		const Dep = { 
   
			map: { 
   },
			// 收集事件 eventName:inputVal或者age
			// 形成的结构 
			/** map: age: [ƒ] inputVal: Array(4) 0: () => {…} 1: () => {…} 2: () => {…} 3: () => {…} */
			collect(eventName, callback) { 
   
				if (!this.map[eventName]) this.map[eventName] = []
				this.map[eventName].push(callback)
			},
			// 触发事件 只有key 对应的才触发回调事件
			trigger(nodeName) { 
   
				this.map[nodeName].forEach((callback) => { 
   
					callback()
				})
			},
		}
		console.log(Dep)

最终结果

在这里插入图片描述

完整代码

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>发布订阅模式</title>
	</head>
	<body>
		<div id="app">
		 
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span> <br />
			<span v-mode="inputVal"></span><br />
			<span v-mode="age"></span><br />
		</div>
	</body>
	<script>
		/* 关键就是一个一对多的关系 */
		let data = { 
   
			inputVal: 'vue发布订阅模式',
			age: 15,
		}
		// 数据代理
		const proxyReative = (data) => { 
   
			observe(data)
			return new Proxy(data, { 
   
				get(target, key) { 
   
					return target[key]
				},
				set(target, key, newVal) { 
   
					if (target[key] === newVal) return
					target[key] = newVal
					// compile()
					// 执行更新
					Dep.trigger(key)
				},
			})
		}
		const observe = (data) => { 
   
			Object.keys(data).forEach((key) => { 
   
				if (typeof data[key] === 'object') { 
   
					data[key] = proxyReative(data[key])
				}
			})
		}

		const vm = proxyReative(data)

		// 发布订阅模式
		const Dep = { 
   
			map: { 
   },
			collect(eventName, callback) { 
   
				if (!this.map[eventName]) this.map[eventName] = []
				this.map[eventName].push(callback)
			},
			trigger(nodeName) { 
   
				this.map[nodeName].forEach((callback) => { 
   
					callback()
				})
			},
		}
		console.log(Dep)

		// 模板编译
		const compile = () => { 
   
			const appRef = document.getElementById('app')
			// const inputRef = document.getElementById('input')
			const childNodes = appRef.childNodes

			childNodes.forEach((node) => { 
   
				// 1 元素节点 3 文本节点
				if (node.nodeType === 1) { 
   
					const arrts = node.attributes
					Array.from(arrts).forEach((arrt) => { 
   
						if (arrt.nodeName === 'v-mode') { 
   
							// 初次渲染赋值
							node.innerText = data[arrt.nodeValue]
							// 后续更新赋值 收集更新函数
							Dep.collect(arrt.nodeValue, () => { 
   
								console.log('更新了', arrt.nodeValue)
								node.innerText = data[arrt.nodeValue]
							})
						}
					})
				}
			})
			 
		}
		compile()
	</script>
</html>

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/10419.html

(0)
上一篇 2024-03-31 22:00
下一篇 2024-04-04 09:15

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信