感受 Vue3 的魔法力量
作者:京东科技 牛至伟
近半年有幸参与了一个创新项目,由于没有任何历史包袱,所以选择了 Vue3 技术栈,总体来说感受如下:
• setup 语法糖<script setup lang="ts">摆脱了书写声明式的代码,用起来很流畅,提升不少效率
• 可以通过 Composition API(组合式 API)封装可复用逻辑,将 UI 和逻辑分离,提高复用性,view 层代码展示更清晰
• 和 Vue3 更搭配的状态管理库 Pinia,少去了很多配置,使用起来更便捷
• 构建工具 Vite,基于 ESM 和 Rollup,省去本地开发时的编译步骤,但是 build 打包时还是会编译(考虑到兼容性)
• 必备 VSCode 插件 Volar,支持 Vue3 内置 API 的 TS 类型推断,但是不兼容 Vue2,如果需要在 Vue2 和 Vue3 项目中切换,比较麻烦
当然也遇到一些问题,最典型的就是响应式相关的问题
响应式篇
本篇主要借助 watch 函数,理解 ref、reactive 等响应式数据/状态,有兴趣的同学可以查看 Vue3 源代码部分加深理解,
watch 数据源可以是 ref (包括计算属性)、响应式对象、getter 函数、或多个数据源组成的数组
总结:
在 Vue3 中状态都是默认深层响应式的(情景七),嵌套的引用类型在取值(get)时一定是返回 Proxy 响应式对象
watch 数据源为响应式对象时(情景四、七、九),会隐式的创建一个深层侦听器,不需要再显示设置 deep: true
情景三和情景八两种情况下,必须显示设置 deep: true,强制转换为深层侦听器
情景五和情景七对比下,虽然写法完全相同,但是如果属性值为基本类型时是监听不到的,尤其是 ts 类型声明为 any 时,ide 也不会提示告警,导致排查问题比较费力
所以精确的 ts 类型声明很重要,否则经常会出现莫名其妙的 watch 不生效的问题
ref 值为基本类型时通过 get\set 拦截实现响应式;ref 值为引用类型时通过将.value 属性转换为 reactive 响应式对象实现;
deep 会影响性能,而 reactive 会隐式的设置 deep: true,所以只有明确状态数据结构比较简单且数据量不大时使用 reactive,其他一律使用 ref
Props 篇
设置默认值
双向绑定(多个值)
• 自定义组件
• 使用组件
单向数据流
大部分情况下应该遵循【单向数据流】原则,禁止子组件直接修改 props,否则复杂应用下的数据流将变得混乱,极易出现 bug 且难排查
直接修改 props 会有告警,但是如果 props 是引用类型,修改 props 内部值将不会有告警提示,因此应该有团队约定(第 5 条除外)
如果 props 为引用类型,赋值到子组件状态时,需要解除引用(第 5 条除外)
复杂的逻辑,可以将状态以及修改状态的方法,封装成自定义 hooks 或者提升到 store 内部,避免 props 的层层传递与修改
一些父子组件本就紧密耦合的场景下,可以允许修改 props 内部的值,可以减少很多复杂度和工作量(需要团队约定固定场景)
逻辑/UI 解耦篇
利用 Vue3 的 Composition/组合式 API,将某种逻辑涉及到的状态,以及修改状态的方法封装成一个自定义 hook,将组件中的逻辑解耦,这样即使 UI 有不同的形态或者调整,只要逻辑不变,就可以复用逻辑。下面是本项目中涉及的一个真实案例-逻辑树组件,UI 有 2 种形态且可以相互转化。
• hooks 部分的代码:useDynamicTree.ts
• 在不同组件中使用(UI1/UI2 组件为递归组件,内部实现不再展开)
Pinia 状态管理篇
将复杂逻辑的状态以及修改状态的方法提升到 store 内部管理,可以避免 props 的层层传递,减少 props 复杂度,状态管理更清晰
• 定义一个 store(非声明式):User.ts
• 在组件中使用
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/d8b1486507c2e31233c1d0557】。文章转载请联系作者。
评论