我的回答
将接口的可重复部分及其功能提取到可重用的代码段中, 使我们的应用程序在可维护性和灵活性方面走得更远
组合式 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函数中,无法访问this
console.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);
监视多个数据源:
// 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>
<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.