内容
对 vue 3.x 中的一些常用特性做下记录
比对 vue 2.x 做分析
写在前面
Vue 的强大可谓不言而喻,可它仍在进行着不断的完善,作为追求者的我们,当然要紧跟其步伐,同其一起变得强大。本文就谈一谈,关于 vue 3.x 中一些实用的新特性,赶紧偷学起来(哈哈哈)。源文档地址:composition-api
emmmm 借大佬撑下牌面。。。
好了,开始我的表演
起步
对于新特性的使用,需要依赖官方 API ,所以此处就需要创建 vue 项目,然后安装 @vue/composition-api
,安装后将其应用于 vue 项目(类似于 vue-router 的使用)即可。
1. setup
该setup功能是新的组件选项。它充当在组件内部使用Composition API的入口点。
1.1 调用时间
setup创建组件实例时,在初始组件后立即调用。在生命周期方面,它在 beforeCreate
之后 created
之前调用
1.2 基本使用
<template>
<div>
我是 setup 函数
<div>{
{
name}}</div>
</div>
</template>
<script>
import {
reactive} from '@vue/composition-api'
export default {
props:{
name:String //用于验证props 类型
},
// setup函数传入两个参数
setup(props,context){
//setup() 函数调用的时机为 beforeCreate() 之前,created() 之后
console.log('setup');
console.log(props); //打印 props 的值
console.log(context); // 打印 context(object)
console.log(context.root) //setup 使用this 的方式,必须在参数中传入context 来打点调用
},
}
</script>
注意:此处是将组件作为 App.vue
的子组件进行渲染,props 来自于 App.vue (下同),渲染结果略
2. reactive,ref
vue 2.x 组件的数据都有data() 函数包裹; 此处两个 api 都用于动态创建响应式数据。reactive 一次可创建 n 多个数据({name:‘’,age:’’…});ref 一次只能创建一个数据对象。实操时经常会 reactive 和 ref 配合使用
2.1 reactive
<template>
<div>
<div>{
{
msg}}</div>
<div>couut值为{
{
count}}</div>
<button @click="count++">+1</button>
</div>
</template>
<script>
import {
reactive} from '@vue/composition-api'
export default {
setup(){
//setup() 函数调用的时机为 beforeCreate() 之前,created() 之后
const myData = reactive({
msg:'奥利给',count:1}) //rective() 必须写在 setup() 中,创建私有数据(响应式)
console.log(myData.msg) //内部调用
return myData
},
}
2.2 ref
<template>
<div>
我是ref函数
<p>{
{
refCount}}</p>
</div>
</template>
<script>
import {
ref} from '@vue/composition-api'
export default {
setup(){
const refCount = ref(1); // ref() 返回值为一个对象,且只包含 value 一个属性
console.log(refCount.value); // 在 setup 中用 .value 调用,在模板中不写 .value
return {
//此处return 后加{} 返回对象
refCount,
}
}
}
</script>
2.3 reactive 返回 ref 对象
setup(){
const count = ref(1);
const state = reactive({
count //此数会自动把响应式数据展开为原始的值,不需要打掉调用
})
return state
}
2.4 新的 ref 会覆盖 旧的 ref
3. isRef,toRefs
3.1 isRef 用于判断对象是否是由 ref方法创建出来的对象。应用场景:当需要展开某个可能是 ref() 创建的对象的时候
const data = isRef(foo) ? foo.value : 'foo'
3.2 toRefs 可以将 reactive 创建的响应式对象转换成普通对象,此时这个对象上的每个属性都是 ref() 类型的响应式数据
<template>
<div>
我是toRefs函数
<p>{
{age}}</p>
<button @click="add">+1</button>
</div>
</template>
<script>
import {
reactive,toRefs} from '@vue/composition-api'
export default {
setup(){
const data = reactive({
name:'zs',
age:18
})
// 定义逻辑的函数(vue 2 写在 methods 中,vue 3 全部写在 setup() 中)
const add = ()=>{
data.age++
}
return{
// ...data, 直接把 reactive 创建的响应式数据展开后数据就不具备响应式;
...toRefs(data), //需要用toRefs 进行包裹,让其每个节点都成为ref 创建的响应式节点
add // 把方法返回出去(逻辑较多时,把函数写在 setup 中)
}
}
}
</script>
4. computed
4.1 创建只读的计算属性
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error (只读无法进行更改)
4.2 创建可读写的计算属性
const one = ref(1);
const two = computed({
get:()=> one.value + 1,
set:val=> one.value = val
})
console.log(one.value) //1
console.log(two.value) //2
two.value = 8
console.log(two.value) //9
5. watch
5.1 监视单个数据源
setup() {
// 监听ref 创建的数据
const refCount = ref(1);
// 监听 state 数据变化,数据改变执行函数
watch(refCount, (newRef, oldRef) =>
console.log(newRef, oldRef) //切记此处无需 .value
);
// 定义定时器来改变数据
setTimeout(() => {
refCount.value++;
}, 1000);
// 监听 reactive 创建的数据
const retcount = reactive({
count: 0,
});
watch(
() => retcount.count,
(newCount, oldCount) => console.log(newCount, oldCount)
);
setTimeout(() => {
retcount.count++;
}, 1000);
},
注意:监视单个 reactive
数据源时 watch 的第一个参数为函数,第二个参数也为函数;监视单个 ref
数据源时 watch 的第一个参数为 ref 数据,第二个参数为函数
5.2 监视多个数据源(注释含解释)
setup() {
const retCount = reactive({
age:18,
length:20
})
// 监听多个 reactive 数据时,第一个参数为数组(中包含函数),第二个参数为函数(函数参数为数组)
watch([()=>retCount.age,()=>retCount.length],([newAge,newLenght],[oldAge,oldLength])=>{
console.log(newAge,oldAge)
console.log(newLenght,oldLength)
})
setTimeout(()=>{
retCount.age++,
retCount.length++
},1000)
const refAge = ref(18);
const refLength = ref(21);
// 监听多个 ref 数据时,第一个参数为数组(中包含 ref 数据),第二个参数为函数(函数参数为数组)
watch([refAge,refLength],([newAge,newLenght],[oldAge,oldLength])=>{
console.log(newAge,oldAge)
console.log(newLenght,oldLength)
})
setTimeout(()=>{
refAge.value++,
refLength.value++
},1000)
},
注意:watch 也可有第三个参数,参数的类型为 object
,写一些相关配置
5.3 清除监视
在setup()
函数内创建的 watch
监视,会在当前组件被销毁时自动停止,如果想要明确地停止某个监视,可调用 watch
函数的返回值即可。
const stop = watch(refCount,(oldCount,newCound)=>{
console.log(oldCount,newCound)
});
const stopWatch = ()=>{
//调用watch 返回值即可清除侦听(把方法返回供使用)
stop()
}
5.4 在 watch 中清除无效的异步任务
有时,当被 watch
监视的数据发生变化时,或 watch
本身被 stop
后,我们期望能够清除无效的异步任务,此时,watch
回调函数中提供了一个 cleaup registrator function
来执行清除工作。
这些函数会在以下情况下被调用:1. watch 被重复执行、2. watch 被强制stop了。
示例:(watch 被重复执行,只执行最后一次 watch(性能优化))
<template>
<div>
<input type="text" v-model="refCount">
<!-- 执行效果为,当停止输入1s 后,控制台打印输入内容 -->
</div>
</template>
<script>
import {
ref,watch} from '@vue/composition-api'
export default {
setup(){
const refCount = ref(''); //用ref 创建响应式数据
const asyncWatch = val =>{
//定义返回异步任务的函数
return setTimeout(()=>{
//return 出 timeId
console.log(val)
},1000)
}
watch(refCount,(refCount,newCount,onclear)=>{
//watch对无效的异步任务进行清除
const timeId = asyncWatch(refCount); //监视 refCount 变化
onclear(()=>clearTimeout(timeId)) // 当 1s 内 refCount 发生变化,就清除上次定时器(结果)
})
return{
refCount,
asyncWatch
}
}
}
</script>
6. 钩子函数
vue 3.x 的生命周期函数,可以按需到入到组件中,且只能在 setup()
函数中使用
import {
onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
钩子函数:vue 2.x 与 vue 3.x 的映射关系
- beforeCreate -> use setup()
- created -> use setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- activated -> onActivated
- deactivated -> onDeactivated
- errorCaptured -> onErrorCaptured
7. provide & inject
provide()
和 inject()
可以实现嵌套组件之间的数据传递,这两个传递函数只能在 setup()
函数中使用。父组件使用 provide()
函数向下传递数据,子(后代)
组件使用 inject()
获取上层传递过来的数据。
7.1 共享普通数据
父组件:
<template>
<div>
我是父组件
<com-son></com-son>
</div>
</template>
<script>
import {
provide} from '@vue/composition-api'
import ComSon from './ComSon.vue'
export default {
components:{
'com-son':ComSon
},
setup(){
provide('color','red') // 父组件通过provide 传递数据
}
}
</script>
子组件:
<template>
<div>
<p :style="{
color:color}">我是子组件</p>
</div>
</template>
<script>
import {
inject} from '@vue/composition-api'
export default {
setup(){
const color = inject('color') //子组件通过 inject 接收数据
return {
color
}
}
}
</script>
执行结果为:子组件的字体变为了红色
注意:父组件若要向下传递多条数据,使用 provide 需要一条条创建;对于数据多的情况,完全可以使用:组件间传值方式,或者 vuex (Vuex解读)
7.2 共享响应式数据
父组件:
<template>
<div>
<p>我是父组件</p>
<button @click="refcolor = 'green'">绿色</button>
<button @click="refcolor = 'orange'">橙色</button>
<button @click="refcolor = 'pink'">粉色</button>
<com-son></com-son>
</div>
</template>
<script>
import {
provide,ref} from '@vue/composition-api'
import ComSon from './ComSon.vue'
export default {
components:{
'com-son':ComSon
},
setup(){
const refcolor = ref('red')
provide('color',refcolor)
return{
refcolor
}
}
}
</script>
子组件(同上一个子组件)
执行结果:
8. template refs
通过 ref() 可以获取页面上元素或组件
8.1 元素的引用
示例:
<template>
<div>
<p ref="refp">我是p标签</p>
</div>
</template>
<script>
import {
ref,onMounted} from '@vue/composition-api'
export default {
setup(){
const refp = ref(null); // 创建一个 DOM 引用(名字必须与标签内 ref 值一致)
// 在DOM 首次加载完毕,才能获取元素的引用
onMounted(()=>{
// 为DOM 元素设置字体颜色 (refp.value 是原生的DOM 对象)
refp.value.style.color = 'red'
console.log(refp)
})
return{
refp
}
}
}
</script>
执行结果:
注意:其跟vue 2.x 的 vm.$refs 相比较,似乎更加灵活,传送:refs
vm.$refs 演示
示例:
<template>
<div>
<p ref="refTest">我是用来测试的</p>
</div>
</template>
<script>
export default {
data() {
return {
};
},
mounted() {
console.log(this.$refs.refTest);
},
};
</script>
结果:
8.2 组件的引用
示例:
父组件:
<template>
<div>
<p>我是父组件</p>
<com-son ref="refSon"></com-son>
</div>
</template>
<script>
import {
ref,onMounted} from '@vue/composition-api'
import ComSon from './ComSon.vue'
export default {
components:{
'com-son':ComSon
},
setup(){
const refSon = ref(null);
onMounted(()=>{
console.log(refSon)
})
return{
refSon
}
}
}
</script>
子组件:
<template>
<div>
<p>我是子组件</p>
</div>
</template>
<script>
import {
ref} from '@vue/composition-api'
export default {
setup(){
const str = ref('我是子组件的内容')
return {
str
}
}
}
</script>
执行结果:
注意:此处获取了子组件的所有属性及方法,亦可灵活的操控子组件
写在最后
Vue 的进步也预示着各方面技术正朝着更全面的方向发展,未来的技术人可谓任重道远,所以我们更应该不断紧跟技术潮流。让自己成为 “弄潮人”。come on !!! 最后附上要学习的: composition-api。哈哈哈哈