Implement MVVM with Publish and Subscribe Mode

What is the publish and subscribe model

  • Definition The
    publish-subscribe model is that the publisher does not directly send the message to the receiver (subscriber), but sends the message to a dispatch center, and the dispatch center broadcasts to subscribers according to different types of messages.
  • For example,
    take the WeChat official account as an example. There are many users who pay attention to A, B, C and other official accounts respectively. A user can follow one or more official accounts at the same time. The dispatch center will report the subscriber information according to the category of the official account id. Store it in the array of this category. When you want to publish the information, go and loop the array under this category to publish the message.

What is the MVVM design pattern

M: The data model is the data returned in the background and the data in the data in vue.
V: the content displayed on the view model interface, the HTML code
VM: is the function implemented by the vue framework, which realizes the data change and update view, and the view (input input) changes and updates data

What functions are implemented by the MVVM framework

  • 1. Data proxy: traverse the data in data, proxy them to this to get them directly through this.key, and assign new values ​​when the value in data changes.
  • 2. Data hijacking: traverse the data in data and trigger the release if there is a data change
  • 3. Compile: compile the HTML code, compile the attribute to realize the binding of the method, compile the double braces to compile the template, and call the subscriber to store it
<!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>

Guess you like

Origin blog.csdn.net/weixin_35958891/article/details/108509048