VCA(Vue Composition API)
简介:
在 Vue 3 中,组合式 API(Composition API)是一种用于创建自定义组件的 API,它提供了一种更灵活和可组合的方式来创建组件。
组合式 API 提供了一种将组件的逻辑和样式分离的方法,使您可以更轻松地管理和维护组件的代码。通过使用组合式 API,您可以创建具有更灵活性和可维护性的组件。
优点如下:
- 灵活性:组合式 API 允许您将组件的逻辑和样式分离,使您可以更轻松地管理和维护组件的代码。
- 可维护性:组合式 API 提供了一种更灵活和可组合的方式来创建组件,使您能够创建具有更灵活性和可维护性的组件。
- 可复用性:组合式 API 使您可以创建可复用的组件,从而减少代码冗余并提高代码质量。
- 易于测试:组合式 API 使您可以更容易地测试组件,并确保组件的逻辑正确。
- 更好的可读性:组合式 API 使组件更易于阅读和理解,并提高了代码的可读性。
- 更好的性能:组合式 API 使您能够更好地控制组件的性能,并确保组件在加载速度方面表现良好。
使用
组件中使用VCA
1. 创建响应式变量:reactive,ref,toRef,toRefs
reactive和ref:
两者都可以将一个变量包装成一个响应式对象,区别在于: ref支持对简单数据类型进行包装,并提供value属性用于访问和修改。而reactive是基于proxy对变量进行封装,只支持对对象和数组的封装。
示例代码如下:
setup函数是一个在组件实例创建之前定义组件状态和选项的函数,在此函数中this是无效的
<template>
<div>{{state.name}}--{{state.age}}</div>
<div>{{location}}</div>
<div>{{otherState.name}}-{{otherState.age}}-{{otherState.location}}</div>
<button @click="changeState">click</button>
</template>
<script>
import {reactive,ref} from "vue";
export default {
name: "01-reactive和ref",
setup(){
// reactive
const state = reactive({
name: 'test',
age:18
})
// ref
const location = ref('NKG')
//混写
const otherState = reactive({
name: 'test',
age:18,
location
})
const changeState = () => {
// 操作state
state.name = 'test1'
state.age = 20
//操作ref
location.value = 'XIAN'
}
return {
state,
location,
otherState,
changeState
}
}
}
</script>
<style scoped>
</style>
【注】:ref依然可以实现绑定在dom获取dom节点以及绑定在组件获取组件实例的功能,但是在setup函数中,它是一个包装函数,在使用时,模板中可以直接访问它的值,但在方法中操作时需要用value属性去访问和修改。
toRef,toRefs
toRef用于将对象的某个属性转为响应式,toRefs用于将整个对象的所有属性都转为响应式
用法: 经过toRef和toRefs处理,在模板中使用的感觉更像vue2
<template>
<div>{{name}}--{{age}}</div>
<div>{{myName}}--{{myAge}}</div>
</template>
<script>
import {reactive,toRef,toRefs} from "vue";
export default {
name: "02-toref和torefs",
setup(){
const state = reactive({
name:'test',
age:18
})
const otherState = reactive({
myName:'test1',
myAge: 20
})
return {
name: toRef(state.name),
age: toRef(state.age),
...toRefs(otherState)
}
}
}
</script>
<style scoped>
</style>
2. 计算属性: computed
计算属性(Computed Property)是一种响应式变量,它通过一个表达式来获取它的值,而不是通过数据访问器(Data Access Tester)来获取。并且只有当计算属性依赖更新的时候才会重新计算,在计算属性中不能进行异步操作。
使用示例: 模糊查询功能:computed接受一个回调函数,返回处理后的数据
<template>
<input type="text" v-model="state.inputText"/>
<ul>
<li v-for="item in computedList" :key="item">{{item}}</li>
</ul>
</template>
<script>
import {computed, reactive} from 'vue'
export default {
name: "03-computed",
setup(){
const state = reactive({
inputText:'',
list: Array(10).fill(0).map((item,index) => `${index}${index}`)
})
const computedList = computed(() => state.list.filter(item => item.includes(state.inputText)))
return{
state,
computedList
}
}
}
</script>
<style scoped>
</style>
3.侦听器:watch/watchEffect
watch: 监听某个值变化,如果变化可以执行响应的操作
watch具有一定的惰性,第一次页面展示的时候不会执行,除非加上immediately,当数据变化的时候才会执行
参数可以拿到当前值和原始值
可以侦听多个数据的变化,用一个侦听器承载,
watch的第三个参数是一个对象,{immediate:true,deep:true},immediate表示立即执行,在页面初始化的时候会自动执行,deep表示深度监听一个对象,当对象任意级和任意属性值发生变化时都会执行,因此使用deep:true要特别注意,会十分浪费性能。
watchEffect:
立即执行,没有惰性
自动检查内部代码,代码中有依赖便会执行
不需要传递侦听的内容,会自动感知代码的依赖,不需要传递很多的参数,只需要传递一个回调函数
不能获取之前的值,只能获取当前值
一些异步的操作放在这里会更合适
示例代码:
<template>
<button @click="handleClick">点击--{{count}}--{{watchEffectCnt}}</button>
<ul>
<li v-for="(item,index) in list" :key="index">{{item}}</li>
</ul>
</template>
<script>
import {ref, watch, watchEffect} from "vue";
export default {
name: "04-watch",
setup(){
const count = ref(1)
const list = ref([])
const watchEffectCnt = ref(2)
const handleClick = () => {
count.value ++
}
// 第一种写法——getter函数
watch(() => count.value,(newVal,oldVal) => {
if(newVal){
setTimeout(() => {
list.value = Array(10).fill(newVal)
},2000)
}
})
//第二种写法——响应式变量
watch(count,(newVal,oldVal) => {
if(newVal){
setTimeout(() => {
list.value = Array(10).fill(newVal)
},2000)
}
})
// 监听多个变量
watch([count,watchEffectCnt],(newVal) => {
if(newVal){
setTimeout(() => {
list.value = Array(10).fill(newVal)
},2000)
}
})
watchEffect((newVal) => {
watchEffectCnt.value = count.value * 2
})
return{
count,
handleClick,
list,
watchEffectCnt
}
}
}
</script>
<style scoped>
</style>
4. props&emit
props用于父组件向子组件传值,emit用于子组件像父组件传值,在VCA的写法中,setup函数的第一个参数是props传过来的所有属性,第二个参数config包含slots、emit、attrs等方法,因此父子组件互相通信可以写成如下方式:
props用于父组件向子组件传值,emit用于子组件像父组件传值,在VCA的写法中,setup函数的第一个参数是props传过来的所有属性,第二个参数config包含slots、emit、attrs等方法,因此父子组件互相通信可以写成如下方式:
// 父组件
<template>
<child :title="msg" @test="handleClick"></child>
<button >change--{{msg}}</button>
</template>
<script>
import Child from "./Child.vue";
import {ref} from "vue";
export default {
name: "App",
components: {Child},
setup(){
const msg = ref('父组件传给子组件的值')
const handleClick = (value) => {
debugger
msg.value = value
}
return{
handleClick,
msg
}
}
}
</script>
<style scoped>
</style>
//子组件
<template>
{{myTitle}}
<button @click="handleClick">click--{{myTitle}}</button>
</template>
<script>
import {ref} from "vue";
export default {
name: "Child",
props:{
title:{
type:String,
default:''
}
},
setup(props,{emit}){
const {title} = props
const myTitle = ref(title)
const handleClick = () => {
const childToParent = '子组件给父组件传递的值'
emit('test',childToParent)
}
return{
myTitle,
handleClick
}
}
}
</script>
<style scoped>
</style>
5. provide&inject
provide和inject可以组合实现非父子跨级通信,在vca中的写法如下:inject和provide都是从vue中引入的
// 父组件
<template>
<NavBar v-if="isShow"></NavBar>
<component :is="comName"></component>
</template>
<script>
//getCurrentInstance 获取当前实例
import {getCurrentInstance,ref,provide} from 'vue'
import NavBar from "./components/NavBar.vue";
import List from "./components/List.vue";
import Detail from "./components/Detail.vue";
export default {
name: "App",
components:{
NavBar,List,Detail
},
setup(){
// 获取this
const _this = getCurrentInstance()
console.log(_this)
const comName = ref('List')
const isShow = ref('isShow')
provide('comName',comName)
provide('isShow',isShow)
return {
comName,
isShow
}
}
}
</script>
<style scoped>
</style>
// List
<template>
<div>
<ul>
<li v-for="item in state.vcaList" :key="item" @click="handleClick">{{item}}</li>
</ul>
</div>
</template>
<script>
import {reactive,inject} from "vue";
export default {
name: "List",
setup(){
const state = reactive({
vcaList: []
})
setTimeout(() => {
state.vcaList = Array(100).fill(0).map((item,index) => `${index}${index}`)
})
let name = inject('comName')
const handleClick = () => {
name.value = 'Detail'
}
return{
state,
handleClick
}
}
}
</script>
<style scoped>
</style>
在SPA中使用VCA
在script标签上直接使用setup,
优势: 更少的模板 更简洁的代码
能够使用TS声明props和自定义事件
更好的运行时的性能,其模板会被编译成同一作用域的渲染函数,避免了渲染上下文对象
更好的IDE推理性能,减少了语言服务器从代码中抽取类型的工作
#### 1.单文件组件写法
<template>
{{computedName}}--{{myMsg}}
<button v-test @click="handleClick">change</button>
<Child title="我是父祖件传递的值" @handleClick="handleRight"></Child>
<NavBar ></NavBar>
<div v-cjtest>ddd</div>
<!-- 动态组件:支持组件名直接渲染动态组件-->
<component :is="List"></component>
</template>
<script setup>
import {ref, reactive, toRefs, computed} from "vue";
import List from "./components/List.vue";
import Detail from "./components/Detail.vue";
import NavBar from "./components/NavBar.vue";
import Child from "./Child/child.vue";
let msg = ref('hello')
const state = reactive({
myMsg: 'world'
})
const {myMsg} = {...toRefs(state)}
const handleClick = () => {
msg.value = 'test'
myMsg.value = 'test'
}
const computedName = computed(() => msg.value = msg.value.toUpperCase())
const handleRight = ($event) => {
console.log($event)
}
//局部指令
const vCjtest = (el) =>{
console.log(el)
el.style.background = 'green'
}
</script>
<style scoped>
</style>