# 06 | 新的代码组织方式:Composition API + ``` 在上述代码中,我们使用template标签放置模板、script 标签放置逻辑代码,并且用setup标记我们使用 ``` 这个时候我们就把清单功能独立出来了,可以在任意你需要的地方复用。在课程的后续内容中,我会详细给你介绍基于组件去搭建应用的方式。**通过这种方式,你可以实现对业务逻辑的复用。这样做的好处就是,如果有其他页面也需要用到这个功能,可以直接复用过去。** 然后,我们就可以基于新的语法实现之前的清单应用。下面的代码就是把之前的代码移植过来后,使用ref包裹的响应式数据。在你修改title和todos的时候,注意要修改响应式数据的value属性。 ```xml ``` ## 计算属性 在第二讲开发的清单应用中,我们也用到了计算属性,在Composition API的语法中,计算属性和生命周期等功能,都可以脱离Vue的组件机制单独使用 。我们向TodoList.vue代码块中加入下面的代码: ```xml ``` 在这这段代码中,具体的计算属性的逻辑和第二讲一样,区别仅在于computed的用法上。你能看到,第二讲的computed是组件的一个配置项,而这里的computed的用法是单独引入使用。 ## Composition API 拆分代码 讲到这里,可能你就会意识到,之前的累加器和清单,虽然功能都很简单,但也属于两个功能模块。如果在一个页面里有这两个功能,那就需要在data和methods里分别进行配置。但这样的话,数据和方法相关的代码会写在一起,在组件代码行数多了以后就不好维护。**所以,我们需要使用Composition API 的逻辑来拆分代码,把一个功能相关的数据和方法都维护在一起。** 但是,所有功能代码都写在一起的话,也会带来一些问题:随着功能越来越复杂,script 内部的代码也会越来越多。因此,我们可以进一步对代码进行拆分,把功能独立的模块封装成一个独立的函数,真正做到按需拆分。 在下面,我们新建了一个函数 useTodos: ```xml function useTodos() { let title = ref(""); let todos = ref([{ title: "学习Vue", done: false }]); function addTodo() { todos.value.push({ title: title.value, done: false, }); title.value = ""; } function clear() { todos.value = todos.value.filter((v) => !v.done); } let active = computed(() => { return todos.value.filter((v) => !v.done).length; }); let all = computed(() => todos.value.length); let allDone = computed({ get: function () { return active.value === 0; }, set: function (value) { todos.value.forEach((todo) => { todo.done = value; }); }, }); return { title, todos, addTodo, clear, active, all, allDone }; } ``` 这个函数就是把那些和清单相关的所有数据和方法,都放在函数内部定义并且返回,这样这个函数就可以放在任意的地方来维护。 而我们的组件入口,也就是 ``` 我们在使用Composition API 拆分功能时,也就是执行useTodos的时候,ref、computed等功能都是从 Vue 中单独引入,而不是依赖this上下文。其实你可以把组件内部的任何一段代码,从组件文件里抽离出一个独立的文件进行维护。 现在,我们引入追踪鼠标位置的需求进行讲解,比如我们项目中可能有很多地方需要显示鼠标的坐标位置,那我们就可以在项目的src/utils文件夹下面新建一个mouse.js。我们先从 Vue 中引入所需要的ref函数,然后暴露一个函数,函数内部和上面封装的useTodos类似,不过这次独立成了文件,放在utils文件下独立维护,提供给项目的所有组件使用。 ```xml import {ref} from 'vue' export function useMouse(){ const x = ref(0) const y = ref(0) return {x, y} } ``` 想获取鼠标的位置,我们就需要监听mousemove事件。这需要在组件加载完毕后执行,在Composition API中,我们可以直接引入onMounted和onUnmounted来实现生命周期的功能。 看下面的代码,组件加载的时候,会触发onMounted生命周期,我们执行监听mousemove事件,从而去更新鼠标位置的x和y的值;组件卸载的时候,会触发onUnmounted生命周期,解除mousemove事件。 ```xml import {ref, onMounted,onUnmounted} from 'vue' export function useMouse(){ const x = ref(0) const y = ref(0) function update(e) { x.value = e.pageX y.value = e.pageY } onMounted(() => { window.addEventListener('mousemove', update) }) onUnmounted(() => { window.removeEventListener('mousemove', update) }) return { x, y } } ``` 完成了上面的鼠标事件封装这一步之后,我们在组件的入口就可以和普通函数一样使用useMouse函数。在下面的代码中,上面的代码返回的x和y的值可以在模板任意地方使用,也会随着鼠标的移动而改变数值。 ```xml import {useMouse} from '../utils/mouse' let {x,y} = useMouse() ``` 相信到这里,你一定能体会到 Composition API 对代码组织方式的好处。简单来看,**因为ref和computed等功能都可以从 Vue 中全局引入,所以我们就可以把组件进行任意颗粒度的拆分和组合**,这样就大大提高了代码的可维护性和复用性。 ## ``` 在上面的代码中,我们要在 > ``` 点击累加器时文本颜色的切换效果,如下图所示: ![图片](https://static001.geekbang.org/resource/image/59/18/5974c0d7dbce32306bd2a207a6a37f18.gif?wh=343x105) ## 总结 我们来总结一下今天都学到了什么吧。今天的主要任务就是使用Composition API +