Vue3的组合api(composition-API)
vue2中Options API的弊端在Vue2中,我们 编写组件的方式是Options API<script>export default {// 定义数据data() {return {};},// 声明周期钩子created() {},mounted() {},// 定义方法methods: {},// 组件components:{},// 过滤器fil
vue2中Options API的弊端
在Vue2中,我们 编写组件的方式是Options API
<script>
export default {
// 定义数据
data() {
return {};
},
// 声明周期钩子
created() {},
mounted() {},
// 定义方法
methods: {},
// 组件
components:{},
// 过滤器
filter: {},
// 计算属性
computed: {},
// 自定义指令
directives: {}
};
</script>
但是这种代码有一个很大的弊端:组件逻辑分散,不利于阅读和交流
如何解决呢?这也是Composition API
要做的事情
Vue3的Composition API
为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方,在vue组件中,setup函数
就是这个位置。
setup
一个组件选项,在组件被
创建之前
,props 被解析之后执行。它是组合式 API 的入口。
<script>
export default {
setup() {
console.log(this) // undefined
console.log(111);
},
beforeCreate() {
console.log(222);
},
created() {
console.log(333);
},
};
</script>
浏览器输出的顺序:111 --> 222 --> 333
注意:setup
函数它比声明周期钩子beforeCreate()
执行的还要早,所以在里面是获取不到this
setup的参数▲
(很重要)
它一般会有两个参数:props
和 context
,context它里面包含
三个属性
context三个属性:
attrs
:所有的非prop的属性;
slots
:父组件传递过来的插槽(这个在以渲染函数返回时会有作用);
emit
:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
父子组件传值
# 父组件
<template>
<div class=''>
<my msg="'uu盘'" id="123" @getMsg="get"></my>
</div>
</template>
<script>
import my from '@/components/my'
export default {
setup() {
const get = (val)=>{
console.log(val); // '子组件的数据'
}
// 必须要有返回值,才能在template中使用
return {
get
}
},
components:{
my
}
};
</script>
# 子组件
<script>
export default {
props:["msg"],
setup(props,{attrs,slots,emit}) {
console.log(props); // {msg: 'uu盘'}
console.log(attrs); // {id: '123'}
emit('getMsg','子组件的数据')
}
};
</script>
响应式api
举个例子:
<template>
<div class=''>
<div>{{num}}</div>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script>
export default {
setup() {
let num = 0
const add = () =>{
console.log(num);
num++
}
const sub = () =>{
console.log(num);
num--
}
return {
num,
add,
sub
}
}
};
</script>
当我们点击按钮进行加减时,数据已经改变,但是页面视图并没有改变
这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化
,来引起界面的响应式操作,所以我们要把数据变成响应式数据
.
✅reactive
import { reactive } from 'vue'
setup(){
// 本质是转成proxy对象
let state = reactive({
age:1
})
function handler(){
state.age++
}
return {state,handler}
}
// 使用:
{{state.age}}
<button @click="handler">点击修改</button>
如果我们传入一个基本数据类型vue会给我们报警告:所以vue3为我们提供了另外一种api(ref)
setup() {
let num = reactive(0)
}
✅ref
import { ref } from 'vue'
setup(){
const state = ref(1)
function handler(){
state.value = 2
}
return {
state,handler
}
}
// 使用:
在模板中引入ref的值时,vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过state.value的方式来使用
{{state}}
<button @click="handler">点击修改</button>
✅toRefs
toRefs可以解构reactive对象的属性,分解对象的单个属性 并保留属性的响应式。
import { toRefs } from 'vue'
export default {
setup(){
let state = reactive({
name:'uu盘',
data:[1,2,3,4]
})
......
return {
...toRefs(state)
}
}
}
# 使用
<span v-for="(item,i) in data" :key="item"></span>
✅toRef
如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法
import { toRef } from 'vue'
export default {
setup(){
let state = reactive({
name:'uu盘',
data:[1,2,3,4]
})
let data = toRef(state,'data')
// 修改data数据
const handler = ()=>{
data.value[0] = 'uu盘'
}
return {
data,
handler
}
}
}
# 使用
<span v-for="(item,i) in data" :key="item"></span>
<button @click="handler">点击修改</button>
总结:
- 我们一般用
reactive声明对象
,用ref 用来声明基本数据类型和数组
,不能用来声明对象 - 我们通过
toRefs
和toRef
将reactive解构,它会变成ref数据,如果想要修改要加value(name.value = ‘哈哈哈’)
✅readonly
接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。
import { readonly,ref,reactive } from 'vue'
setup() {
// 1. 接受一个普通对象
let obj1 = readonly({name:'uu盘'})
obj1.name = 'hhh' // 不能修改,vue会报警告
// 2. 接受一个reactive对象
let obj1 = reactive({name:'uu盘'})
let obj2 = readonly(obj1)
obj2.name = 'hhh' // 不能修改,vue会报警告
// 3. 接受一个ref对象
let obj1 = ref(0)
let obj2 = readonly(obj1)
obj2.value = 2 // 不能修改,vue会报警告
}
经过readonly返回的对象是不能被修改的,本质上就是readonly
返回的对象的setter方法被劫持了
✅computed
当我们处理一个数据受多个数据影响时,我们就可以使用计算属性
import { computed,ref } from 'vue'
export default {
setup() {
let firstName = ref('诸葛')
let lastName = ref('孔明')
// computed返回一个不可变的响应式 ref 对象。
let fullName = computed(()=>firstName.value+ ' ' + lastName.value)
const change = () =>{
firstName.value = '周'
// fullName.value = 'uu 盘'
}
return {
fullName,
change
}
}
};
</script>
我们是不能修改computed返回的值,vue会给我们报一个警告:(fullName.value = 'uu 盘'
)
如果我们想要修改计算属性,可以用对象的形式进行书写并给这个对象设置setter方法
和getter方法
,返回一个可读可写的ref对象。
import { computed,ref } from 'vue'
export default {
setup() {
let firstName = ref('诸葛')
let lastName = ref('孔明')
// 如果要修改计算属性
let fullName = computed({
get:()=>firstName.value + ' ' + lastName.value,
set(newVal) {
firstName.value = newVal.split(' ')[0]
lastName.value = newVal.split(' ')[1]
}
})
const change = () =>{
// firstName.value = '周'
fullName.value = 'uu 盘'
}
return {
fullName,
change
}
}
};
✅watchEffect
当监听某些响应式数据变化时,我们希望执行某些操作,这个时候我们可以使用watchEffect;
watchEffect
传入的函数会被立即执行一次,并在执行的过程中会收集依赖,并且只有当依赖
发生变化的时候,才会执行。
import { ref,watchEffect } from 'vue'
export default {
setup() {
let age = ref(10)
let str = ref('uu盘')
// 自动收集响应式的依赖,只要变量在函数体里面出现,就会自动收集
watchEffect(()=>{
console.log('age:' + age.value,'str:' + str.value);
})
const change = ()=>{
age.value++
}
return {
change
}
}
};
点击按钮,因为age发生改变,所以watchEffect传入的函数才会执行:
watchEffect的停止监听
如果在某些情况下我们希望停止监听,这时候我们可以使用watchEffect的停止监听
watchEffect返回值是一个函数,执行这个函数就可以停止监听
import { ref,watchEffect } from 'vue'
export default {
setup() {
let age = ref(10)
let str = ref('uu盘')
相当于定时器会返回一个id 用来清除定时器
let stop = watchEffect(()=>{
console.log('age:' + age.value,'str:' + str.value);
})
const change = ()=>{
age.value++
if(age.value > 18) {
stop()
}
}
return {
change
}
}
};
watchEffect清除副作用
如果我们在开发中我们在监听函数里面进行网络请求,就比如:我们监听的值多次发生改变,它都会触发网络请求,这样对我们的服务器压力特别大。所以我们就可以用到watchEffect清除副作用
。
在我们给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate
import { ref,watchEffect } from 'vue'
export default {
setup() {
let age = ref(10)
let name = ref('uu盘')
let str = ref('hahahaha')
let stop = watchEffect((onInvalidate)=>{
const time = setTimeout(() => {
console.log('发送请求...');
}, 2000);
onInvalidate(()=>{
console.log('onInvalidate');
clearTimeout(time)
})
console.log('age:' + age.value,'str:' + str.value);
})
const change = ()=>{
age.value++
if(age.value > 18) {
stop()
}
}
return {
age,
change
}
}
};
对于终止axios请求:request.cancel()
setup中使用ref获取dom节点
在vue2中我们可以通过this.$refs.xxx
获取到我们想要的dom节点,那么在vue3中因为setup里面获取不到this
,如何获取到dom节点?
其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;
import { watchEffect,ref } from 'vue'
export default {
setup() {
const title = ref(null)
watchEffect(()=>{
console.log(title.value);
})
return {
title
}
}
};
我们会发现打印结果打印了两次:
- 这是因为setup函数在执行时就会立即执行传入的函数,这个时候DOM并没有挂载,所以打印为null;
- 而当DOM挂载时,会给title的ref对象赋值新的值,函数会再次执行,打印出来对应的元素;
如果我们希望在第一次的时候就打印出来对应的元素呢?
watchEffect有副作用,DOM挂载或者更新之前就会触发
,我们可以通过flush: post
在dom更新后运行函数,确保模板引用与dom保持同步,并引入正确的元素。
import { watchEffect,ref } from 'vue'
export default {
setup() {
const title = ref(null)
watchEffect(()=>{
console.log(title.value);
},{
flush:"post"
})
return {
title
}
}
};
✅watch
watch允许我们:
- 懒执行副作用(第一次不会直接执行);
- 更具体的说明当哪些状态发生变化时,触发侦听器的执行;
- 访问侦听状态变化前后的值;
watch可以访问新值和旧值,watchEffect不能
监听单个数据源
import { watch,ref,reactive } from 'vue'
export default {
setup() {
const state = reactive({
name:'uu盘',
age:18,
obj:{
sex:'nv'
}
})
const num = ref(0)
watch(()=>state.name,(newV,oldV)=>{
console.log(newV,oldV); // hhh uu盘
})
watch(num,(newV,oldV)=>{
console.log(newV,oldV); // 10 0
})
const handler = () =>{
state.name = 'hhh'
num.value = 10
}
return {
handler
}
}
};
监听多个数据源
import { watch,ref,reactive } from 'vue'
export default {
setup() {
const state = reactive({
name:'uu盘',
age:18,
obj:{
sex:'nv'
}
})
const num = ref(0)
watch([num,()=>state.name],([newV1,newV2],[oldV1,oldV2])=>{
console.log(newV1,oldV1);
console.log(newV2,oldV2);
})
const handler = () =>{
state.name = 'hhh'
num.value = 10
}
return {
handler
}
}
};
watch的选项
如果我们希望监听一个深层的监听,那么依然需要设置deep:true
,如果想要立即执行可以添加immediate:true
import { watch,ref,reactive } from 'vue'
export default {
setup() {
const state = reactive({
name:'uu盘',
age:18,
obj:{
sex:'nv'
}
})
const num = ref(0)
watch(()=>state,(value)=>{
console.log(value);
},{
deep:true,
immediate:true
})
const handler = () =>{
state.obj.sex = '88'
}
return {
handler
}
}
};
更多推荐
所有评论(0)