本来写了很多,还有很多图解,可就按了一次撤销后就全没了(╬◣д◢),自己在写的过程中已经慢慢构建了体系,也就不想再重写了,想深入理解就看看下面这篇吧。回头再写个Vue深入源码系列叭。
图解 Vue 响应式原理 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/168768245
一、前言——问题引出
当按如下代码修改时并不会在页面自动修改显示,但数据的的确确是被修改了的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表过滤</title>
<script type="text/javascript" src="../vuejs/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<button @click="updataMei">更新马冬梅</button>
<input type="text" placeholder="请输入姓名" v-model="keyWord"></input>
<ul>
<li v-for="(p, index) in Persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
]
},
methods: {
updataMei(){
this.persons[0] = {id:'001',name:'马老师',age:55,sex:'男'};
//结果时Vue监测不到
}
},
})
</script>
</html>
二、问题解决
(一)Vue如何实现"追踪数据变化"
响应式的实现......
(二)Vue监测变化的注意事项
# 对于对象......
# 对于数组......
# 规避方法(使用特定方法触发响应式)
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
深入响应式原理 — Vue.js (vuejs.org)https://cn.vuejs.org/v2/guide/reactivity.html
三、文字总结
vue.js是通过数据劫持的方式实现数据的双向绑定的,其中过程如下:
当把一个JavaScript对象传给Vue实例的data选项时,Vue会遍历此对象的所有属性并使用 Object.defineProperty(),把这些属性全部转化为 getter/setter 。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的时候把属性记录为依赖,之后当依赖的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。因此,每当数据变化的时候会引起视图的变化,更新view。
因为Vue在初始化实例时会对属性进行 getter/setter 转化,这样,被转化的属性就是 响应式的,而只有在data中声明的属性会执行这个过程。所以,后续通过JavaScript传过来的对象属性因为没有在 data 中声明,并且通过这种方式传对象并不会执行这个转化响应式过程,因此这些属性全都不是响应式的。
也是因为这个原因,现在的 Vue.js 无法监听到对象属性的添加和删除,也无法检测到数组的变动。
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
四、Vue响应式简单实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>模拟一个数据监测</title>
</head>
<body>
<div>
<h2 id="h2"></h2>
</div>
<script>
const data = {
name:'温开水',
age:21
};
// --------------------------------------------方法一------------------------------------------------//
// 可以用于创建一个监视的实例对象,用于监视data中的属性变化。数据代理
function Observer(obj){
// 汇总对象中的所有属性形成一个[数组]
const keys = Object.keys(obj);
console.log(keys);
// 遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k];
},
set(val){
console.log(`${k}被修改了,马上进行解析模板,生成虚拟DOM,diff算法比较...`);
obj[k] = val;
}
})
})
}
// 创建一个监视的实例对象,用于监视data中的属性变化
const obs = new Observer(data);
//---------------------------------------方法二----------------------------------------------------//
//其实和方法一一样,不过是我自己思考出来的,好理解一点
const data2 = {};
Object.defineProperty(data2,'age',{
get(){
return data.age;
},
set(val){
console.log(`age被修改了,马上进行解析模板,生成虚拟DOM,diff算法比较...`);
data.age = val;
}
})
</script>
</body>
</html>
一句话:创建一个新的对象data2,让data2对象数据(属性)可以获取(get)与修改(set)原对象data中的数据(属性)