一、setup
setup
是组合Composition API
中的入口函数,也是第一个要使用的函数。
1、setup
只在初始化时执行一次,所有的Composition API
函数都在此使用。
2、setup
是在beforeCreate
生命周期之前执行的(只执行一次)
beforeCreate() {
console.log('beforeCreate执行了');
},
setup() {
console.log('setup执行了');
return {};
},
//setup执行了
//beforeCreate执行了
由此可以推断出setup
执行的时候,组件对象还没有创建,组件实例对象this
还不可用,此时this
是undefined
, 不能通过this
来访问data/computed/methods/props
。
3、返回对象中的属性会与data
函数返回对象的属性合并成为组件对象的属性,返回对象中的方法会与methods
中的方法合并成功组件对象的方法,如果有重名,setup
优先。
因为在setup
中this
不可用,methods
中可以访问setup
提供的属性和方法, 但在setup
方法中不能访问data
和methods
里的内容,所以还是不建议混合使用。
4、setup
函数如果返回对象, 对象中的 属性 或 方法 , 模板 中可以直接使用
//templete
<div>{{number}}</div>
//JS
setup() {
const number = 18;
return {
number
};
},
5、注意:setup
不能是一个async
函数: 因为返回值不再是return
的对象,而是promise,
模板中就不可以使用return
中返回对象的数据了。
6、setup的参数(props,context)
(1)props: 是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用 props
接收到的所有的属性
(2)context:上下文对象,可以通过es6
语法解构 setup(props, {attrs, slots, emit})
- attrs:获取当前组件标签上所有没有通过
props
接收的属性的对象, 相当于this.$attrs
- slots:包含所有传入的插槽内容的对象,相当于
this.$slots
- emit:用来分发自定义事件的函数,相当于
this.$emit
二、ref
1、作用:定义一个响应式的数据(一般用来定义一个基本类型的响应式数据Undefined
、Null
、Boolean
、Number
和String
)
2、语法:const xxx = ref(initValue);
注意:script
中操作数据需要使用xxx.value
的形式,而模板中不需要添加.value
3、ref 用于定义响应式数据
// 用一个例子来演示:实现一个按钮,点击可以增加数字
<template>
<div>{{count}}</div>
<button @click='updateCount'>增加</button>
</template>
// 在Vue3中
setup() {
// ref用于定义一个响应式的数据,返回的是一个Ref对象,对象中有一个value属性
//如果需要对数据进行操作,需要使用该Ref对象的value属性
const count = ref(0);
function updateCount() {
count.value++;
}
return {
count,
updateCount,
};
},
4、ref 用于获取 dom 节点:在Vue2
中我们通过this.$refs
来获取dom
节点,Vue3
中我们通过ref
来获取节点
首先需要在标签上添加 ref='xxx'
,然后再setup
中定义一个初始值为null
的ref
类型,名字要和标签的ref
属性一致
const xxx = ref(null)
注意:一定要在setup
的return
中返回,不然会报错。
// 还是用一个例子来演示:让输入框自动获取焦点
<template>
<h2>App</h2>
<input type="text" ref="inputRef">
</template>
<script lang="ts">
import { onMounted, ref } from 'vue'
/*
ref获取元素: 利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
*/
export default {
setup() {
const inputRef = ref<HTMLElement|null>(null)
onMounted(() => {
inputRef.value && inputRef.value.focus()
})
return {
inputRef
}
},
}
</script>
三、reactive
1、语法:const proxy = reactive(obj)
2、作用:定义多个数据的响应式,接收一个普通对象然后返回该普通对象的响应式代理器对象(Proxy)
,响应式转换是“深层的”:会影响对象内部所有嵌套的属性,所有的数据都是响应式的。
<template>
<h3>姓名:{{user.name}}</h3>
<h3>wife:{{user.wife}}</h3>
<button @click="updateUser">更新</button>
</template>
setup() {
const user = reactive({
name: '**',
wife: {
name: 'xioaohong',
age: 18,
books: [],
},
});
const updateUser = () => {
user.name = '小红';
user.age += 2;
user.wife.books[0] = '**';
};
return {
user,
updateUser,
};
},
3、重点来了:ref 他强调的是一个数据的value的更改,reactive 强调的是定义的对象的某一个属性的更改。
四、toRefs
1、作用:把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个 ref
2、应用:我们使用 reactive
创建的对象,如果想在模板中使用,就必须得使用 xxx.xxx
的形式;如果大量用到的话还是很麻烦的,但是使用 es6
解构以后,会失去响应式。
那么toRefs
的作用就体现在这,利用toRefs
可以将一个响应式 reactive
对象的所有原始属性转换为响应式的ref
属性。
<template>
<div>
name:{{name}}
</div>
</template>
<script lang='ts'>
import { defineComponent, reactive, toRefs } from 'vue';
export default defineComponent({
name: '',
setup() {
const state = reactive({
name: 'hzw',
});
const state2 = toRefs(state);
setInterval(() => {
state.name += '===';
}, 1000);
return {
//通过toRefs返回的对象,解构出来的属性也是响应式的
...state2,
};
},
});
</script>
五、computed函数
1、与Vue2
中的computed
配置功能一致,返回的是一个ref
类型的对象
2、计算属性的函数中如果只传入一个回调函数 表示的是get
操作
3、计算属性的函数中可以传入一个对象,可以包含set
和get
函数,进行读取和修改的操作
const fullName2 = computed({
get() {
return user.firstName + '_' + user.lastName;
},
set(val: string) {
const names = val.split('_');
user.firstName = names[0];
user.lastName = names[1];
},
});
return {
user,
fullName2,
};
六、watch 函数
1、与Vue2
中的watch
配置功能一致:(1)参数1 - 要监听的数据;(2)参数2 - 回调函数;(3)参数3 - 配置。
2、作用:监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调。
(1)默认初始时不执行回调,但可以通过配置 immediate
为true
来指定初始时立即执行第一次
(2)通过配置 deep
为true
来指定深度监视
3、监听单一数据
import { ref, reactive, computed, watch } from 'vue';
setup(props) {
// ref
const age = ref(20);
watch(() => age.value, (nv, ov) => { ... });
// reactive
const product = reactive({ name: '饮料', count: 1 });
watch(() => product.count, (nv, ov) => { ... });
// props
watch(() => props.msg, (nv, ov) => { ... });
// computed
const userAge = computed(() => `今年${age.value}岁了!`);
watch(() => userAge.value, (nv, ov) => { ... });
}
4、监听对象
import { ref, reactive, watch } from 'vue';
setup(props) {
// ref
const user = ref({ name: 'zhang_san', age: 20 });
// 字面量引发的监听触发: user.value = { ... };
watch(() => user.value, (nv, ov) => { ... });
// 如果使用 user.value.age = 30这种方式去修改user的age值; 将不会触发上面的监听,需要使用watch的第三个参数(深度监听),且触发监听后的nv===ov true
watch(() => user.value, (nv, ov) => { ... }, { deep: true });
// 如果我们只需要监听name的值,那么
watch(() => user.value.name, (nv, ov) => { ... });
// reactive
const reactiveData = reactive({ user: { name: 'zhang_san', age: 20 } });
// 字面量引发的监听触发: reactiveData.user = { ... };
watch(() => reactiveData.user, (nv, ov) => { ... });
// 如果使用 user.user.age = 30这种方式去修改user的age值,将不会触发监听,需要使用watch的第三个参数(深度监听),且触发监听后的nv===ov true
watch(() => reactiveData.user, (nv, ov) => { ... }, { deep: true });
// 如果我们只需要监听name的值,那么
watch(() => reactiveData.user.name, (nv, ov) => { ... });
}
5、监听数组
import { ref, reactive, watch } from 'vue';
setup(props) {
// ref
const user = ref([
{ name: 'zhang_san', age: 10 },
{ name: 'li_si', age: 10 }
]);
// 字面量引发的监听触发: user.value = [ ... ];
watch(() => user.value, (nv, ov) => { ... });
// 如果使用数组的操作方法(如:push())或者user.value[0].age = 20这类操作去修改数组某项的属性值,将不会触发监听,也需要使用深度监听模式,且触发监听后的nv===ov true
watch(() => user.value, (nv, ov) => { ... }, { deep: true });
// reactive
const reactiveData = reactive({
user: [
{ name: 'zhang_san', age: 10 },
{ name: 'li_si', age: 10 }
]
});
// 字面量引发的监听触发: user.user = [ ... ];
watch(() => reactiveData.user, (nv, ov) => { ... });
// 如果使用数组的操作方法(如:push())或者user.value[0].age = 20这类操作去修改数组某项的属性值,将不会触发监听,也需要使用深度监听模式,且触发监听后的nv===ov true
watch(() => reactiveData.user, (nv, ov) => { ... }, { deep: true });
}
6、监听多个数据
import { ref, reactive, computed, watch } from 'vue';
setup(props) {
const age = ref(20);
const user = ref({ name: 'zhang_san', age: 20 });
watch([() => age.value, () => user.name], ([newAge, newName], [oldAge, oldName]) => { ... });
}
7、终止监听
import { ref, watch } from 'vue';
const age = ref(20);
// watch监听会返回一个方法
const stop = watch(age, (nv, ov) => { ... });
// 当调用此方法后,该监听就会被移除
stop();
七、watchEffect函数
1、作用:监视数据发生变化时执行回调,不用直接指定要监视的数据,回调函数中使用的哪些响应式数据就监视哪些响应式数据,
2、默认初始时就会执行第一次,从而可以收集需要监视的数据。
import { watchEffect, ref } from 'vue';
const user = reactive({
firstName: '**',
lastName: '**',
});
const fullName4 = ref('');
watchEffect(() => {
fullName4.value = user.firstName + '_' + user.lastName;
});
return {
user,
fullName4,
};
八、生命周期对比
注意:3.0
中的生命周期钩子要比2.X
中相同生命周期的钩子要快
setup() {
onBeforeMount(() => {
console.log('--onBeforeMount')
})
onMounted(() => {
console.log('--onMounted')
})
......
onBeforeUnmount(() => {
console.log('--onBeforeUnmount')
})
onUnmounted(() => {
console.log('--onUnmounted')
})
}
九、provide 与 inject
作用:实现跨层级组件(祖孙)间通信
// 父组件
setup() {
const color = ref('red')
provide('color', color)
return {
color
}
// 孙子组件
setup() {
const color = inject('color')
return {
color
}
这个 vue2 里也有,差不多一样的用法。