用发布订阅模式实现MVVM

什么是发布订阅模式

  • 定义
    发布订阅模式是发布者不直接将消息发送给接收者(订阅者),而是将消息发给一个调度中心,调度中心根据消息的不同类别,向订阅者广播下去。
  • 举例
    拿微信公众号举例,有很多个用户关注分别关注A、B、C等公众号,一个用户可以同时关注一个或多个公众号,调度中心会根据公众号id这个类别来将订阅者的信息存储到这个类别的数组里面去,当要发布信息的时候,就去把这个类别下面的数组循环一遍将消息发布出去。

什么是MVVM设计模式

M:是数据模型就是后台返回的数据和vue里data中的数据
V:视图模型界面显示的内容,HTML代码
VM:是vue框架实现的功能,实现数据改变更新视图,视图(input输入)改变更新数据

MVVM框架由哪些功能来实现

  • 1、数据代理:遍历data里面的数据,将它们代理到this上方便直接通过this.key的方式获取,并将data里的值改变的时候赋给新的值。
  • 2、数据劫持:遍历data里的数据,如果有数据改变就触发发布的方法
  • 3、编译:将HTML代码进行编译,编译属性实现方法的绑定,编译双大括号实现模版编译,并调用订阅器将它存储下来
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	<div id="app">
		<p>{
   
   {name}}</p>
		<p>{
   
   {age}}</p>
		<input v-model="name" type="text">
		<button @click="changeName">改变</button>
		<button @click="changeName2">改变w</button>
	</div>
	<script>
		class Vue{
     
     
			constructor(options){
     
     
				this.$data = options.data();
				this.$methods = options.methods;
				this.$elNode = document.querySelector(options.el)
				this.$list = {
     
     }
				// 数据代理
				this.proxy_data()

				// 数据劫持
				this.observer()
				
				// 编译器
				this.compile();
			}

			// 数据代理,将data中的数据绑定到this上
			proxy_data(){
     
     
				Object.keys(this.$data).forEach(item=>{
     
     
					Object.defineProperty(this,item,{
     
     
						get(){
     
     
							return this.$data[item]
						},
						set(val){
     
     
							if(this.$data[item]!=val){
     
     
								this.$data[item] = val;
							}
						}
					})

				})
			}

			// 数据劫持,监测到data中的数据的变化,如果是改变就广播修改
			observer(){
     
     
				let that = this;
				Object.keys(that.$data).forEach(key=>{
     
     
					defineProperty(that.$data,key,that.$data[key])
				})

				function defineProperty(data,key,value){
     
     
					Object.defineProperty(data,key,{
     
     
						get(){
     
     
							return value
						},
						set(newVal){
     
     
							if(newVal == value){
     
     
								return false;
							}
							value = newVal;
							that.emit(key)
						}
					})
				}
			}

			// 真实DOM转化为文档碎片,在内存中处理提高性能
			nodeToFragment() {
     
     
				let fragment = document.createDocumentFragment();
				let nodes = [...this.$elNode.childNodes]
				nodes.forEach(item=>{
     
     
					fragment.appendChild(item)
				})
				return fragment;
			}

			// 对HTML进行编译,将双括号里的内容替换成data的值,将@xxx的属性绑定对应的事件或方法
			compile(){
     
     
				let fragment = this.nodeToFragment()
				let childNodes = fragment.childNodes;
				let reg = /\{\{(.*)\}\}/
				let that = this;
				
				childNodes.forEach(item=>{
     
     
					if((item.nodeType===1||item.nodeType===3)&&reg.test(item.textContent)){
     
     
						let key = item.textContent.replace(reg,`$1`);
						setText(item,key)
						// 订阅
						this.on(key,setText.bind(this,item,key))
					}else if(item.nodeType===1){
     
     
						let nodeAttrs = [...item.attributes]
						nodeAttrs.forEach(cell=>{
     
     
							if(cell.name.includes('@')){
     
     
								item.addEventListener(cell.name.substr(1),this.$methods[cell.value].bind(this))
							}
							if(cell.name =='v-model'){
     
     
								item.addEventListener('input',function(e){
     
     
									that[cell.value] = e.srcElement.value;
								})
							}
						})
					}
				})
				this.$elNode.appendChild(fragment)

				function setText(node,key){
     
     
					node.textContent = that[key];
				}
			}

			// 订阅
			on(key,fn){
     
     
				if(!(this.$list[key] instanceof Array)){
     
     
					this.$list[key] = []
				}
				this.$list[key].push(fn)
			}

			// 发布
			emit(key){
     
     
				this.$list[key].forEach(fn=>{
     
     
					fn()
				})
			}

		}
	</script>
	<script>
		new Vue({
     
     
			el:'#app',
			data(){
     
     
				return{
     
     
					name:'小明',
					age:66
				}
			},
			methods:{
     
     
				changeName(){
     
     
					this.name = '小红'
					this.age = 88
				},
				changeName2(){
     
     
					this.name = '第二次修改名字'
					this.age = '第二次修改年龄'
				}
			}
		})
	</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/weixin_35958891/article/details/108509048