1 前言
Vue3的正式发布已有一段时间,其生态系统也日渐完善。近期,我尝试运用taro与vue3技术对冷链小程序进行重构,经过一段时间的开发与实际应用,我对这一过程产生了一些个人见解。
总体而言,Vue3在根本原理层面以及实际应用开发方面均实现了显著提升。
在源代码的层面上,通过采用替代点号的API,一方面实现了代理对象的功能,另一方面实现了对属性递归监控的优化,这显著提升了性能;同时,解决了对象动态属性添加和数组变化监听的问题;对diff算法进行了改进,引入了静态标记,大幅提升了Vue的执行速度;此外,还实施了静态提升、事件监听缓存等多种提升效率的策略。
在应用层面,主要的变化是将API升级为组合式API,业务开发中摒弃了data、生命周期函数等元素的分离式开发模式,从而使得代码与业务之间的关联性得到显著增强。这样的改进在代码编写、阅读以及维护过程中,都极大地提升了开发者的体验。
有了更优越的支撑,我们深知,在大型前端项目中,运用类型校验技术,能够显著提升项目的稳定性,从而为Vue3在大型项目开发领域提供了更为坚实的质量保障。
2 组合式API
所谓的组合式API,它将Vue2中的数据绑定、生命周期钩子以及数据监听等功能,都转化为了钩子函数,并将这些函数组合进函数体中。其核心要素在于函数本身。函数的存在价值,正是为了应用这些新引入的组合式API,而这些API的使用范围仅限于函数内部。
函数的执行时机位于初始化之后、实际执行之前,这意味着在此时,Vue实例尚未完成初始化,因此在此阶段不能调用this对象。函数的输出结果将被注入至Vue实例中,以便Vue组件能够访问,因此,若要在组件模板中展示数据,相关数据必须在函数内部被定义。
组合式API的构成表现在两个方面。首先,它意味着将特定业务的数据与处理流程合并,这实际上是对关注点的整合,使得编写代码和操作业务逻辑变得更加便捷,同时有助于更集中地关注业务流程,并且便于我们阅读和理解代码。在第二层含义中,若某个组件的业务流程变得相当复杂,且其代码体量变得相当庞大,我们便可在内部对相关业务模块进行进一步的提取,以此让代码的逻辑变得更加明晰,并实现了更为深入的整合效果。
按照以下示例代码操作,将业务代码部分A提取出来后,该部分代码所生成的数据便可在组件内部得以应用。
// 组件 引入函数A自模块'A' 将此代码导出,使其成为一个定义好的组件。 名称为 'componentName', setup() { ...functionA() } })
// 代码块A export default () => { return { a: 1 } }
3 响应式API
在Vue3框架中,响应式编程的核心体现在`ref`以及两个关键函数上。关于响应式API,我有两点想要探讨:首先,是关于为何要引入这一响应式API的原因;其次,则是要分析`ref`函数与另一个函数之间的相似与不同之处。
3.1 为什么增加响应式API
在Vue2框架中,所有数据都集中存储在data属性中,而这些data属性中的数据均具备响应性。然而,这种做法带来一个问题,即一些常量数据并不需要被监听,这导致资源的不必要消耗。鉴于此,Vue3引入了新的响应式API,允许开发者仅对那些需要动态更新DOM的数据进行响应式处理,而对于那些无需动态更新的数据则不进行响应式处理,从而在极大程度上优化了资源的使用效率。编写代码时,务必认真考虑哪些数据需要实现响应式绑定,哪些则无需绑定,切不可盲目地将所有数据都绑定上。否则,不仅代码逻辑可能难以理解,还可能降低程序的执行效率(尤其是对于习惯使用Vue2的开发者而言)。
3.2 ref和的异同点
在弄清楚增设响应式API的必要性之后,我们注意到Vue3推出了ref与另一个函数,二者构成了响应式API的组成部分。为何会有两个这样的API呢?为何不只用一个就足够了呢?那么,这两个API究竟有何不同之处呢?
在应用层面,对通过ref绑定的数据,我们需借助点号“.”来执行数据更新操作。同样地,对于绑定的数据,也需通过点号“.”来进行数据修改。通常情况下,针对单个的常规数据,我们倾向于使用ref来设定其响应性。至于涉及复杂数据,例如表单数据对象或某个模块的多个数据集,则通常采用点号“.”来定义其响应性。
那么,对于对象是否一定要进行定义呢?实际上并非如此,有多种选择。官方的观点是:人们可以根据自己的习惯选用不同的API。实际上,我认为,这些API各有适用的场合,其中ref更侧重于数据的变动,尤其突出数据中某个特定属性的变化。
4 思想
项目规模一旦扩大,将代码划分为模块便有助于管理。然而,若采取此措施,我们可能无意中引入了并未实际使用的代码。Tree技术则是一种能够有效剔除最终文件中冗余代码,进而优化文件体积的方法。
Vue3采纳了tree的机制,对组件及其生命周期函数等相关方法进行了拆分。这样一来,组件内部调用的代码若未被引用,便不会包含在最终的打包文件中。这一改动显著降低了Vue3项目的打包文件大小。因此,这也导致了使用方式上的变化。
4.1 生命周期函数的使用方法
从Vue库中引入了defineComponent、ref和onMounted这三个功能模块。 export default defineComponent({ name: 'Gift', setup() {
4.2 Vuex的使用方法
引入 "vuex" 库中的 "useStore" 函数。 从Vue库中引入了defineComponent、ref和computed等函数。 export default defineComponent({ name: 'Gift', setup() { const counter = ref(0); const store = useStore(); 通过使用computed属性,我们能够获取store的值,并实现与computed的协同作用。 return { counter, storeData } } })
4.3 的使用方法
import { useStore } from "vuex"; 引入了来自"vue-router"库的useRouter函数。 import { defineComponent, ref, computed } from 'vue'; export default defineComponent({ name: 'Gift', setup() { const counter = ref(0); const router = useRouter(); const onClick = () => { 执行路由跳转操作,目标为名为“AddGift”的页面。 } return { counter, onClick } } })
5 关于的使用
这部分内容主要涉及Ts的相关知识,同时它与Vue3的开发过程紧密相连。Vue3框架本身是用Ts语言编写的,所以在进行Vue3项目的开发时,我们必须使用Ts。因此,对Ts的了解是必不可少的。
关于Ts的运用细节此处不再赘述,在此需强调的是,在具体业务应用中,如何构建Ts代码的组织结构。经过广泛运用Ts,我深刻感受到:Ts的核心思想在于优先考虑数据结构,随后依据这些数据结构来开展页面开发工作。与此形成对比的是,以往的前端开发模式通常是先着手页面编写,随后才着手关注数据问题。
在着手构建一个网页的过程中,我们首先得明确若干基本要素。在网页开发阶段,我们需要特别注意以下几点:确保页面数据的准确性、关注接口返回数据的格式、以及处理请求参数的类型等细节。
下面是开发一个列表页面的例子:
// 这是列表中每一项的数据类型 interface IDataItem { id: string | number; name: string; desc: string; [key: string]: any; } 接口的返回值类型通常是未知的,故而,我们通常无法预知接口所提供数据的具体类型,这就需要我们采用泛型来应对这一不确定性。 interface IRes { code: number; msg: string; data: T } // 口返回数据类型定义 interface IDataInfo { list: Array; pageNum: number; pageSize: number; total: number; } // 请求 export const getDatalist = ( params: Record ): Promise> => { 执行Http请求,获取位于"/api/data/list"路径下的数据列表,参数为params。 };
一旦上述代码定义完毕,我们对于页面数据的理解便趋于明朗,此时直接编写页面内容会显得更为清晰,同时,出错的可能性也会显著减少。