我的回答
将接口的可重复部分及其功能提取到可重用的代码段中, 使我们的应用程序在可维护性和灵活性方面走得更远
组合式 API 可以将用户的逻辑关注点相关的代码配置在一起
参考回答
首先要了解以下 Composition API 设计的好处在哪里? 逻辑组合和复用、类型推导、打包尺寸等
在 vue3.0 之前所有的组件都可以看作一个可选项的配置集合,通过 data、computed、methods、watch、以及 created、mounted 等声明周期函数,用这个可选项集合来声明一个组件。
这样写的好处是组织结构清晰,但是在逻辑复用上就不太友好啦,我们都知道的是 js 中最简洁清晰的复用方式就是将逻辑封装到一个函数中,然后函数与函数之间相互调用。
Vue3.0 很好的支持 TS,而 TS 的最重要的一个特性就是类型推导,而函数相对于嵌套的对象来说对类型推导更加友好
另外,以函数形式组织的模块以具名方式导入使用,在tree-sharking的时候支持会更好
setup 函数
setup()函数式 vue3 中专门为组件提供的一个新属性,它是我们在使用 vue3 的 composition-api 的统一入口。也就是说我们使用的新特性都要在这个函数中进行。
执行时机:setup 函数会在beforeCreate()之后,created()之前执行。
参数: props、context
使用: 第一步和之前的写法一样,需要在 props 中定义外界传入的参数类型等
props: {msg: String;}
setup函数的第一个形参就是用来接收props的数据的
setup(props) {console.log(props);}
setup函数的第二个形参是一个上下文对象(context), 它包含了以下在vue2中组件实例的一些属性(需要通过this来调用的),包括如下:
setup(props, context) {// 需要注意的是:在setup函数中,无法访问thisconsole.log(props);console.log(context);console.log(context.attrs);console.log(context.slots);console.log(context.parent);console.log(context.emit);}
响应数据声明
在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) {// 需要注意的是:页面所需要的数据必须在函数最后return出来,才能在页面模板中使用。const state = reactive({ count: 0 });return state;}};</script>
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>
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>
watch
watch()函数用来监听某些数据的变化,从而触发某些特定的操作
基本用法:
const refCount = ref(0);watch(() => console.warn(refCount.value));setInterval(() => {refCount.value++;}, 1000);
监视多个数据源:
// reactiveconst 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);// reflet 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函数的返回值 返回值为functionconst removeWtach = watch(()=>{})// 调用返回值函数,清除监听removeWtach()
在watch中清除无效的异步任务: 有时候当被watch函数监视的值发生变化时,或者watch本身被stop之后,我们期望能够清除哪些无效的异步任务,此时,watch回调函数提供了一个清除函数来执行清除工作。调用场景:
- watch被重复执行
watch被强制stop
<template><div><input type="text" v-model="keyword" /></div></template><script>import { ref, watch } from "vue";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>
生命周期
新的生命周期函数需要按需导入,并且在setup函数内使用。
vue2.x和vue3.x的关系:
- beforeCreate() —- setup()
- created() —- setup()
- beforeMount() —- onBeforeMount()
- mounted() —- onMounted()
- beforeUpdate() —- onBeforeUpdate()
- update() —- onUpdate()
- beforeDestory() —- onBeforeUnmount()
- destoryed() —- onUnmounted()
- errorCaptured() —- onErrorCaptured()
setup(){onBeforeMount(()=>{console.log("onBeforeMount")})onMounted(()=>{console.log("onMounted")})onBeforeUnmount(()=>{console.log("onBeforeUnmount")})// ...}
provide & inject
在vue2.x的时候,我们可以使用provide和inject实现嵌套组件之间的数据传递。但是在vue3.x中需要在setup()函数内使用。 ```javascript // 父组件 setup() { const father = ref(“父组件传递的”); provide(“father”, father); }
// 子组件 setup() { const data = inject(“father”); return { data }; }
<a name="FA68J"></a>### 获取页面DOM或者组件```javascript<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>
defineComponent
这个函数仅仅提供了类型判断,主要是为了更好的结合TS来使用,能为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.
