1.改写计数器例子:
Vue2
中的代码:
<template>
<p>{{count}}</p>
<button @click="increase">👍</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
data(){
return {
count:0
}
},
methods:{
increase(){
this.count++;
}
}
});
</script>
使用vue3
改写后的代码:
setup
:在生命周期之前运行的- 通过代码可以发现,
increase
函数改变count
的值是改变的count
对象的value
,但template
中展示值是直接通过count
展示,而不是count.value
computed
:是一个函数,接收一个参数
:回调函数,回调函数中处理想要返回的值
<template>
<p>{{count}}</p>
<p>{{double}}</p>
<button @click="increase">👍</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {ref,computed} from "vue";
export default defineComponent({
name: 'App',
setup(){
const count = ref(0);
const increase = () => {
count.value ++;
}
const double = computed(()=>{
return count.value * 2;
})
return {
count,increase,double
}
}
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
结果如下:
2.更进一步,reactive
1.使用reactive
实现计数器:
-
reactive
:reactive
是一个函数,参数是一个对象;通过reactive
包装后返回一个响应式的数据data
-
因为
computed
的原因,需要使用interface
去约束下data
的类型,否则如下图:上图的
data
会报错,解决方案就是添加interface
去约束,代码如下:
<template>
<p>{{data.count}}</p>
<p>{{data.double}}</p>
<button @click="data.increase">👍</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {ref,computed,reactive} from "vue";
interface DataProps {
count:number;
double:number;
increase:() => void;
}
export default defineComponent({
name: 'App',
setup(){
const data:DataProps = reactive({
count:0,
increase:()=>{
data.count++;
},
double:computed(()=>{
return data.count * 2
})
})
return {
data
}
}
});
</script>
2.无效的优化方案:
这里面看到data.count...
等的很麻烦,想要在return {data}
中对data
进行解构或者es6的"..."展开操作符
,但是解构后发现页面计数器失效
,因为直接通过下面的方式:
//略
//页面点击无反应
return {
...data
}
//或者
//页面点击后依然无反应
return {
count:data.count,
increase:data.increase,
double:data.double
}
3.正确使用toRefs
去优化:(响应式对象——->普通对象)
-
toRefs
:使用一个reactive对象
作为参数,返回一个普通的对象
-
通过
toRefs
转化后的refData
如下图,是一个Ref
类型,然后展开refData
即可。 -
代码如下:
<template>
<p>{{count}}</p>
<p>{{double}}</p>
<button @click="increase">👍</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {ref,computed,reactive,toRefs} from "vue";
interface DataProps {
count:number;
double:number;
increase:() => void;
}
export default defineComponent({
name: 'App',
setup(){
const data:DataProps = reactive({
count:0,
increase:()=>{
data.count++;
},
double:computed(()=>{
return data.count * 2
})
})
const refData = toRefs(data);
return {
...refData
}
}
});
</script>
3.ref
和reactive
的区别:
上面使用了变量声明、ref、以及reactive
三种方式去定义变量:
用ref
定义的数据,打印结果是一个被对象包裹的响应的数据
,使用reactive
的方式和纯变量声明
的方式打印结果是一样的,这是什么原因呢?
在控制台输出一个警告信息, 提示100
这个值不能被reactive
创建。
官方也推荐我们在定义数据
的时候,reactive
定义复杂的数据类型
的数据,ref
推荐定义基本数据类型
,所以如果要使用reactive
定义基本数据类型
的话,需要在reactive
中将数据包装
一下。
- 但是使用
reactive
定义的数据和ref
定义的数据打印结果有一些差异:
ref
定义的数据打印结果需要.value
才能获取到结果
,而reactive
则不需要
总结:
reactive
和ref
都是用来定义响应式数据
的。reactive
更推荐去定义复杂的数据类型
,不能直接解构,需要需要使用toRefs()
转成普通对象后解构;ref
更推荐定义基本类型
ref
和reactive
本质可以简单地理解为ref
是对reactive
的二次包装
,ref
定义的数据访问的时候要多一个.value
- 使用
ref
定义基本数据类型
,ref
也可以定义数组
和对象
。
4.对比vue2
和vue3
的响应式
vue2的响应式:
-
通过
object.defineproperty()
实现响应式的,由于这个方法的限制,对于对象上新增的属性是无能为力的; 对于数组
则需要拦截
它的原型方法
来完成响应式 -
对于对象:
-
对于数组:
为此vue
中还可以使用vm.$set
方法。官网链接:cn.vuejs.org/v2/guide/re…
vue3的响应式:
-
利用了
proxy
实现的响应式如果有一个
data
对象,那么使用defineProperty
和Proxy
拦截如下
// defineProperty
Object.defineProperty(data,"name",{
get(){},
set(){},
})
//Proxy
new Proxy(data,{
get(key){},
set(key,value){}
})
defineProperty
是针对某个明确的属性的,对于新增的属性无能为力Proxy
更像是从更高的维度去,针对于整个data对象
的。