工欲善其事,必先利其器
介绍几个关于响应式原理的名词
数据驱动:开发过程中只关心数据的本身,并不需要关心如何渲染到视图上
数据响应式:.数据模型仅仅是普通的JavaScript对象,而当我们修改数据时,
视图会进行更新,避免了繁琐的DOM操作,提高开发效率
双向绑定:.数据改变,视图改变;视图改变,数据也随之改变
我们可以使用v- model在表单元素上创建双向数据绑定
ES5实现响应式
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
</body>
<script>
//模拟vue里面的data数据
let data = {
msg:"hello world",
};
//模拟vue实例
let vm = {};
//数据劫持,当访问或者设置vm里面成员的时候,做一些干预操作
Object.defineProperty(vm,'msg',{
//允许遍历操作
enumerable:true,
//允许修改配置操作
configurable:true,
//当获取值的时候自动调用get方法
get(){
console.log('get方法执行了',data.msg);
//默认返回data里面msg
return data.msg
},
//当设置值的时候,自动执行set方法
set(newValue){
//逻辑判断如果新的值等于原先值则不作任何操作
if (newValue === data.msg) {
return
};
//当代码走到此处,意味着新的值并不等于当前值,在这里重新赋值
data.msg = newValue;
//把修改过后的值,更新到dom上面
document.querySelector('#app').textContent = data.msg
}
});
//打印当前vm.msg,会自动调用get方法
console.log(vm.msg)
//设置新的值,会自动调用set方法
vm.msg = '123';
</script>
</html>
ES5转换多个属性转换getter和setter
上面的代码仅仅是演示了单个属性转换getter和setter,
接下来为大家演示ES5为多个属性转换getter和setter,希望可以帮助到大家
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
</body>
<script>
//模拟vue里面的data数据
let data = {
msg:"hello world",
count:10
};
//模拟vue实例
let vm = {};
//设置新的值,会自动调用set方法
proxyData(data)
function proxyData(data){
Object.keys(data).forEach(key=>{
Object.defineProperty(vm,key,{
enumerable:true,
configurable:true,
get(){
return data[key]
},
set(newValue){
if (newValue === data[key]) {
return
};
data[key] = newValue;
document.querySelector('#app').textContent = data[key]
}
})
})
};
console.log(vm)
vm.msg = 'Spring '
</script>
</html>
ES6实现响应式
<!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"></div>
</body>
<script>
//vue3.0的数据代理通过ES6的Proxy,
//ES6的Proxy
//1、直接监听对象并非属性 2、由于是ES6新增的方法IE不支持,性能有浏览器优化
//设置需要代理的数据
let data = {
age:20,
name:"Loki",
sex:"female"
};
//通过vm访问代理成员
//ES6的Proxy是一个构造函数,里面可以传递两个参数
//第一个是需要代理的对象,第二个参数是一个对象,第二个参数里面的成员是
//执行代理行为的函数,当我们访问vm的时候触发get函数
//当我们设置vm里面的值的时候触发set函数
let vm = new Proxy(data,{
//get方法有两个参数,作用分别是,第一个参数是代表当前的代理对象
//第二个参数是,代理对象的key值
get(target,key) {
//返回当前访问的key值
return target[key]
},
//set有3个参数,前两个参数分别是代表当前的代理对象,第二个参数是,代理对象的key值
//第三个参数是新的值
set(target,key,newValue){
//逻辑判断是当前值是否等于新的值
if (target[key] === newValue) {
return
}
//当前值等于新的值
target[key] = newValue;
//把新的值渲染到dom上面
document.querySelector('#app').textContent = target[key]
}
});
//测验代码
vm.age = 100;
</script>
</html>
发布订阅模式
<!-- 这里模拟vue的自定义事件,实现发布订阅模式具体代码请看下面 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>发布订阅模式实现</title>
</head>
<body>
</body>
<script>
//通过构造函数实现发布订阅
class EventCenter {
constructor(){
//定义空对象,这里不需要使用原型,所以通过下面这种方法创建对象
this.subs = Object.create(null);
}
//注册事件函数,传入两个参数一个是事件类型一个是事件的回调函数
$on(eventType,handler){
//例如是click事件{ click : [fun1,fun2,fun3,fun4] }一个事件对应多个方法
//对象的数据结构,可能是同类型的事件触发多次,所以click的值数组
//逻辑判断this.subs[eventType]的事件是否存在,如果不存在值为数组
this.subs[eventType] = this.subs[eventType] || [];
//把时间添加到数组里面
this.subs[eventType].push(handler);
}
//触发时间函数传入一个参数,事件类型
$emit(eventType){
//逻辑判断是否存在事件
if (this.subs[eventType]) {
//如果存在事件就遍历这个数组,并执行这个数组
this.subs[eventType].forEach(handler => {
handler();
});
}
}
}
//测试代码
let ec = new EventCenter();
ec.$on('click',()=>{
console.log('click1');
});
ec.$on('click',()=>{
console.log('click2');
});
ec.$emit("click");
//result => click1
//result => click2
</script>
</html>
观察者模式
发布订阅和观察者模式的区别
观察者模式: 是由具体目标调度,比如事件触发,Dep就会去触发观察者的方法,
所以观察者模式的订阅者和发布者是存在依赖的
发布订阅模式:有统一的调度中心,因此发布和订阅不需要知道对方的存在
谢谢观看,如有不足,敬请指教