大家好,欢迎来到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