赞
踩
在Vue 2中,响应式系统是基于Object.defineProperty
实现的。它通过劫持对象的属性来实现数据的响应式更新。
当你将一个对象传递给Vue实例的data
选项时,Vue会遍历对象的每个属性,并使用Object.defineProperty
方法将其转换为getter和setter。这样一来,当你访问或修改这些属性时,Vue能够捕获到这些操作并触发相应的更新。
具体而言,Object.defineProperty
方法用于定义一个对象的新属性,或者修改对象的现有属性。通过在属性上设置getter和setter,我们可以监听属性的读取和修改行为,并在这些行为发生时执行相应的操作。
Vue的响应式系统利用了这一特性,在getter中收集依赖(即追踪属性的订阅者),在setter中触发更新(通知订阅者进行响应式更新)。
1、Vue的响应式系统仅对已经存在的属性进行劫持,而不会劫持对象的整个原型链。这意味着如果你向一个已经创建的对象直接添加新属性或删除属性,Vue无法检测到这些更改。
2、由于数组本质上是对象,Vue可以通过Object.defineProperty
或Proxy
拦截对象的属性访问和修改。但是,对于数组而言,直接修改索引对应的值(如果对应的值是对象数据、指的是修改整条对象数据、即将一整个对象赋值给当前索引的对象数据,修改当前索引值对应的值对象的某个属性是会触发dom更新的)并不会触发属性的setter,因此Vue无法监测到这种变化。
直接为data里的对象通过赋值的方式添加属性和delete删除属性、控制台打印数据添加和删除属性成功、但是页面未能渲染最新数据、即未触发dom的更新。
- <template>
- <div class="home">
- <h3>这是vue2测试页面</h3>
- <p>用户信息: {{ userMsg }} </p>
- <p><button @click="add">添加用户性别为男</button></p>
- <p><button @click="change">修改用户年龄为18</button></p>
- <p><button @click="deleteH">删除用户身高信息</button></p>
- </div>
- </template>
-
- <script>
-
- export default {
- data() {
- return {
- // 用户信息
- userMsg: {
- name: 'zs',
- age: '20',
- height: '180',
- }
- }
- },
- methods: {
- // 为对象直接添加数据
- add() {
- this.userMsg.sex = '男'
- console.log('添加性别后')
- console.log(this.userMsg)
- },
- change() {
- this.userMsg.age = '18'
- console.log('修改数据年龄后')
- console.log(this.userMsg)
- },
- deleteH() {
- delete this.userMsg.height
- console.log('删除身高数据后')
- console.log(this.userMsg)
- }
- },
- }
- </script>
- <style>
- .home{
- margin: 0 auto;
- text-align: center;
- }
- </style>
这些操作都是对操作对象进行重新赋值,Vue 可以检测到这种赋值操作,并且会重新渲染相关的 DOM 元素,因此你看到了 DOM 元素能够更新。
- <template>
- <div class="home">
- <h3>这是vue2测试页面</h3>
- <p>用户信息: {{ userMsg }} </p>
- <p><button @click="add">添加用户性别为男</button></p>
- <p><button @click="change">修改用户年龄为18</button></p>
- <p><button @click="deleteH">删除用户身高信息</button></p>
- </div>
- </template>
-
- <script>
-
- export default {
- data() {
- return {
- // 用户信息
- userMsg: {
- name: 'zs',
- age: '20',
- height: '180',
- }
- }
- },
- methods: {
- // 为对象直接添加数据
- add() {
- // this.userMsg.sex = '男'
- let newUserMsg = { ...this.userMsg }
- newUserMsg.sex = '男'
- this.userMsg = newUserMsg
- console.log('添加性别后')
- console.log(this.userMsg)
- },
- change() {
- this.userMsg.age = '18'
- console.log('修改数据年龄后')
- console.log(this.userMsg)
- },
- deleteH() {
- let newUserMsg = { ...this.userMsg }
- delete newUserMsg.height
- this.userMsg = newUserMsg
- console.log('删除身高数据后')
- console.log(this.userMsg)
- }
- },
- }
- </script>
- <style>
- .home{
- margin: 0 auto;
- text-align: center;
- }
- </style>
Vue.set(obj, key, value)
:将响应式对象 obj
的属性 key
设置为 value
,如果 obj
是响应式的,则确保这个属性也是响应式的,并触发视图更新。Vue.delete(obj, key)
:删除响应式对象 obj
的属性 key
,并触发视图更新。- <template>
- <div class="home">
- <h3>这是vue2测试页面</h3>
- <p>用户信息: {{ userMsg }} </p>
- <p><button @click="add">添加用户性别为男</button></p>
- <p><button @click="change">修改用户年龄为18</button></p>
- <p><button @click="deleteH">删除用户身高信息</button></p>
- </div>
- </template>
-
- <script>
-
- export default {
- data() {
- return {
- // 用户信息
- userMsg: {
- name: 'zs',
- age: '20',
- height: '180',
- }
- }
- },
- methods: {
- // 为对象直接添加数据
- add() {
- // this.userMsg.sex = '男'
- // let newUserMsg = { ...this.userMsg }
- // newUserMsg.sex = '男'
- // this.userMsg = newUserMsg
- this.$set(this.userMsg, 'sex', '男');
- console.log('添加性别后')
- console.log(this.userMsg)
- },
- change() {
- this.userMsg.age = '18'
- console.log('修改数据年龄后')
- console.log(this.userMsg)
- },
- deleteH() {
- // let newUserMsg = { ...this.userMsg }
- // delete newUserMsg.height
- // this.userMsg = newUserMsg
- this.$delete(this.userMsg, 'height');
- console.log('删除身高数据后')
- console.log(this.userMsg)
- }
- },
- }
- </script>
- <style>
- .home{
- margin: 0 auto;
- text-align: center;
- }
- </style>
直接为data里的数组通过直接修改索引对应的值,页面未能渲染最新数据、即未触发dom的更新。
- <template>
- <div class="home">
- <h3>这是vue2测试页面</h3>
- <!-- <p>用户信息: {{ userMsg }} </p> -->
- <p>用户家人信息: {{ userMsg.family }} </p>
- <!-- <p><button @click="add">添加用户性别为男</button></p>
- <p><button @click="change">修改用户年龄为18</button></p>
- <p><button @click="deleteH">删除用户身高信息</button></p> -->
- <p><button @click="changeFather">修改父亲信息</button></p>
- </div>
- </template>
-
- <script>
-
- export default {
- data() {
- return {
- // 用户信息
- userMsg: {
- name: 'zs',
- age: '20',
- height: '180',
- family: [{ name: 'father', age: '45', sex: '男' }, { name: 'mother', age: '42', sex: '女' }]
- }
- }
- },
- methods: {
- // 为对象直接添加数据
- add() {
- // this.userMsg.sex = '男'
- // let newUserMsg = { ...this.userMsg }
- // newUserMsg.sex = '男'
- // this.userMsg = newUserMsg
- this.$set(this.userMsg, 'sex', '男');
- console.log('添加性别后')
- console.log(this.userMsg)
- },
- change() {
- this.userMsg.age = '18'
- console.log('修改数据年龄后')
- console.log(this.userMsg)
- },
- deleteH() {
- // let newUserMsg = { ...this.userMsg }
- // delete newUserMsg.height
- // this.userMsg = newUserMsg
- this.$delete(this.userMsg, 'height');
- console.log('删除身高数据后')
- console.log(this.userMsg)
- },
- // 修改父亲信息
- changeFather() {
- this.userMsg.family[0] = { name: 'father', age: '40', sex: '男' ,height: '180'}
- // this.userMsg.family[0].age = '40' // dom 是会更新的
- console.log('修改后的父亲信息')
- console.log(this.userMsg.family)
- },
- },
- }
- </script>
- <style>
- .home{
- margin: 0 auto;
- text-align: center;
- }
- </style>
点击了修改父亲信息按钮、数据更新成功、但是页面未同步渲染、即为dom未更新
和场景一差不多
方案一 浅拷贝数组每条数据到一个新数组 对浅拷贝的数组数据操作 将浅拷贝的数组数据赋值给数组数据
方案二 this.$set() 方法
- <template>
- <div class="home">
- <h3>这是vue2测试页面</h3>
- <!-- <p>用户信息: {{ userMsg }} </p> -->
- <p>用户家人信息: {{ userMsg.family }} </p>
- <!-- <p><button @click="add">添加用户性别为男</button></p>
- <p><button @click="change">修改用户年龄为18</button></p>
- <p><button @click="deleteH">删除用户身高信息</button></p> -->
- <p><button @click="changeFather">修改父亲信息</button></p>
- </div>
- </template>
-
- <script>
-
- export default {
- data() {
- return {
- // 用户信息
- userMsg: {
- name: 'zs',
- age: '20',
- height: '180',
- family: [{ name: 'father', age: '45', sex: '男' }, { name: 'mother', age: '42', sex: '女' }]
- },
- }
- },
- methods: {
- // 为对象直接添加数据
- add() {
- // this.userMsg.sex = '男'
- // let newUserMsg = { ...this.userMsg }
- // newUserMsg.sex = '男'
- // this.userMsg = newUserMsg
- this.$set(this.userMsg, 'sex', '男');
- console.log('添加性别后')
- console.log(this.userMsg)
- },
- change() {
- this.userMsg.age = '18'
- console.log('修改数据年龄后')
- console.log(this.userMsg)
- },
- deleteH() {
- // let newUserMsg = { ...this.userMsg }
- // delete newUserMsg.height
- // this.userMsg = newUserMsg
- this.$delete(this.userMsg, 'height');
- console.log('删除身高数据后')
- console.log(this.userMsg)
- },
- // 修改父亲信息
- changeFather() {
- // this.userMsg.family[0] = { name: 'father', age: '40', sex: '男' ,height: '180'}
- // this.userMsg.family[0].age = '40' // dom 是会更新的
-
- // 方案一 浅拷贝数组每条数据到一个新数组 对浅拷贝的数组数据操作 将浅拷贝的数组数据赋值给数组数据
- let newArr = [ ...this.userMsg.family ]
- newArr[0] = { name: 'father', age: '40', sex: '男' ,height: '180'}
- this.userMsg.family = newArr
- console.log(this.userMsg)
-
- // 方案二 this.$set() 方法
- // this.$set(this.userMsg.family, 0, { name: 'father', age: '40', sex: '男' });
- },
- },
- }
- </script>
- <style>
- .home{
- margin: 0 auto;
- text-align: center;
- }
- </style>
- // 定义一个构造函数 Dep,用于收集依赖和通知更新
- function Dep() {
- this.subs = [];
- }
-
- Dep.prototype = {
- addSub(sub) {
- this.subs.push(sub);
- },
- notify() {
- this.subs.forEach(sub => {
- sub.update();
- });
- }
- };
-
- // 定义一个监听器 Observer,用于劫持对象的属性
- function Observer(data) {
- this.data = data;
- this.walk(data);
- }
-
- Observer.prototype = {
- walk(data) {
- Object.keys(data).forEach(key => {
- this.defineReactive(data, key, data[key]);
- });
- },
- defineReactive(data, key, val) {
- const dep = new Dep();
- Object.defineProperty(data, key, {
- enumerable: true,
- configurable: true,
- get() {
- if (Dep.target) {
- dep.addSub(Dep.target);
- }
- return val;
- },
- set(newVal) {
- if (val === newVal) {
- return;
- }
- val = newVal;
- dep.notify();
- }
- });
- }
- };
-
- // 定义一个 Watcher,用于进行依赖收集和更新
- function Watcher(vm, exp, cb) {
- this.vm = vm;
- this.exp = exp;
- this.cb = cb;
- this.value = this.get();
- }
-
- Watcher.prototype = {
- get() {
- Dep.target = this;
- const value = this.vm[this.exp];
- Dep.target = null;
- return value;
- },
- update() {
- const value = this.vm[this.exp];
- this.cb.call(this.vm, value);
- }
- };
-
- // 宮户 Vue 构造函数
- function Vue(options) {
- this.data = options.data;
- new Observer(this.data);
- // 初始化一个 Watcher 对象,用于触发依赖收集
- new Watcher(this, 'data', () => {
- console.log('数据更新了');
- });
-
- // 其他 Vue 相关逻辑...
- }
-
- // 创建一个 Vue 实例
- var vm = new Vue({
- data: {
- message: 'Hello, Vue!'
- }
- });
-
- // 修改数据,触发更新
- vm.data.message = 'Hello, New Vue!';
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。