Vue学习—深入剖析响应式

tech2024-10-21  22

响应式

一、响应式一1.怎么更改数据2.vm.$el3.vm.$nextTick & Vue.nextTick 二、响应式二三、响应式原理简述

一、响应式一

vue的响应式就是当数据变化,页面就会重新渲染。

1.怎么更改数据

<div id="app"> {{ msg }} </div> <script> const vm = new Vue({ el: '#app', data: { msg: '数据修改前' } }); setTimeout(()=>{//修改数据的方法有很多 vm.msg = '数据修改后'; },1000); </script>

问:为什么data会直接出现在vm实例对象中咧?

答:当创建vue实例时,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据变化,执行某个监听函数(怎么实现的?想一想,提示:Object.defineProperty,试着实现一下)

问:实例中除了data数据外,其他东西是啥子?

为了防止名称冲突。因为会将data中数据代理给vue,假如说我们自己写的data名称和vue中自带的属性冲突了,那么就会覆盖vue内部的属性,所以vue会把自己内部的属性成员名称前加上$或_,如果加上的是$,代表是我们可以使用的,如果加上的是_,是vue自己内部使用的方法或属性,我们不需要调用

更改的数据必须是存在的数据,否则不能重新渲染页面,因为他监听不到,如: <!-- 即使更改了数据,也不会重新渲染页面 --> <div id="app"> {{ person.wife }} </div> const vm = new Vue({ el: '#app', data: { person: { name: 'jimo', age: 18, } } }); setTimeout(()=>{ vm.person.wife = 'liu';//不会重新渲染 },1000); 更改的数据必须已渲染过的数据,否则从性能角度考虑,不会重新渲染页面,如: <!-- 即使更改了数据,也不会重新渲染页面 --> <div id="app"> {{ person.age}} </div> const vm = new Vue({ el: '#app', data: { msg: '数据修改前', person: { name: 'jimo', age: 18 } } }) setTimeout(()=>{ vm.msg = '数据修改后';//页面页面没有变化 },1000) 注意 <div id="app"> {{ msg }} {{ person.wife }}//刚开始未经声明 </div> <script> const vm = new Vue({ el: '#app', data: { msg: '数据修改前', person: { name: 'jimo', age: 18 } } }); setTimeout(()=>{ vm.person.wife = 'liu';//声明赋值 vm.msg = '数据修改后';//渲染页面后都出现 },1000); </script>

更改数据后,页面会立刻重新渲染吗?

vue更新DOM的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和DOM操作。

同步执行栈执行完毕后,会执行异步队列

<div id="app">{{ msg }}</div> const vm = new Vue({ el: '#app', data: { msg: '数据修改前' } }) vm.msg = '数据修改后'; console.log(vm.msg); // 数据修改后,此时数据已更改 console.log(vm.$el.innerHTML); // 数据修改前。此时页面还未重新渲染

2.vm.$el

值为被Vue控制的元素(或者说,Vue挂载的元素)

3.vm.$nextTick & Vue.nextTick

如何在更改数据后,看到渲染后的页面上的值?

答:利用vm.$nextTick或Vue.nextTick,在页面重新渲染,DOM更新后,会立刻执行vm.$nextTick

<div id="app">{{ msg }}</div> const vm = new Vue({ el: '#app', data: { msg: '数据修改前' } }) vm.msg = '数据修改后'; console.log(vm.msg); // 数据修改后,此时数据已更改 // 1. 使用vm.$nextTick vm.$nextTick(() => { console.log(vm.$el.innerHTML); // 数据修改后 }) // 2. 使用Vue.nextTick Vue.nextTick(() => { console.log(vm.$el.innerHTML); // 数据修改后 }) vm.nextTick和Vue.nextTick还可以作为Promise使用 <div id="app">{{ msg }}</div> const vm = new Vue({ el: '#app', data: { msg: '数据修改前' } }) vm.msg = '数据修改后'; // 1. 使用vm.$nextTick vm.$nextTick().then(() => { console.log(vm.$el.innerHTML); // 数据修改后 }) // 2. 使用Vue.nextTick Vue.nextTick().then(() => { console.log(vm.$el.innerHTML); // 数据修改后 }) vm.$nextTick 和 Vue.nextTick的区别?

Vue.nextTick内部函数的this指向window

Vue.nextTick(function () { console.log(this); // window })

vm.$nextTick内部函数的this指向Vue实例对象

vm.$nextTick(function () { console.log(this); // vm实例 }) 好奇nextTick是怎么实现的吗?异步任务分为宏任务(macro)和微任务(micro)前面章节已经讲过事件循环.宏任务比较慢(如setTimeout等),微任务比较快(如Promise.then()等)微任务在前,宏任务在后(eventloop,事件环) // 控制台打印顺序:promise > timeout setTimeout(() => { console.log('timeout'); }, 0) Promise.resolve().then(() => { console.log('promise'); }) 在nextTick的实现源码中,会先判断是否支持微任务,不支持后,才会执行宏任务 if(typeof Promise !== 'undefined') { // 微任务 // 首先看一下浏览器中有没有promise // 因为IE浏览器中不能执行Promise const p = Promise.resolve(); } else if(typeof MutationObserver !== 'undefined') { // 微任务 // 突变观察 // 监听文档中文字的变化,如果文字有变化,就会执行回调 // vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数 } else if(typeof setImmediate !== 'undefined') { // 宏任务 // 只在IE下有 } else { // 宏任务 // 如果上面都不能执行,那么则会调用setTimeout } 曾经vue用过的宏任务:MessageChannel 消息通道 宏任务(取消了)

二、响应式二

除了未被声明过和未被渲染的数据外,还有什么数据更改后不会渲染页面?

1. 利用索引直接设置一个数组项时:

<!-- 即使向数组中添加了第4项,数组仍然显示3项 --> <!-- 咳咳,一家三口,有第4个人也不能摆出来给大家看呀~ --> <div id="app">{{ Family }}</div> const vm = new Vue({ el: '#app' data: { Family: ['爸爸', '妈妈', '儿子'] } }) vm.Family[3] = '小三'; // 不是响应式的,页面不变化

2. 修改数组的长度时:

<!-- 更改了数组长度后,数组仍然显示1项 --> <div id="app">{{ arr }}</div> const vm = new Vue({ el: '#app' data: { arr: ['小刘'] } }) vm.arr.length = 0; // 不是响应式的,页面不变化

3. 添加或删除对象:

<!-- 身高还是那个身高,媳妇也只有一个,不要痴心妄想 --> <div id="app">{{ person}}</div> const vm = new Vue({ el: '#app' data: { person: { name: 'jimo', age: 18 } } }) vm.person.sex= 'man'; // 不是响应式的 delete vm.person.sex; // 不是响应式的

问:要如何响应式的更新数组和对象?

更改数组:

利用数组变异方法:push、pop、shift、unshift、splice、sort、reverse利用vm.$set/Vue.set实例方法利用vm.$delete或Vue.delete删除数组中的某一项

vm.$set是Vue.set的别名 使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)

vm.$delete是Vue.delete的别名 使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)

<div id="app">{{ arr }}</div> const vm = new Vue({ el: '#app', data: { arr: [1, 2, 3] } }) // 使用数组变异方法 vm.arr.push('4'); // 使用vm.$set vm.$set(vm.arr, 3, 99);

<div id="app">{{ arr }}</div> const vm = new Vue({ el: '#app' data: { arr: ['小刘'] } }) // 更改长度时,可以用数组的splice方法 vm.dengWife.splice(100);

更改对象:

添加利用vm.$set/Vue.set实例方法删除利用vm.$delete/Vue.delete方法 <div id="app">{{ person }}</div> const vm = new Vue({ el: '#app' data: { person: { name: 'jimo', age: 18 } } }) // 添加 vm.$set(vm.person, 'sex', 'man'); // 删除 vm.$delete(vm.person, 'age') 总结:

更改数组用变异方法,就够了 更改对象就用vm.$set和vm.$delete

三、响应式原理简述

利用Object.defineProperty实现响应式的劣势

天生就需要进行递归监听不到数组不存在的索引的改变监听不到数组长度的改变监听不到对象的增删
最新回复(0)