接下来我们就以前面创建的mini-vue3为基础来实现Vue3新增的API,首先要讲的就是Composition API。
什么是Compostion API呢?顾名思义,组合式API。相对而言我们在Vue2中使用的叫Options API,也即配置型API,我们的代码通过Vue给定的options,将代码写入到各个options中。比如mountd是虚拟dom装载完成之后的钩子,因此在SSR的服务端这个钩子不会执行。然后我们的data定义必须放到data中,计算属性则需要放在computed,以及methods等不同的options。
使用Option API的好处,我个人而言,觉得这个最大的好处就是降低了门槛,也就是说:因为你必须按照给定的option来组织你的代码,即使你的代码写的真的挺乱,但是因为有这个Options在,所以别人阅读你的代码还是有章可循的,知道从哪里开始,对应的方法应该去哪里找,也就是说提升了你的下限。既然提到了好处,自然要说下缺点。缺点就是因为功能点的分散。
比如我要想在created里面去拉取数据,然后进行渲染,我们可能得在method里定义两个方法,一个是拉取数据,一个是渲染数据。
然后我们要在created里面调用method里的方法A,在方法A里面我们去调用拉取数据的方法,然后再调用渲染数据的方法,因此会导致我们原本应该是写在一起的一个业务逻辑的代码要被分散到各处。当然这个主要是针对一些对性能或是代码架构有高要求的场景。一般的情况下确实并不需要这个。不过既然有场景,那也还是要能满足,因此Vue3推出了Composition API,通过setup方法来实现,我们可以看到官方文档对这个的定义:一组低侵入式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑。而且这个对于Vue3来说是可选的,也就是说如果你不想用组合式API,那么你仍然可以继续使用Vue2的OptionAPI。
具体的相关API的文档可以参考Composition API
对于一些统一的这种业务逻辑代码,原来可能每个组件内都需要去写,而组件的重用并不是那么容易实现。不过通过组合式API,则可以将很多公共逻辑抽象为公共方法,然后就可以很好的复用了.
但是组合式API也有缺点,他的缺点就是因为太自由了,所以对于水品没那么高的同学来说,很可能写的代码就能了一团乱麻,也就是所谓的面条代码。因此组合式API虽然可以提高上限,但是下限也低了。ok,说完了概念,就应该来具体实现了:
<html> <head> <title>Composition API</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"></div> <script> const { createApp, ref, onMounted, onUnmounted } = Vue; // options写法 // const App = { // template: `X: {{ x }}, Y:{{ y }}`, // data() { // return { // x: 0, // y: 0, // } // }, // mounted() { // window.addEventListener('mousemove', this.moveFunc); // }, // unmounted() { // window.removeEventListener('mousemove', this.moveFunc) // }, // methods: { // moveFunc(e) { // this.x = e.pageX; // this.y = e.pageY; // }, // } // }; // 组合式API写法 function useMouse() { const x = ref(0); const y = ref(0); const moveFunc = (e) => { x.value = e.pageX; y.value = e.pageY; }; // 挂载时绑定方法 onMounted(() => { window.addEventListener('mousemove', moveFunc); }); // 卸载时移除绑定 onUnmounted(() => { window.removeEventListener('mousemove', moveFunc); }); return { x, y }; } const App = { setup() { const { x, y } = useMouse(); // 关于这里为什么要解构而不是直接返回函数结果,尤大表示,这里可能会有其他的变量需要返回,以及可能会有冲突的情况需要 // 来进行处理 return { x, y, }; }, template: `X: {{ x }}, Y:{{ y }}`, }; createApp(App).mount('#app'); </script> </body> </html>
这段代码同时使用了options API和组合式API,可以看到使用组合式API的情况下,代码的复用变得简单,而且逻辑清晰。
接下来我们可以看一个上文提到的例子,也就是说,一个立即获取数据并展示,然后当属性变化时,再次重新获取数据并展示的例子:
<html> <head> <title>Vue3 Fetch Demo</title> </head> <body> <div id="app"></div> <script src="https://unpkg.com/vue@next"></script> <script> const { createApp, ref, watchEffect } = Vue; // fetch相关逻辑 // 通过使用watchEffect方法,第一次就会立即获取数据 // 后续id发生改变的情况下则会重新获取数据 function useFetch(fetchUrl) { // 尤大这里不建议使用reactive,类似 const state = reactive({}); // 的方式是因为reactive的值不能被解构,否则会失去响应性。如果一定要用reactive // 则需要在返回时通过toRefs封装 const data = ref(null); const error = ref(null); const isLoading = ref(true); watchEffect(() => { // 开始获取数据时需要重置原数据 data.value = null; error.value = null; isLoading.value = true; fetch(fetchUrl()).then(res => res.json()).then(result => { // 加载完成,赋值并将loading置为false data.value = result; isLoading.value = false; }).catch(err => { error.value = err; isLoading.value = false; }); }); return { data, error, isLoading, }; } const Post = { template: ` <div v-if="isLoading">Loading...</div> <div v-else-if="data">{{ data }}</div> <div v-else-if="error"> Fetch Data Error: {{ error.message }} </div> `, props: { id: Number, }, setup(props) { console.log(props) const fetchUrl = () => { return `https://jsonplaceholder.typicode.com/todos/${props.id}`; }; const { data, error, isLoading } = useFetch(fetchUrl); return { data, error, isLoading, } }, }; // 通过props的方式传入id,并且在id改变后会触发Post组件的重新获取数据和重新渲染 const App = { components: { Post, }, data() { return { id: 1, }; }, template: ` <button @click="id += 1">Plus ID</button> <Post :id="id" /> `, }; createApp(App).mount('#app'); </script> </body> </html>
在这段示例代码中我们就不必像options API那样在多个options处增加不相关联的逻辑,在组合式API中,相关逻辑可以写在一处。当然,组合式API也不是银弹。并不是适合所有场景,尤其是当作者对抽象把握还没有那么好的时候,options API更能保证大家写的代码具有一定的条理性。在Vue3的官网也有许多的关于在什么场景如何应用组合式API的例子,可以去看。我也会继续阅读并通过自己的理解来写出更多的关于Vue3的API的示例和解释。
下一章节目标通过组合式API来实现一个小的系统。