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。
还有一种更方便的方法让你无需return,就能将数据和函数暴露给那就是setup语法糖
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中写到才会被获取到。