setup概述
setup() 接受 props 和 context 作为参数,主要用于设置组件的响应式数据、方法和计算属性等,它返回一个对象,这个对象包含了需要暴露给模板的属性。
- props,它是响应式的,当传入新的 prop 时,它将被更新。
- context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。
- setup() 函数在组件创建 created() 之前执行
<script setup>
import { ref } from 'vue'
const sum = ref(1)
</script>
<template>
<div>
<h1>v3</h1>
<h3>{{ sum }}</h3>
<button @click="sum++">+1</button>
</div>
</template>
拓展:安装setup语法简写插件 语法糖
npm i vite-plugin-vue-setup-extend -D
然后在vite.config.ts中引入它
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
最后添加一句调用,写完dev重启即可使用
ref () :声明响应式状态
作用:定义响应式变量
语法:let xxx=ref(初始值)
ref 接受一个值,返回一个响应式对象,一般用来处理简单数据类型的响应式,但如果传入的值是对象 ref 会求助 reactive,返回RefImpl的实例简称ref对象。
但若要在方法中修改ref创建的响应式数据,的写法是: state.value = xxx
- JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可
- 对于let name=ref('张三') 来说,name不是响应式的,name.value是响应式的
ref 创建响应式对象
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
{{ count }}
<button @click="count++">Count</button>
</template>
应用案例:
<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'
// 创建响应式数据
let name = ref('张三')
let age = ref(18)
// 非响应式 静态
let tel = '13888888888'
let address = '北京昌平区宏福苑·宏福科技园'
// 修改名字
function changeName() {
name.value = 'zhang-san'
console.log(name.value)
}
// 修改年龄
function changeAge() {
age.value += 1
console.log(age.value)
}
// 查看电话号码
function showTel() {
console.log(tel)
}
</script>
reactive 创建基本类型的响应式函数
<script lang="ts" setup name="Person">
import {reactive} from 'vue'
//数据
let car = reactive({brand:'汽车',price:100})
let games=[
{id:'a1',name:'王者'},
{id:'a1',name:'原神'},
{id:'a1',name:'三国'}
]
ref与reactive区别
ref创建的变量必须使用 .value(可使用volar插件自动添加.value)
car.value={brand:'宝马',price:1}
reactive有局限性,重新分配一个新对象时会失去响应式变成普通对象
function changeCar(){
//下面这个写法页面可以更新
Object.asssign(car,{brand:'宝马',price:1})
}
使用原则:
1.若需要一个基本类型的响应式数据,必须使用 ref
2.若需要一个响应式对象,层级不深,ref、 reactive 都可以
3.若需要一个响应式对象,且层级较深,推荐使用 reactive
toRefs与toRef
作用:将一个响应式对象中的每一个属性,转换为ref(响应式)对象。
toRefs与toRef功能一致,但toRefs可以批量转换。
Computed计算属性
-
只读的写法 :computed(() => xxxxxx)
-
可读可写的写法: computed({ get: () => xxxx, set: (val) => { xxxx } })
计算属性只要数据发生变化就会重新计算
<template>
<div class="person">
//双向绑定v-model,单向绑定v-bind:value或:value
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{{fullName}}</span> <br>
</div>
</template>
<script lang="ts" setup name="Person">
import {ref,computed} from 'vue'
let firstName = ref( 'zhang')
let lastName = ref( 'san ')
// 这么定义的fullName是一个计算属性,可读可写
let fullName = computed({
get(){
return firstName.value.slice(0,1).toUpperCase()+firstName.value.slice(1) + '-' + lastName.value
},
set(val){
const [str1,str2] = val.split('_')
firstName.value = str1
lastName.value = str2
}
})
</script>
import {computed} from 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
Vue2和3计算属性写法的区别:
不过上面3计算写法是只读的,不能修改,若修改可通过 getter 和 setter 来创建:
watch监听
- ref 定义的数据
- reactive 定义的数据
- 函数返回一个值(getter函数)
- 一个包含上述内容的数组
情况1:监视ref 定义的基本类型数据
(注:在监视ref数据的时候不写value,如上图sum(回调函数)后面不加value,建议安装volar插件自动添加。后面的"oldvalue"可省略,括号里可只写一个value)
如何解除监听(比如加个条件sum>10时解除监视):
watch调用是有一个返回值的,我们可以在watch()前加变量接收它,然后添加方法来停止监视
情况2:监视ref 定义的对象类型数据
- 直接写数据名,监视的是对象的地址值
- 监视对象内部数据要手动开启深度监视
- 若修改的是ref 定义的对象中的属性,newValue oldValue是同一个对象,都是新值
- 若修改的是ref 定义的对象,newValue是新值oldValu旧值,不是同一个对象了
<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 }
}
//深度监视
watch(person, (newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
}, { deep: true })
</script>
watch(参数一:被监视的数据,......{
...... 参数二:监视的回调......
},{ 参数三:配置对象 deep监视深度为真/假,immediate是否立即监听 等... })
immediate是否开启立即监听,deep是否开启深度监听
flush回调的触发时机,onTrack / onTrigger用于调试的两个函数
情况3:监视reactive定义的对象类型数据 (且默认开启深度监视)
深度监听只能监听对象且无法关闭
ref和reactive区别
情况四:监视ref或reactive定义的对象类型数据中的某个属性
监视对象中的某个属性用函数式,监视对象是地址值 需手动开启深度监视
1.若该属性值不是【对象类型】,需要写成函数形式
2.若该属性值是依然是【对象类型】,可直接编,也可写成函数,不过建议写成函数
deep监视的是地址值,对象类型默认是地址值;
若要在地址值的基础上像关注细枝末节开启deep深度即可
情况五:监视上述的多个数据
watch监视的数据像数组一样 ‘数组’中写的类型分需求情况,newValue是这个数组的整体
watchEffect
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
- 不需要手动传入依赖(不用指定监听对象)
- 无法获取原始值,只能获取更新后的值
- 立即执行(在
onMounted
前调用) - 一些异步操作放里面更加的合适
watch与watchEffect主要区别是追踪响应式依赖的方式:
- watch 需要明确指定要监视的数据源,而 watchEffect 则自动收集其使用的响应式数据,代码更简洁。
- watch 的回调函数可以获得旧值和新值的参数,而 watchEffect 的回调函数无法直接获取旧值和新值的参数。
- 当监听对象或数组时,watch 需要开启 deep 选项才能深度监听属性或元素的变化,而 watchEffect 会自动追踪响应式数据的依赖,并在赋值、方法调用等操作后重新执行回调函数。
import { watch, reactive } from 'vue';
const person = reactive({
name: '张三',
age: 18,
});
//使用watch实现
watch(
() => person.age, // 监听 age 属性
(newValue, oldValue) => {
console.log(`age 从 ${oldValue} 变成了 ${newValue}`);
}
);
//使用watchEffect实现
watchEffect(() => {
console.log(`age 变成了 ${person.age}`);
});
可以看到,watch 需要明确指定要监听的数据源,而 watchEffect 则可以自动追踪响应式数据。并且,watch 的回调函数可以获得旧值和新值的参数,而 watchEffect 的回调函数则需要手动获取旧值和新值。
标签的ref属性
Vue3中ref
函数来创建一个响应式的引用,并将其分配给模板中的标签的 ref
属性。这样就可以通过引用来访问和操作对应的组件或元素。它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。
注:使用 ref
创建的引用在访问其值时需要使用 .value
访问模板引用
为了通过组合式 API 获得该模板引用,我们需要声明一个同名的 ref:
<script setup>
import { ref, onMounted } from 'vue'
// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
如果不使用 <script setup>
,需确保从 setup()
返回 ref:
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}