一.深入响应式原理
Vue最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的JavaScrpit对象。而当你修改他们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避免一些常见的问题。接下来我们一起探究下吧。
二.Object.defineProperty属性
当你把普通的JavaScrpit对象传入Vue实例作为data选项,Vue将遍历此对象所有的property,并且使用Object.defineProperty?把这些property全部转为getter/setter。Object.defineProperty是ES5中一个无法shim的特性,这也就是Vue不支持IE8以及更低版本浏览器的原因。
关于对shim的解释
Vue响应式原理中:Object.defineProperty是Es5中无法shim的特性,那么这里的shim是什么呢? 定义:shim可以将新的API引入到旧的环境中,而且仅靠就环境中已有的手段实现。
这些getter/setter对用户来说是不可见的,但是在内部他们让Vue能够追踪依赖,在property被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对getter/setter的格式化并不同,所以建议安装vue-devtools来获取对检查数据更加友好的用户界面。
每个组件实例都对应watcher实例,它会在组件渲染的过程中把'接触'过的数据,property记录为依赖。之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。?
图示:
三.检测变化的注意事项
(1)Vue不能检测数组和对象的变化
由于JavaScrpit的限制,Vue不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应式。
(2)对于对象
Vue无法检测property的添加或移除。由于Vue会在初始化实例时对property执行getter/setter转化,所以property必须在data对象上存在才能让Vue将它转化为响应式的。
需求要在一个对象里面添加一个属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{obj.a}}</h1>
<h1>{{obj.c}}</h1>
<button @click="fn"></button>
</div>
<script>
const app=new Vue({
el:"#app",
data() {
return {
obj:{a:1}
}
},
methods: {
fn(){
// this.obj.a=10 // 直接修改原始的对象是可以修改的
// this.obj.c=10 // 但是想要直接给对象添加一个属性是不可以的
// this.$set(this.obj,'c',10) 可以利用官方提供的api
}
},
})
</script>
</body>
</html>
在原始有的对象里直接通过:
? // this.obj.c=10,添加一个属是无效的,
但是可以提供Vue提供的api:
? // this.$set(this.obj,'c',10)?
剖析:this.$set(要添加的对象,属性名(要用字符串包裹起来),属性值)
(3)对于数组
1.对于数组除了一些数组的api:push,pop,unshift,shift等api是响应的之外
2.也可以使用this.$set提供的这个api,在数组中的配置如下:
this.$set(要修改的数组,索引的位置,要添加的元素)
四.实现一个响应式
1.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 显示obj.a的值 -->
1
</div>
<script>
var obj={a:1}
Object.defineProperty(obj,'a',{
get:function(){
console.log(`有人对a属性进行了访问`);
},
set:function(newVal){
document.getElementById('app').innerHTML=newVal
}
})
</script>
</body>
</html>
2.图示
当有人访问到了a属性就会触发get这个函数
?
当有人给a进行赋值的时候就会触发set这个函数
?这个时候可以看见左边的视图发生了改变,符合Vue双向数据绑定的原理,即:数据------------视图,也可以的到的是newVal的值是我们输入的值是10。
五:Object.defineProperty的一些参数
1.Value
let obj={
a:1
}
Object.defineProperty(obj,'c',{
value:10
})
console.log(obj);
图示:
可以看出obj对象里面多出了c属性并且它的值为10,相当于obj.c=10;
2.writable
定于:如果writable的值为false的话,则里面的值是不可以进行修改的、
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
a:1
}
Object.defineProperty(obj,'c',{
writable:false,
value:10
})
obj.a=1000
obj.c=50 // 用defineProperty修改里面的值是没用效果的
console.log(obj);
</script>
</body>
</html>
图示:
3.configurable
1.configurable:false,表示不能被删除,也不能被重定义。
2.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
a:1
}
Object.defineProperty(obj,'c',{
// writable:false,
value:10,
configurable:false
})
delete obj.a //可以被删除
delete obj.c //不能被删除
// 用defineProperty修改里面的值是没用效果的
console.log(obj);
</script>
</body>
</html>
3.图示:
4.不能被重定义:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
a:1
}
Object.defineProperty(obj,'c',{
// writable:false,
value:10,
configurable:false
})
// 不能重新进行定义
Object.defineProperty(obj,'c',{
value:30
})
console.log(obj);
</script>
</body>
</html>
图示:
4.enumerable
定义:如果该值为false的话,则表示是不可以进行枚举的。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var person = {}
Object.defineProperty(person,'name',{
configurable:false,//能否使用delete、能否需改属性特性、或能否修改访问器属性、,false为不可重新定义,默认值为true
enumerable:false,//对象属性是否可通过for-in循环,flase为不可循环,默认值为true
writable:false,//对象属性是否可修改,flase为不可修改,默认值为true
value:'xiaoming' //对象属性的默认值,默认值为undefined
});
for(var i in person){
console.log(person[i]) //无结果,不可循环
}
</script>
</body>
</html>
图示:当为enumerable的时候可以遍历出来
六.封装一个函数,监听对象的属性值变化。
1.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={a:1,b:2,c:3}
let newObj={}
for (let key in obj) {
Object.defineProperty(newObj,key,{
set:function(newVal){
console.log(`有人修改了${key},新值是${newVal}`);
obj[key]=newVal
},
get:function(){
return obj[key]
}
})
}
</script>
</body>
</html>
2.用了set会被当成拦截器,获取不了值,需要设置变量来解决问题
3.图示:
?七.扩展obj[`key`],obj[key],obj.key的用法和区别
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
name:'卡卡西'
}
console.log(obj[`name`]);
console.log(obj.name);
for (let key in obj) {
console.log(obj[key]);
}
</script>
</body>
</html>
对象的属性名是一个字符
验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
name:'卡卡西',
age:20
}
console.log(obj[`name`]);
// 所以取出对象属性值的时候,还可以这样写obj[`name`]
for (let key in obj) {
console.log(typeof key);
}
</script>
</body>
</html>
图示:
?
可以看出分为两种进行使用:
去除对象的属性值:对象.属性名,对象.[`属性名`]
如果需要进行枚举的话:对象[属性名]
列如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj={
name:'卡卡西',
age:20
}
for (const key in obj) {
console.log(key);
console.log(obj[key]);
}
</script>
</body>
</html>
|