这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
reactive
之前我们看了一个关于ref和computed的将原始类型转换为响应式对象的小案例:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ count }}</h1>
<h1>{{ double }}</h1>
<button @click="increase">👍+1</button>
</div>
</template>
<script lang="ts">
import { ref, computed } from "vue";
export default {
name: "App",
setup() {
const count = ref(0);
const double = computed(() => {
return count.value * 2;
});
const increase = () => {
count.value++;
};
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>
但是这个案例中使用ref一般使用的是原始类型,而且几个变量也非常分散,那如果我们想让这些变量全部都包裹在一个变量当中,这时候我们就可以使用reactive来实现。我们来看一下改写的代码:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ data.count }}</h1>
<h1>{{ data.double }}</h1>
<button @click="data.increase">👍+1</button>
</div>
</template>
<script lang="ts">
import { ref, computed, reactive } from "vue";
interface DataProps {
count: number;
double: number;
increase: () => void;
}
export default {
name: "App",
setup() {
const data: DataProps = reactive({
count: 0,
increase: () => {
data.count++;
},
double: computed(() => data.count * 2),
});
return {
data,
};
},
};
</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>
实现的效果:
注意:
const data = reactive({
count: 0,
increase: () => {
data.count++;
},
double: computed(() => data.count * 2),
});
这里如果我们不使用interface他会给我们提示一个类型错误报错。报错信息如下:
'data' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
35 | // };
36 | // const data: DataProps = reactive({
> 37 | const data = reactive({
| ^^^^
38 | count: 0,
39 | increase: () => {
40 | data.count++;
这是因为我们在computed回调中呢使用data.count会造成一个类型推论的循环。所以我们要使用interface新建一个类型来解决。
优化:
这个时候又会发现我们频繁地使用data.那么我们能不能使用ES6中的扩展运算符( spread )操作来把对象展开。
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ count }}</h1>
<h1>{{ double }}</h1>
<button @click="increase">👍+1</button>
</div>
</template>
<script lang="ts">
import { ref, computed, reactive } from "vue";
interface DataProps {
count: number;
double: number;
increase: () => void;
}
export default {
name: "App",
setup() {
const data: DataProps = reactive({
count: 0,
increase: () => {
data.count++;
},
double: computed(() => data.count * 2),
});
return {
...data,
};
},
};
</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>
效果:
或者使用最基本的方式:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ count }}</h1>
<h1>{{ double }}</h1>
<button @click="increase">👍+1</button>
</div>
</template>
<script lang="ts">
import { ref, computed, reactive } from "vue";
interface DataProps {
count: number;
double: number;
increase: () => void;
}
export default {
name: "App",
setup() {
const data: DataProps = reactive({
count: 0,
increase: () => {
data.count++;
},
double: computed(() => data.count * 2),
});
return {
count: data.count,
increase: data.increase,
double: data.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>
效果:
这是为什么呢,我们看一下count的类型:
那么它变成了number类型所以它不具备响应式了才会出现这个结果。
toRefs
这个时候我们使用toRefs就解决了。
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
代码:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{ count }}</h1>
<h1>{{ double }}</h1>
<button @click="increase">👍+1</button>
</div>
</template>
<script lang="ts">
import { ref, computed, reactive } from "vue";
interface DataProps {
count: number;
double: number;
increase: () => void;
}
export default {
name: "App",
setup() {
const data: DataProps = reactive({
count: 0,
increase: () => {
data.count++;
},
double: computed(() => data.count * 2),
});
const refData = toRefs(data);
return {
...refData,
};
},
};
</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>
效果: