什么是发布订阅模式
- 定义
发布订阅模式是发布者不直接将消息发送给接收者(订阅者),而是将消息发给一个调度中心,调度中心根据消息的不同类别,向订阅者广播下去。 - 举例
拿微信公众号举例,有很多个用户关注分别关注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)&®.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>