组合式API
setup
setup选项的写法与执行时机
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
// 返回值会暴露给模板和其他的选项式 API 钩子
return {
count
}
},
mounted() {
console.log(this.count) // 0
}
}
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
在模板中访问从 setup
返回的 ref
时,它会自动浅层解包,因此你无须再在模板中为它写 .value
。当通过 this
访问时也会同样如此解包。
setup语法糖
对于结合单文件组件使用的组合式 API,推荐通过 <script setup> 以获得更加简洁及符合人体工程学的语法。
通过setup语法糖可以简化我们写的代码,对上面的代码我们可以简化为
<script setup>
import { ref } from 'vue'
const count = ref(0);
mounted() {
console.log(this.count) // 0
}
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
总结
- setup选项的执行时机
beforeCreate钩子之前 自动执行
- setup写代码的特点是什么
定义数据+函数 然后以对象方式return
- <script setup>解决了什么问题
经过语法糖的封装更简单的使用组合式API
- setup中的this还指向组件实例吗
setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行
reactive和ref函数
reactive()
作用: 接受对象类型数据的参数传入并返回一个响应式的对象
核心步骤:
<script setup>
// 导入
import {reactive} from 'vue'
// 执行函数 传入参数 变量接收
const state = reactive(对象类型数据)
</script>
- 从vue包中导入reactive函数
- 在<script setup>中执行reactive函数并传入类型为对象的初始值,并使用变量接收返回值
ref()
作用: 接受简单类型或者对象类型的数据传入并返回一个响应式的对象
核心步骤:
<script setup>
// 导入
import {ref} from 'vue'
// 执行函数 传入参数 变量接收
const count = reactive(简单类型或者复杂类型数据)
</script>
ref实现简单计数器示例代码:
<script setup>
// 导入ref函数
import {ref} from 'vue'
// 执行函数 传入参数[简单类型 + 对象类型] 变量接收
const count = ref(0)
const setCount = () =>{
// 脚本区域修改ref产生的响应式对象数据 必须通过.value属性
count.value++
}
</script>
<template>
<div>
<button @click="setCount">{{count}}</button>
</div>
</template>
在模板中访问从 setup 返回的 ref 时,它会自动浅层解包,因此你无须再在模板中为它写 .value。当通过 this 访问时也会同样如此解包。
总结
- reactive和ref函数的共同作用是什么?
用函数调用的方式生成响应式数据
- reactive vs ref ?
[1]reactive不能处理简单类型的数据 [2]ref参数类型支持更好但是必须通过.value访问修改 [3]ref函数的内部实现依赖于reactive函数
- 在实际工作中推荐使用哪个 ?
推荐使用ref函数,更加灵活
computed
computed计算属性是Vue.js中一种特殊的实例属性,可以根据其他实例属性的值的变化而变化。 它可以被用来存储和计算一些复杂的值,而不必每次都去重新计算它们,这样可以提高应用的性能。
核心步骤
- 导入computed函数
- 执行函数在回调参数中return基于响应式数据做计算的值,用变量接收
<script setup>
// 导入
import {computed} from 'vue'
// 执行函数 变量接收 在回调参数中return计算值
const computedState = computed(()=>{
return 基于响应式数据做计算之后的值
})
</script>
过滤数组中小于等于2的元素得到新数组代码示例如下:
<script setup>
// 导入computed
import {ref, computed} from 'vue'
// 原始响应式数组
const list = ref([1, 2, 3, 4, 5, 6, 7, 8])
// 执行函数 return计算之后的值 变量接收
const computedList = computed(() => {
return list.value.filter(item => item>2)
})
</script>
<template>
<div>
原始响应式数组-{{list}}
</div>
<div>
计算属性数组-{{computedList}}
</div>
</template>
总结
- 计算属性不应该有“副作用”
计算属性API的主要作用是为了计算,除了计算外的其他逻辑不应该出现在计算属性函数中,如异步请求/修改dom等。
- 避免直接修改计算属性的值
计算属性应该是只读的
watch
watch函数的作用是侦听一个或者多个数据的变化,数据变化时立即执行回调函数
两个额外参数: 1.immediate(立即执行) 2.deep(深度侦听)
基础使用 - 侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<script setup>
// 1. 导入watch
import {ref, watch} from 'vue'
const count = ref(0)
// 2. 调用watch 侦听变化
watch(count, (newValue, oldValue) => {
console.log('count发生了变化,老值为${oldValue}, 新值为${newValue}')
})
</script>
基础使用 - 侦听多个数据
说明: 同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
<script setup>
// 1. 导入watch
import {ref, watch} from 'vue'
const count = ref(0)
const name = ref('cp')
// 2. 侦听多个数据源
watch(
[count, name],
([newCount, newName], [oldCount, oldName]) =>{
console.log('count或者name变化了',[newCount, newName], [oldCount, oldName])
}
)
</script>
immediate(立即执行)
说明: 在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调
代码示例如下:
<script setup>
// 1. 导入watch
import {ref, watch} from 'vue'
const count = ref(0)
// 2. TODO: watch立即执行
watch(count, (newValue, oldValue) => {
console.log('count发生了变化,老值为${oldValue}, 新值为${newValue}')
},{
immediate: true
})
</script>
deep(深度侦听)
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项
<script setup>
// 1. 导入watch
import {ref, watch} from 'vue'
const state = ref({ count: 0 })
// TODO: watch深度监听
watch(state, () => {
console.log('数据变化了')
}, {
deep: true
})
const changeStateByCount = () => {
// 直接修改属性 -> 不会触发回调
state.value.count++
}
</script>
<template>
<div>
{{ state.count }}
<button @click="changeStateByCount">通过count修改</button>
</div>
</template>
精确侦听对象的某个属性
需求:下列对象中,在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
const info = ref({
name: 'cp',
age: 18
})
精确侦听的代码如下:
<script setup>
// 导入watch
import {ref, watch} from 'vue'
const state = ref({
name: 'cp',
age: 18
})
const chageName = () => {
// 修改name
state.value.name = 'chaichai-teacher'
}
const chageAge = () => {
// 修改age
state.value.age = 20
}
// TODO: 精确侦听某个具体属性
watch(
() => info.value.age,
() => console.log('age发生变化了')
)
// deep性能损耗 尽量不开启deep
</script>
<template>
<div>
<div>当前name -- {{state.name}}</div>
<div>当前age -- {{state.age}}</div>
<div>
<button @click="chageName"> 修改name </button>
<button @click="chageAge"> 修改age </button>
</div>
</div>
</template>
总结
- 作为watch函数的第一个参数,ref对象需要添加.value吗?
不需要,watch会自动读取
- watch只能侦听单个数据吗?
单个或多个
- 不开启deep,直接修改嵌套属性能触发回调吗?
不能,默认是浅层侦听
- 不开启deep,想在某个层次比较深的属性变化时执行回调怎么做?
可以使用精确侦听,把第一个参数写成函数的写法,返回要监听的具体属性
生命周期函数
生命周期API(选项式 VS 组合式)
生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时按照注册的顺序依次执行。
<script setup>
onMounted(() => {
console.log('mount1')
});
onMounted(() => {
console.log('mount2')
});
</script>
父子通信
组合式API下的父传子
基本思想
- 父组件给子组件绑定属性
- 子组件内部通过props选项接收
<script setup>
// 引入子组件
import sonComVue from './son-com.vue'
const count = ref(100)
setTimeout(() =>{
count.value = 200
}, 300)
</script>
<template>
<div class="father">
<h2>父组件</h2>
<!-- 1. 绑定属性 message -->
<sonComVue message="this is app message" :count="count">
</div>
</template>
<script setup>
// 2. 通过 defineProps "编译器宏" 接收子组件传递的数据
const props = defineProps({
message: String,
count:Number
})
</script>
<template>
<div class="son">
<h3>子组件</h3>
<div>
父组件传入的数据 - {{ message }} - {{ count }}
</div>
</div>
</template>
组合式API下的子传父
基本思想
- 父组件中给子组件标签通过@绑定事件
- 子组件内部通过$emit方法触发事件
<script setup>
import SonCom from './son-com.vue'
const getMessage = (msg) =>{
console.log(msg)
}
</script>
<template>
<div class="father">
<h3>父组件App</h3>
<!-- 绑定事件 -->
<SonCom @get-message="getMessage" />
</div>
</template>
<script setup>
// 2.通过defineEmits()-> emit(this.$emit)
const emit defineEmits(['get-message'])
const sendMsg = () => {
// TODO 触发自定义事件 传数据给父组件
emit('get-message', 'this is son message')
}
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<button @click="sendMsg">触发自定义事件</button>
</div>
</template>
模板引用
通过ref表示获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理)
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
<script setup>
import {onMounted, ref } from 'vue'
// 1. 调用ref函数 -> ref对象
const h1Ref = ref(null)
// 组件挂载完毕之后才能获取
onMounted(() => {
console.log(h1Ref.value)
})
</script>
<template>
<div class="son">
<h3>子组件Son</h3>
<button @click="sendMsg">触发自定义事件</button>
</div>
</template>
defineExpose()
默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问。
provide和inject
作用和场景
跨层传递普通数据
- 顶层组件通过provide函数提供数据
- 底层组件通过inject函数获取数据
// 顶层组件
provide('key', 顶层组件中的数据)
// 底层组件
const message = inject('key')
跨层传递响应式数据
在调用provide函数时,第二个参数设置为ref对象
// 顶层组件
provide('app-key', ref对象)
// 底层组件
const message = inject('app-key')
跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
// 顶层组件
const setCount = () => {
count.value++
}
provide('setCount-key',setCount)
// 底层组件
const setCount = inject('setCount-key')
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示