Vue3 Compitition Api
首先要了解一下 Composition API 设计的好处在哪里?逻辑组合和复用、类型推导、打包尺寸等。
在 vue3.0 之前所有的组件都可以看做一个可选项的配置集合,通过 data、computed、methods、watch 以及 created、mounted 等生命周期函数,用这个可选项集合来声明一个组件。
这样写的好处是组织结构清晰,但是在逻辑复用上就不太友好了。我们都知道,js 中最简洁清晰的复用方式就是将逻辑封装到一个函数中,然后函数与函数之间相互调用。
Vue3.0 很好的支持了 Typescript,而 Typescript 的最重要的一个特性就是类型推导,而函数相对于嵌套的对象来说对类型推导更加的友好。
另外,以函数形式组织的模块以具名方式导入使用,在tree-sharking的时候支持会更好。
1.1 step 函数
setup()函数式vue3中专门为组件提供的一个新属性,它是我们在使用vue3的composition-api的统一入口。也就是说我们使用的新特性都要在这个函数中进行。
执行时机:
setup函数会在beforeCreate()之后,created()之前执行。
参数:
- props
- context
使用:
- 第一步和之前的写法一样,需要在props中定义外界传入的参数类型等
props: {
msg: String
}
- setup函数的第一个形参就是用来接受props数据的。
setup(props) {
console.log(props);
}
- context
setup函数的第二个形参是一个上下文对象,它包含了一下在vue2中组件实例的一些属性(需要通过this来调用的),包括如下:
setup(props, context) {
console.log(props);
console.log(context);
console.log(context.attrs);
console.log(context.slots);
console.log(context.parent);
console.log(context.root);
console.log(context.emit);
console.log(context.refs);
}
注意:在setup函数中,无法访问this。
1.2 响应数据声明
在vue2中创建响应式数据的方式为:data、props; 其中data中的数据是双向的,而props中的数据的单向的。 在新特性中有两种方式能创建响应式数据:reactive()和ref()。
reactive:
reactive()函数接收一个普通对象,返回一个响应式的数据对象。 等价于vue2中的Vue.observable()函数。下面实现一个数量加一的操作。
<template>
<div>
<div>{{count}}</div>
<button @click="count+=1">+1</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
setup(props, context) {
const state = reactive({ count: 0 });
return state;
}
};
</script>
需要注意的是页面需要的数据必须在函数最后retrun出来,才能在页面模板中使用。
ref:
ref()函数将一个给定的值转为响应式的数据对象,它返回一个对象,响应式的数据值需要通过调用value属性访问,而在页面模板中则可直接访问。如上实现一个数量加一的操作。
<template>
<div>
<div>{{count}}</div>
<button @click="count+=1">+1</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "ref",
setup() {
const count = ref("1");
console.log(count.value);
count.value++;
console.log(count.value);
return {
count
};
}
};
</script>
isRef:
isRef()用来判断某个值是否为ref()函数创建出来的对象。
toRefs
toRefs()函数可以将reactive创建出来的响应式对象转换为通过ref()创建出来的响应式对象。
<template>
<div ref="app">
<div>{{count}}</div>
<button @click="add">+1</button>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
name: "toRefs",
setup() {
const state = reactive({
count: 0
});
const add = () => [state.count++];
return {
...toRefs(state),
add
};
}
};
</script>
1.3 computed
computed()用来创建计算属性,computed()函数的返回值是一个ref的实例。
创建只读的计算属性:
在调用computed()函数期间,传入一个函数,可以得到一个只读的计算属性
创建可读可写的计算属性:
在调用computed()函数期间,传入一个包含get和set的对象,可以得到一个可读可写的计算属性。
<template>
<div class="computed">
{{computedCountReadOnly}}
<button @click="refCountReadOnly+=1">+1</button>
<div>{{computedCountReadWrite}}</div>
</div>
</template>
<script>
import { ref, computed } from "vue";
export default {
setup() {
const refCountReadOnly = ref(0);
// 只读的计算属性
const computedCountReadOnly = computed(
() => refCountReadOnly.value + 1
);
// computedCountReadOnly.value = 9; // 这行代码会报错
// 可读可写的计算属性
const refCountReadWrite = ref(0);
const computedCountReadWrite = computed({
set: val => {
refCountReadWrite.value = val - 1;
},
get: () => refCountReadWrite.value + 1
});
computedCountReadWrite.value = 11;
return {
refCountReadOnly,
computedCountReadOnly,
refCountReadWrite,
computedCountReadWrite
};
}
};
</script>
1.4 Watch
watch()函数用来监听某些数据的变化,从而触发某些特定的操作。
基本用法:
const refCount = ref(0);
watch(() => console.warn(refCount.value));
setInterval(() => {
refCount.value++;
}, 1000);
监视多个数据源:
// reactive
const state = reactive({ count: 0, name: "jokul" });
watch(
[() => state.count, () => state.name],
([newCount, newName], [oldCount, oldName]) => {
console.log(`reactive $count 旧值:${oldCount}; 新值:${newCount}`);
console.log(`reactive $name 旧值:${oldName}; 新值:${newName}`);
},
{
lazy: true
}
);
setTimeout(() => {
state.count = 27;
state.name = "Jokul";
}, 2000);
// ref
let refCount = ref(0);
let refName = ref("guohh");
watch(
[refCount, refName],
([newCount, newName], [oldCount, oldName]) => {
console.log(`ref $count 旧值:${oldCount}; 新值:${newCount}`);
console.log(`ref $name 旧值:${oldName}; 新值:${newName}`);
},
{
lazy: true
}
);
setTimeout(() => {
refCount.value = 27;
refName.value = "Jokul";
}, 2000);
清除监听:
在setup()函数中创建的watch()监听,会在当前组件被销毁的时候自动清除,如果想要明确地或者提前结束某个监听,可以调用watch()的返回值。
// 定义变量接受watch函数的返回值 返回值为function
const removeWtach = watch(()=>{})
// 调用返回值函数,清除监听
removeWtach()
在watch中清除无效的异步任务:
有时候当被watch函数监视的值发生变化时,或者watch本身被stop之后,我们期望能够清楚哪些无效的异步任务,此时,watch回调函数提供了一个清除函数来执行清除工作。调用场景:
- watch被重复执行
- watch被强制stop
<template>
<div>
<input type="text" v-model="keyword" />
</div>
</template>
import { ref, watch } from "vue";
<script>
setup(){
const keyword = ref("");
const asyncprint = val => {
return setTimeout(() => {
console.log(val);
}, 1000);
};
watch(
keyword,
(newVal, oldVal, clean) => {
const timer = asyncprint(newVal);
clean(() => clearTimeout(timer));
},
{ lazy: true }
);
return { keyword };
}
</script>
1.5 生命周期
新的生命周期函数需要按需导入,并且在setup函数内使用。vue2.x与vue3.x的关系:
- beforeCreate() ☞ setup()
- created() ☞ setup()
- beforeMount() ☞ onBeforeMount()
- mounted() ☞ onMounted()
- beforeUpdate() ☞ onBeforeUpdate()
- updated() ☞ onUpdated()
- beforeDestory() ☞ onBeforeUnmount()
- destoryed() ☞ onUnmounted()
- errorCaptured() ☞ onErrorCaptured()
onBeforeMount(()=>{
console.log("onBeforeMount")
})
onMounted(()=>{
console.log("onMounted")
})
onBeforeUnmount(()=>{
console.log("onBeforeUnmount")
})
// ...
}
1.5 provide & inject
在vue2.x的时候,我们可以使用provide和inject实现嵌套组件之间的数据传递,但是在vue3.x中需要在setup()函数内使用。
// 父组件
setup() {
const father = ref("父组件传递的");
provide("father", father);
}
// 子组件
setup() {
const data = inject("father");
return {
data
};
}
1.6 获取页面DOM或者组件
<template>
<div ref="divRef">页面dom</div>
</template>
<script>
import { ref, onMounted } from "vue";
export default {
setup() {
const divRef = ref(null);
onMounted(() => {
divRef.value.style.color = "blue";
});
return {
divRef
};
}
};
</script>
1.7 defineComponent
这个函数仅仅提供了类型推断,主要是为了更好的结合TypeScript来使用,能为setup()函数中的props提供完整的类型推断。
<script>
import { defineComponent } from "@vue/composition-api";
export default defineComponent({
props: {
foo: String
},
setup(props) {
console.warn(typeof props.foo);
}
});
</script>
当传递一个数字的foo时,页面就会报错
[Vue warn]: Invalid prop: type check failed for prop "foo". Expected String with value "0", got Number with value 0.