Setup语法糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script lang="ts">
export default{
name: "Test",
setup() {
let a = 1;
let b = 2;
function change() {
a = 3;
b = 4;
}
return { a, b, change };
},
}

</script>

vue2中使用data和method来分开数据和方法

vue3中将数据和方法直接放到了setup()中。

至于vue2中的data和method,setup无法使用,但是data和method可以使用setup中return的数据。所以一个vue项目中要明确使用vue2还是vue3的语法,前者叫Options API,后者叫Composition API

如果要在

还有一种更方便的方法让你无需return,就能将数据和函数暴露给

1
2
<script setup lang="ts">
</script>

但是这里面不能写export defalt{},所以name的定义要另外写一个不包含setup的script,或者是用插件实现在script标签里定义name

接下来讨论的就是compositionAPI

Ref和Reactive

setup中的一般数据不是响应式的,也就是在方法中改变了数据时,template里的这个数据不会改变,会按照旧值继续显示。而ref和reactive就是两个能让数据变为响应式的方法。

ref使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script lang="ts">
export default {
name: 'Person',
};
</script>

<script setup lang="ts">
import { ref } from 'vue';

let count = ref<number>(0);

function changeCount() {
count.value++;
}
</script>

<template>
<div id = 'person'>
数字:{{ count }}
<button @click="changeCount">+</button>
</div>
</template>

<style scoped>
</style>

这就是ref定义基本数据类型的方法,注意到需要修改count的对象时,修改的是count.value。这是因为ref创建的是一个对象,value是我们用到的修改内容的地方。

ref也可以定义对象,同样也要用value获取值

vscode中自动补全ref的value的方法:打开设置,搜索vue,找到DOT Value勾选

reactive使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script lang="ts">
export default {
name: 'Person',
};
</script>

<script setup lang="ts">
import { reactive } from 'vue';

let car = reactive({
brand: 'Ford',
model: 'Mustang',
year: 1969,
color: 'red'
});

let books = reactive([
{
name: 'vue3',
id: 1
},
{
name: 'vue2',
id: 2
}
]);
function changeYear() {
car.year = 2024;
}
function changeName() {
books[0].name = 'react';
}
</script>

reactive只能定义一个对象,但是它不需要用value来访问数据。但是重新赋值reactive对象需要使用assign方法,不然会失去响应式功能。

ref对比reactive

  • ref能定义基本类型和对象,reactive只能定义对象
  • ref访问数据要用value,但是reactive不需要
  • reactive定义的数据如果说被重新分配,将会失去响应式的能力,必须使用assign方法

原则:创建基本类型用ref,创建对象两者均可,层级深的对象用reactive(不用写value)

toRef与toRefs

toRef 和 toRefs 的作用都是解构一个reactive,将其转换为 ref 类型。

而且被解构的变量同样是响应式的,reactive, toRef, toRefs 解构的每一个同一个来源的变量(如下图中的age),受到改变时,其他几个也会跟着改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup lang="ts">
import { toRef, toRefs, reactive } from 'vue';
let person = reactive({
name : 'jason',
age: 11
});
let {name, age} = toRefs(person);
let _age = toRef(person, 'age');

function changeAge() {
age.value += 1;
person.age += 1;
_age.value += 1;
console.log(_age.value, person.age, age.value);
//out: 14, 14, 14

}
</script>

但是如果直接解构,得到的变量就是独立不受改变的了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script setup lang="ts">

import { toRef, toRefs, reactive } from 'vue';
let person = reactive({
name : 'jason',
age: 11,
sex: "male",
height: 180
});
let {name : personName, age : personAge} = (person);
let _age = toRef(person, 'age');

function changeAge() {
personAge += 1;
person.age += 1;
_age.value += 1;
console.log(_age.value, person.age, personAge);
// out: 13, 13, 12
}

</script>

Computed计算属性

v-model

如果正常使用v-bind:value=”val”(简写:value=”val”),输入框中的value改变不会反应到script中的变量,也就是不会返回到val。使用v-model=”val”则可以。

Computed

这是一个可以按照变量值返回新的ref对象的方法。

我们来写一个通过合并输入的用户名和性别来合成用户id的样例程序。{userName, sex, userId}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup lang="ts">

import { toRef, toRefs, reactive, computed, ref } from 'vue';
let userName = ref<String>("jaychou");
let sex = ref<String>("man");

let userId = computed(() => {
return userName.value + "-" + sex.value;
});

</script>

<template>
<div id = 'person'>
用户名: <input type = "text" v-model="userName"> <br>
性别: <input type = "text" v-model="sex"> <br>
用户ID: {{userId}}
</div>
</template>

输入框中的值发生变化,反应到userName和sex上。之后computed检测到变量发生变化,对userId进行处理。(注意:若computed中的操作的变量值(userName和sex)发生变化,他才会执行)。

进阶用法

如果使用上面👆这种lambda表达式的方法书写computed方法,会发现userId无法被赋值,因为它的值是被其他变量确定的。我们可以通过setter来改变这些确定userId的变量来改变userId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script setup lang="ts">

import { toRef, toRefs, reactive, computed, ref } from 'vue';
let userName = ref<String>("jaychou");
let sex = ref<String>("man");

let userId = computed({
get(){
return userName.value + "-" + sex.value;
},
set(val){
console.log("userId changed");
let [_name, _sex] = val.split("-");
userName.value = _name;
sex.value = _sex;
}
});

function changeUserName() {
userId.value = "jolin-woman";
}

</script>

<template>
<div id = 'person'>
用户名: <input type = "text" v-model="userName"> <br>
性别: <input type = "text" v-model="sex"> <br>
用户ID: {{userId}}
</div>
<button @click="changeUserName">jay</button>
</template>

computed中的set函数可以捕获赋值给userId的值,拆分开来赋值给:控制userId值的变量来间接改变userId。set里显然不能直接改变userId.value,因为这样会再次调用set函数,造成死循环。

ref标签

ref也可以是一个html标签上的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup lang="ts">

import { ref } from 'vue';

let num = ref(0);
let numHtml = ref(null);
function updateNum() {
num.value++;
console.log(numHtml.value); //<div data-v-...>1</div> data-v这个数据是由scoped引起的,用于唯一化这个标签的样式
}

</script>

<template>
<div>
<div ref = "numHtml">{{ num }}</div>
</div>
<button @click="updateNum">Update Num</button>
</template>

由代码中可以看到,在标签中添加numHtml,同时在代码中定义一个numHtml的ref变量就可以输出这个标签的内容。

这是放在html标签的情况,如果说放到组件标签里,ref对象管理的就是一个实例了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//in App.vue
<template>
<div id = 'app'>
<h1>
Hello Vue 3!
</h1>
<button @click="outPerson">Change</button>
<Person ref = "person" />

</div>

</template>

<script lang = "ts" setup>
import Person from './components/Person.vue';
import { ref } from 'vue';
let person = ref(null);

function outPerson() {
console.log(person.value);
}

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//in Person.vue
<script setup lang="ts">

import { ref, defineExpose } from 'vue';

let num = ref(0);
let numHtml = ref(null);
function updateNum() {
num.value++;
console.log(numHtml.value);
}

defineExpose({
num,
updateNum,
numHtml
});

</script>

如果Person组件中不定义defineExpose,App中不会获取到任何值。就相当于这些数据默认是private的,只有在defineExpose中写到才会被获取到。