这一节了解一下Vue3中的响应式,响应式是Vue框架重要的特点,常见的是通过数据绑定的方式将变量的值渲染到页面中,当变量发生变化时,页面对应的元素也会更新,简单总结:
API
- setup 入口函数
含义:组合式 API 统一入口,组件执行最先触发 作用:声明数据、方法、生命周期、引入 API
javascript
<template>
<view>{{ msg }}</view>
</template>
<script setup>
const msg = 'setup组合式入口'
</script>
- ref 基础响应式
含义:创建基本类型响应式数据 作用:处理字符串、数字、布尔值
javascript
<template>
<view>{{ count }}</view>
<button @click="count++">加一</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
- reactive 对象响应式
含义:创建对象 / 数组类型响应式数据 作用:批量管理表单、复杂对象数据
javascript
<template>
<view>{{ user.name }} {{ user.age }}</view>
</template>
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: '小明', age: 18 })
</script>
- toRefs 解构保留响应
含义:把 reactive 对象转为 ref 属性集合 作用:解构取值不丢失响应性
javascript
<script setup>
import { reactive, toRefs } from 'vue'
const info = reactive({a:1,b:2})
const {a,b} = toRefs(info)
</script>
- computed 计算属性
含义:依赖数据自动计算,自带缓存 作用:数据格式化、派生新数据
javascript
<template>
<view>总和:{{ sum }}</view>
</template>
<script setup>
import { ref, computed } from 'vue'
const n1 = ref(10), n2 = ref(20)
const sum = computed(() => n1.value + n2.value)
</script>
- computed 可读写模式
含义:同时配置读取与赋值逻辑 作用:双向联动修改源数据
javascript
<script setup>
import { ref, computed } from 'vue'
const num = ref(10)
const double = computed({
get: () => num.value *2,
set: val => num.value = val/2
})
</script>
- watch 精准监听
含义:指定监听单个 / 多个数据变化 作用:数据变动执行异步、复杂逻辑
javascript
<template>
<input v-model="text"/>
</template>
<script setup>
import { ref, watch } from 'vue'
const text = ref('')
watch(text,(newVal)=>console.log('新值',newVal))
</script>
- watch 深度监听 deep
含义:监听对象内部属性变更 作用:监控嵌套对象、数组修改
javascript
<script setup>
import { reactive, watch } from 'vue'
const obj = reactive({name:'小红'})
watch(()=>obj,()=>console.log('对象变更'),{deep:true})
</script>
- watchEffect 自动依赖监听
含义:自动收集内部依赖,无需指定监听源 作用:简化监听代码,依赖变化自动执行
javascript
<script setup>
import { ref, watchEffect } from 'vue'
const num = ref(0)
watchEffect(()=>console.log('数值:',num.value))
</script>
- onMounted 挂载生命周期
含义:DOM 渲染完成后执行 作用:操作 DOM、请求接口、初始化业务
javascript
<script setup>
import { onMounted } from 'vue'
onMounted(()=>console.log('页面DOM加载完成'))
</script>
- onUpdated 视图更新钩子
含义:数据变更页面重新渲染后触发 作用:更新后 DOM 操作、状态同步
javascript
<script setup>
import { onUpdated } from 'vue'
onUpdated(()=>console.log('页面视图已更新'))
</script>
- onBeforeUnmount 销毁前钩子
含义:组件销毁前瞬间执行 作用:清除定时器、解绑事件
javascript
<script setup>
import { onBeforeUnmount } from 'vue'
let timer = setInterval(()=>{},1000)
onBeforeUnmount(()=>clearInterval(timer))
</script>
- defineProps 接收父组件参数
含义:组合式专用 props 接收 API 作用:父子组件传参、参数校验
javascript
<template>
<view>{{ title }}</view>
</template>
<script setup>
const props = defineProps({
title: String
})
</script>
- defineEmits 触发子父事件
含义:组合式派发自定义事件 作用:子组件向父组件传递消息数据
javascript
<template>
<button @click="send">提交</button>
</template>
<script setup>
const emit = defineEmits(['submit'])
const send = () => emit('submit','提交数据')
</script>
- defineExpose 向外暴露属性
含义:组件主动对外抛出数据方法 作用:父组件直接获取子组件内部值
javascript
<script setup>
const msg = '子组件数据'
defineExpose({msg})
</script>
- ref 获取 DOM 元素
含义:绑定标签获取真实 DOM 节点 作用:元素聚焦、样式操作
javascript
<template>
<input ref="ipt"/>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const ipt = ref(null)
onMounted(()=>ipt.value.focus())
</script>
- provide /inject 跨层传值
含义:祖先提供数据,后代任意层级接收 作用:多层组件便捷传参,避开逐层 props
javascript
<!-- 祖先组件 -->
<script setup>
import { provide } from 'vue'
provide('key','全局共享数据')
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const data = inject('key')
</script>
栗子:
javascript
<template>
<view class="wrap">
<view>计数:{{ count }}</view>
<button @click="count++">点击累加</button>
<view>双倍值:{{ doubleCount }}</view>
<input v-model="user.name" placeholder="输入姓名"/>
<view>监听姓名:{{ user.name }}</view>
<Child :info="user" @child-send="recvMsg"/>
</view>
</template>
<script setup>
import {ref,reactive,computed,watch,onMounted} from 'vue'
const count = ref(0)
const doubleCount = computed(()=>count.value*2)
const user = reactive({name:''})
// 监听数据
watch(()=>user.name,(val)=>{
console.log('姓名变化:',val)
},{immediate:true})
onMounted(()=>{
uni.showToast({title:'页面加载完毕',icon:'none'})
})
// 子组件
const Child = {
props:['info'],
emits:['child-send'],
setup(props,emit){
const send = ()=>emit('child-send',props.info.name)
return {send}
},
template:`<button @click="send">向父组件传值</button>`
}
// 接收子组件数据
const recvMsg = (val)=>{
uni.showToast({title:'收到:'+val})
}
</script>
<style scoped>
.wrap{padding:30rpx;}
input{border:1rpx solid #eee;padding:20rpx;border-radius:10rpx;margin:20rpx 0;}
button{margin:10rpx 0;}
</style>
javascript
<template>
<view class="container">
<view class="title">Demo</view>
<view class="section">
<text class="label">1. ref 响应式计数</text>
<button type="primary" @click="add">点击 +1</button>
<view>当前数值:{{ count }}</view>
</view>
<view class="section">
<text class="label">2. reactive 响应式对象</text>
<input v-model="task.title" class="input" placeholder="输入任务标题" />
<input v-model="task.desc" class="input" placeholder="输入任务描述" />
<view>标题:{{ task.title }}</view>
<view>描述:{{ task.desc }}</view>
</view>
<view class="section">
<text class="label">3. computed 计算属性(字数统计)</text>
<view>标题字数:{{ titleLength }}</view>
</view>
<view class="section">
<text class="label">4. watch 监听任务标题</text>
</view>
<view class="section">
<text class="label">5. watchEffect 自动监听</text>
</view>
<view class="section">
<text class="label">6. ref 获取DOM(自动聚焦)</text>
<input ref="inputRef" class="input" placeholder="我会自动聚焦" />
</view>
<view class="section">
<text class="label">7. 子组件通信</text>
<TaskItem
:item="task"
@delete="handleDelete"
/>
</view>
</view>
</template>
<script setup>
import {
ref,
reactive,
computed,
watch,
watchEffect,
onMounted
} from 'vue'
const count = ref(0)
const add = () => count.value++
const task = reactive({
title: '',
desc: ''
})
const titleLength = computed(() => {
return task.title.length
})
watch(
() => task.title,
(newVal) => {
console.log('watch 监听标题变化:', newVal)
}
)
watchEffect(() => {
console.log('watchEffect 自动监听:', task.desc)
})
const inputRef = ref(null)
onMounted(() => {
console.log('页面挂载完成:onMounted')
// 自动聚焦
if (inputRef.value) {
inputRef.value.focus()
}
})
const TaskItem = {
// 接收父组件参数
props: {
item: {
type: Object,
required: true
}
},
emits: ['delete'],
setup(props, { emit }) {
const deleteTask = () => {
emit('delete', '删除成功')
}
return { deleteTask }
},
template: `
<view class="task-box">
<view>子组件接收:{{ item.title }}</view>
<button @click="deleteTask" size="mini">通知父组件删除</button>
</view>
`
}
const handleDelete = (msg) => {
uni.showToast({ title: msg })
}
</script>
<style scoped>
.container {
padding: 30rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
margin-bottom: 40rpx;
}
.section {
margin-bottom: 40rpx;
}
.label {
font-weight: bold;
font-size: 28rpx;
margin-bottom: 15rpx;
display: block;
}
.input {
border: 1rpx solid #eee;
padding: 20rpx;
border-radius: 10rpx;
margin-bottom: 10rpx;
}
.task-box {
padding: 20rpx;
background: #f9f9f9;
border-radius: 10rpx;
}
</style>