|
|
### setup 概述
`setup`是`Vue3`中一个新的配置项,值是一个函数,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在`setup`中。
特点如下:
- `setup`函数返回的对象中的内容,可直接在模板中使用。- `setup`中访问`this`是`undefined`。- `setup`函数会在`beforeCreate`之前调用,它是“领先”所有钩子执行的。
```vue<template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div></template>
<script lang="ts"> export default { name:'Person', setup(){ // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据) let name = '张三' let age = 18 let tel = '13888888888'
// 方法,原来写在methods中 function changeName(){ name = 'zhang-san' //注意:此时这么修改name页面是不变化的 console.log(name) } function changeAge(){ age += 1 //注意:此时这么修改age页面是不变化的 console.log(age) } function showTel(){ alert(tel) }
// 返回一个对象,对象中的内容,模板中可以直接使用 return {name,age,tel,changeName,changeAge,showTel} } }</script>```
### setup 的返回值
- 若返回一个**对象**:则对象中的:属性、方法等,在模板中均可以直接使用**(重点关注)。**- 若返回一个**函数**:则可以自定义渲染内容,代码如下:
```jsxsetup(){ return ()=> '你好啊!'}```
### setup 与 Options API 的关系
- `Vue2` 的配置(`data`、`methos`......)中**可以访问到** `setup`中的属性、方法。- 但在`setup`中**不能访问到**`Vue2`的配置(`data`、`methos`......)。- 如果与`Vue2`冲突,则`setup`优先。
### setup 语法糖
`setup`函数有一个语法糖,这个语法糖,可以让我们把`setup`独立出去,代码如下:
```vue<template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changName">修改名字</button> <button @click="changAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div></template>
<script lang="ts"> export default { name:'Person', }</script>
<!-- 下面的写法是setup语法糖 --><script setup lang="ts"> console.log(this) //undefined // 数据(注意:此时的name、age、tel都不是响应式数据) let name = '张三' let age = 18 let tel = '13888888888'
// 方法 function changName(){ name = '李四'//注意:此时这么修改name页面是不变化的 } function changAge(){ console.log(age) age += 1 //注意:此时这么修改age页面是不变化的 } function showTel(){ alert(tel) }</script>```
扩展:上述代码,还需要编写一个不写`setup`的`script`标签,去指定组件名字,比较麻烦,我们可以借助`vite`中的插件简化
1. 第一步:`npm i vite-plugin-vue-setup-extend -D`2. 第二步:`vite.config.ts`
```jsximport { defineConfig } from 'vite'import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({ plugins: [ VueSetupExtend() ]})```
3. 第三步:`<script setup lang="ts" name="Person">`
## 【ref 创建:基本类型的响应式数据】
- **作用:**定义响应式变量。- **语法:**`let xxx = ref(初始值)`。- **返回值:**一个`RefImpl`的实例对象,简称`ref对象`或`ref`,`ref`对象的`value`**属性是响应式的**。- **注意点:** - `JS`中操作数据需要:`xxx.value`,但模板中不需要`.value`,直接使用即可。 - 对于`let name = ref('张三')`来说,`name`不是响应式的,`name.value`是响应式的。
```vue<template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div></template>
<script setup lang="ts" name="Person"> import {ref} from 'vue' // name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。 let name = ref('张三') let age = ref(18) // tel就是一个普通的字符串,不是响应式的 let tel = '13888888888'
function changeName(){ // JS中操作ref对象时候需要.value name.value = '李四' console.log(name.value)
// 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。 // name = ref('zhang-san') } function changeAge(){ // JS中操作ref对象时候需要.value age.value += 1 console.log(age.value) } function showTel(){ alert(tel) }</script>```
## 【reactive 创建:对象类型的响应式数据】
- **作用:**定义一个**响应式对象**(基本类型不要用它,要用`ref`,否则报错)- **语法:**`let 响应式对象= reactive(源对象)`。- **返回值:**一个`Proxy`的实例对象,简称:响应式对象。- **注意点:**`reactive`定义的响应式数据是“深层次”的。
```vue<template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <h2>游戏列表:</h2> <ul> <li v-for="g in games" :key="g.id">{{ g.name }}</li> </ul> <h2>测试:{{obj.a.b.c.d}}</h2> <button @click="changeCarPrice">修改汽车价格</button> <button @click="changeFirstGame">修改第一游戏</button> <button @click="test">测试</button> </div></template>
<script lang="ts" setup name="Person">import { reactive } from 'vue'
// 数据let car = reactive({ brand: '奔驰', price: 100 })let games = reactive([ { id: 'ahsgdyfa01', name: '英雄联盟' }, { id: 'ahsgdyfa02', name: '王者荣耀' }, { id: 'ahsgdyfa03', name: '原神' }])let obj = reactive({ a:{ b:{ c:{ d:666 } } }})
function changeCarPrice() { car.price += 10}function changeFirstGame() { games[0].name = '流星蝴蝶剑'}function test(){ obj.a.b.c.d = 999}</script>```
## 【ref 创建:对象类型的响应式数据】
- 其实`ref`接收的数据可以是:**基本类型**、**对象类型**。- 若`ref`接收的是对象类型,内部其实也是调用了`reactive`函数。
```vue<template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <h2>游戏列表:</h2> <ul> <li v-for="g in games" :key="g.id">{{ g.name }}</li> </ul> <h2>测试:{{obj.a.b.c.d}}</h2> <button @click="changeCarPrice">修改汽车价格</button> <button @click="changeFirstGame">修改第一游戏</button> <button @click="test">测试</button> </div></template>
<script lang="ts" setup name="Person">import { ref } from 'vue'
// 数据let car = ref({ brand: '奔驰', price: 100 })let games = ref([ { id: 'ahsgdyfa01', name: '英雄联盟' }, { id: 'ahsgdyfa02', name: '王者荣耀' }, { id: 'ahsgdyfa03', name: '原神' }])let obj = ref({ a:{ b:{ c:{ d:666 } } }})
console.log(car)
function changeCarPrice() { car.value.price += 10}function changeFirstGame() { games.value[0].name = '流星蝴蝶剑'}function test(){ obj.value.a.b.c.d = 999}</script>```
## 【ref 对比 reactive】
宏观角度看:
> 1. `ref`用来定义:**基本类型数据**、**对象类型数据**;
>> 2. `reactive`用来定义:**对象类型数据**。
- 区别:
> 1. `ref`创建的变量必须使用`.value`(可以使用`volar`插件自动添加`.value`)。
>> <img src="C:/Users/Administrator/Desktop/资料/images/自动补充value.png" alt="自动补充value" style="zoom:50%;border-radius:20px" />
>> 2. `reactive`重新分配一个新对象,会**失去**响应式(可以使用`Object.assign`去整体替换)。
- 使用原则:
> 1. 若需要一个基本类型的响应式数据,必须使用`ref`。
> 2. 若需要一个响应式对象,层级不深,`ref`、`reactive`都可以。
> 3. 若需要一个响应式对象,且层级较深,推荐使用`reactive`。
## 【toRefs 与 toRef】
- 作用:将一个响应式对象中的每一个属性,转换为`ref`对象。- 备注:`toRefs`与`toRef`功能一致,但`toRefs`可以批量转换。- 语法如下:
```vue<template> <div class="person"> <h2>姓名:{{person.name}}</h2> <h2>年龄:{{person.age}}</h2> <h2>性别:{{person.gender}}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeGender">修改性别</button> </div></template>
<script lang="ts" setup name="Person"> import {ref,reactive,toRefs,toRef} from 'vue'
// 数据 let person = reactive({name:'张三', age:18, gender:'男'}) // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力 let {name,gender} = toRefs(person) // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力 let age = toRef(person,'age')
// 方法 function changeName(){ name.value += '~' } function changeAge(){ age.value += 1 } function changeGender(){ gender.value = '女' }</script>```
## 【computed】
作用:根据已有数据计算出新数据(和`Vue2`中的`computed`作用一致)。
<img src="C:/Users/Administrator/Desktop/资料/images/computed.gif" style="zoom:20%;" />
```vue<template> <div class="person"> 姓:<input type="text" v-model="firstName"> <br> 名:<input type="text" v-model="lastName"> <br> 全名:<span>{{fullName}}</span> <br> <button @click="changeFullName">全名改为:li-si</button> </div></template>
<script setup lang="ts" name="App"> import {ref,computed} from 'vue'
let firstName = ref('zhang') let lastName = ref('san')
// 计算属性——只读取,不修改 /* let fullName = computed(()=>{ return firstName.value + '-' + lastName.value }) */
// 计算属性——既读取又修改 let fullName = computed({ // 读取 get(){ return firstName.value + '-' + lastName.value }, // 修改 set(val){ console.log('有人修改了fullName',val) firstName.value = val.split('-')[0] lastName.value = val.split('-')[1] } })
function changeFullName(){ fullName.value = 'li-si' } </script>```
## 【watch】
- 作用:监视数据的变化(和`Vue2`中的`watch`作用一致)- 特点:`Vue3`中的`watch`只能监视以下**四种数据**:
> 1. `ref`定义的数据。
> 2. `reactive`定义的数据。
> 3. 函数返回一个值(`getter`函数)。
> 4. 一个包含上述内容的数组。
我们在`Vue3`中使用`watch`的时候,通常会遇到以下几种情况:
### * 情况一
监视`ref`定义的【基本类型】数据:直接写数据名即可,监视的是其`value`值的改变。
```vue<template> <div class="person"> <h1>情况一:监视【ref】定义的【基本类型】数据</h1> <h2>当前求和为:{{sum}}</h2> <button @click="changeSum">点我sum+1</button> </div></template>
<script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let sum = ref(0) // 方法 function changeSum(){ sum.value += 1 } // 监视,情况一:监视【ref】定义的【基本类型】数据 const stopWatch = watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) if(newValue >= 10){ stopWatch() } })</script>```
### * 情况二
监视`ref`定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。
> 注意:
>> * 若修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。
>> * 若修改整个`ref`定义的对象,`newValue` 是新值, `oldValue` 是旧值,因为不是同一个对象了。
```vue<template> <div class="person"> <h1>情况二:监视【ref】定义的【对象类型】数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> </div></template>
<script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let person = ref({ name:'张三', age:18 }) // 方法 function changeName(){ person.value.name += '~' } function changeAge(){ person.value.age += 1 } function changePerson(){ person.value = {name:'李四',age:90} } /* 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视 watch的第一个参数是:被监视的数据 watch的第二个参数是:监视的回调 watch的第三个参数是:配置对象(deep、immediate等等.....) */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true}) </script>```
### * 情况三
监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。
```vue<template> <div class="person"> <h1>情况三:监视【reactive】定义的【对象类型】数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> <hr> <h2>测试:{{obj.a.b.c}}</h2> <button @click="test">修改obj.a.b.c</button> </div></template>
<script lang="ts" setup name="Person"> import {reactive,watch} from 'vue' // 数据 let person = reactive({ name:'张三', age:18 }) let obj = reactive({ a:{ b:{ c:666 } } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changePerson(){ Object.assign(person,{name:'李四',age:80}) } function test(){ obj.a.b.c = 888 }
// 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的 watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) watch(obj,(newValue,oldValue)=>{ console.log('Obj变化了',newValue,oldValue) })</script>```
### * 情况四
监视`ref`或`reactive`定义的【对象类型】数据中的**某个属性**,注意点如下:
1. 若该属性值**不是**【对象类型】,需要写成函数形式。2. 若该属性值是**依然**是【对象类型】,可直接编,也可写成函数,建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
```vue<template> <div class="person"> <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> </div></template>
<script lang="ts" setup name="Person"> import {reactive,watch} from 'vue'
// 数据 let person = reactive({ name:'张三', age:18, car:{ c1:'奔驰', c2:'宝马' } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changeC1(){ person.car.c1 = '奥迪' } function changeC2(){ person.car.c2 = '大众' } function changeCar(){ person.car = {c1:'雅迪',c2:'爱玛'} }
// 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式 /* watch(()=> person.name,(newValue,oldValue)=>{ console.log('person.name变化了',newValue,oldValue) }) */
// 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数 watch(()=>person.car,(newValue,oldValue)=>{ console.log('person.car变化了',newValue,oldValue) },{deep:true})</script>```
### * 情况五
监视上述的多个数据
```vue<template> <div class="person"> <h1>情况五:监视上述的多个数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> </div></template>
<script lang="ts" setup name="Person"> import {reactive,watch} from 'vue'
// 数据 let person = reactive({ name:'张三', age:18, car:{ c1:'奔驰', c2:'宝马' } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changeC1(){ person.car.c1 = '奥迪' } function changeC2(){ person.car.c2 = '大众' } function changeCar(){ person.car = {c1:'雅迪',c2:'爱玛'} }
// 监视,情况五:监视上述的多个数据 watch([()=>person.name,person.car],(newValue,oldValue)=>{ console.log('person.car变化了',newValue,oldValue) },{deep:true})
</script>```
|