defineProperty
这个obj上的方法可以用来修改指定的对象属性的值
语法:Object.defineProperty(obj,property,descriptor)
参数一:obj
绑定属性的目标对象
参数二:property
绑定的属性名
参数三:descriptor
属性描述(配置),且此参数本身为一个对象
属性值1:value
设置属性默认值
属性值2:writable
设置属性是否能够修改
属性值3:enumerable
设置属性是否可以枚举,即是否允许遍历
属性值4:configurable
设置属性是否可以删除或编辑
属性值5:get
获取属性的值
属性值6:set
设置属性的值
先定义一个这个玩意
var obj = {
count : 0,
list : [1,2,4]
}
var target = {};
通过value设置属性值
由于Object.defineProperty可以给对象定义属性,我们通过value,可以动态地将obj对象的属性动态地添加给target中。
for (let key in obj){
Object.defineProperty(target,key,{
value: obj[key]
})
}
console.log(target);
通过get方法获取属性的值
【注意!】当设置get方法时,不能有value和writable方法,否则会报错
get方法的值是一个函数,此函数不需要参数
for (let key in obj){
Object.defineProperty(target,key,{
//value: obj[key],
//writable:true,
enumerable: false,
configurable: true,
get: function(){
return obj[key]
}
})
}
console.log(target.count);
通过set方法设置属性的值
set方法的值也是一个函数,定义时会自动注入一个参数,此参数会设置属性的值
for (let key in obj){
Object.defineProperty(target,key,{
//value: obj[key],
//writable:true,
enumerable: false,
configurable: true,
get: function(){
return obj[key]
},
set: function(val){
console.log(val);//打印设置好的值
}
})
}
target.count = 10;//修改count属性的值为10
vue2.x的响应式原理
在Vue2.X 响应式中使用到了 defineProperty 进行数据劫持,所以我们对它必须有一定的了解,那么我们先来了解它的使用方法把, 这里我们来使用 defineProperty来模拟 Vue 中的 data
<body>
<div id="app"></div>
<script>
// 模拟 Vue的data
let data = {
msg: '',
}
// 模拟 Vue 实例
let vm = {}
// 对 vm 的 msg 进行数据劫持
Object.defineProperty(vm, 'msg', {
// 获取数据
get() {
return data.msg
},
// 设置 msg
set(newValue) {
// 如果传入的值相等就不用修改
if (newValue === data.msg) return
// 修改数据
data.msg = newValue
document.querySelector('#app').textContent = data.msg
},
})
// 这样子就调用了 defineProperty vm.msg 的 set
vm.msg = '1234'
</script>
</body>
看了上面的方法只能修改一个属性,实际上我们 data 中数据不可能只有一个,我们何不定义一个方法把data中的数据进行遍历都修改成响应式
<body>
<div id="app"></div>
<script>
// 模拟 Vue的data
let data = {
msg: '哈哈',
age: '18',
}
// 模拟 Vue 实例
let vm = {}
// 把多个属性转化 响应式
function proxyData() {
// 把data 中每一项都[msg,age] 拿出来操作
Object.keys(data).forEach((key) => {
// 对 vm 的 属性 进行数据劫持
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]
},
})
})
}
// 调用方法
proxyData(data)
</script>
</body>
发布订阅模式
首先来说简单介绍下 一共有三个角色
发布者、 订阅者、 信号中心
在Vue 中的例子 就是EventBus emit
<body>
<div id="app"></div>
<script>
class Vue {
constructor() {
// 用来存储事件
// 存储的 例子 this.subs = { 'myclick': [fn1, fn2, fn3] ,'inputchange': [fn1, fn2] }
this.subs = {}
}
// 实现 $on 方法 type是任务队列的类型 ,fn是方法
$on(type, fn) {
// 判断在 subs是否有当前类型的 方法队列存在
if (!this.subs[type]) {
// 没有就新增一个 默认为空数组
this.subs[type] = []
}
// 把方法加到该类型中
this.subs[type].push(fn)
}
// 实现 $emit 方法
$emit(type) {
// 首先得判断该方法是否存在
if (this.subs[type]) {
// 获取到参数
const args = Array.prototype.slice.call(arguments, 1)
// 循环队列调用 fn
this.subs[type].forEach((fn) => fn(...args))
}
}
}
// 使用
const eventHub = new Vue()
// 使用 $on 添加一个 sum 类型的 方法到 subs['sum']中
eventHub.$on('sum', function () {
let count = [...arguments].reduce((x, y) => x + y)
console.log(count)
})
// 触发 sum 方法
eventHub.$emit('sum', 1, 2, 4, 5, 6, 7, 8, 9, 10)
</script>
</body>
观察者模式
与发布订阅者不同 观察者中 发布者和订阅者(观察者)是相互依赖的 必须要求观察者订阅内容改变事件 ,而发布订阅者是由调度中心进行调度,那么观察者模式是相互依赖,下面就举个简单例子
<body>
<div id="app"></div>
<script>
// 目标
class Subject {
constructor() {
this.observerLists = []
}
// 添加观察者
addObs(obs) {
// 判断观察者是否有 和 存在更新订阅的方法
if (obs && obs.update) {
// 添加到观察者列表中
this.observerLists.push(obs)
}
}
// 通知观察者
notify() {
this.observerLists.forEach((obs) => {
// 每个观察者收到通知后 会更新事件
obs.update()
})
}
// 清空观察者
empty() {
this.subs = []
}
}
class Observer {
// 定义观察者内容更新事件
update() {
// 在更新事件要处理的逻辑
console.log('目标更新了')
}
}
// 使用
// 创建目标
let sub = new Subject()
// 创建观察者
let obs1 = new Observer()
let obs2 = new Observer()
// 把观察者添加到列表中
sub.addObs(obs1)
sub.addObs(obs2)
// 目标开启了通知 每个观察者者都会自己触发 update 更新事件
sub.notify()
</script>
</body>
鞭尸supershop
sendShopCar() {
let that = this
let arr = []
this.tabbar.forEach(item => {
console.log(item);
item.list.forEach(item1 => {
if (item1.num > 0) {
arr.push(item1)
}
})
})
this.$store.state.Cartlist = arr
console.log(arr);
uni.request({
url: `https://mall.wuhanzhuangxiu01.cn/apip/cart/`,
method: "POST",
data: {
"data": JSON.stringify(arr),
"appid": that.appId,
"openid": that.openid,
"shopid": that.shop_info.uid
},
success: (res) => {
console.log(res);
}
})
},
关于为什么在shopCarts页面没有实现响应式的原因
新加入的Cartlist属性是直接显示在控制台上的,并没有被匹配一对getter和setter,这就是视图不能立即响应的原因。
直接给state里的数据使用最简单粗暴的赋值的方式添加属性,并没有通过Vue对数据进行操作,所以这样并不会是响应式。
我们需要使用Vue.set(target,propertyName,value)这个方法来实现修改响应式数据
- target:要更改的数据(一般是响应式对象) 例:state.data
- property:这个对象中的子对象或者属性
- value:你想要赋的值
Vue.set(this.$store.state.Cartlist,"Cartlist",arr)