Code前端首页关于Code前端联系我们

刚学Vue3怎么写出不被同事吐槽的代码?

terry 5小时前 阅读数 72 #Vue

身边刚从Vue2转过来或者直接入门Vue3的小伙伴,经常刚写完页面就被拉去改个三五次:命名太随便看不懂、Composition API拆得像乱麻、性能没考虑到浏览器卡半天、连插槽怎么用才好都搞不清楚,其实要避免这些问题,核心不是死记硬背几十条硬杠杠,而是抓住“可维护、可复用、性能稳”这三个底层逻辑,再结合官方推荐的主流规范来落地。

从命名开始,做一个“写代码给人看”的开发者

刚学编程的时候,我们总觉得“功能跑起来就行”,但在团队协作里,“功能跑起来+别人半小时能看懂逻辑脉络”才是合格的门槛,Vue3的命名规范和Vue2有延续,但也有细节升级,核心原则是语义化、一致性、不混淆

先说说组件的命名,不管是单文件组件的文件名,还是组件内部的注册名、使用名,都要统一,官方推荐用大驼峰(PascalCase)命名单文件组件,比如ProductCard.vue、UserProfile.vue,这种方式在编辑器里能和普通JS/TS文件(小驼峰/短横线)快速区分,引用的时候也不会出错,注册全局组件或者局部组件的时候,继续用大驼峰,比如components: { ProductCard },在模板里可以灵活用大驼峰或者短横线,但建议一个项目里只选一种,我个人更偏向短横线,因为和HTML原生标签风格统一,不会和自定义指令搞混——自定义指令通常是短横线或者小驼峰,但模板里用得更多的也是短横线嘛,比如v-loading、v-copy这种。

再说说变量和函数,Composition API的出现让变量命名变得更重要了,很多小伙伴刚用setup的时候,会把所有变量堆在一块,比如count、data、list、show、total,读起来根本不知道count是什么的数量、list放的是什么数据,正确的做法是把相关的变量和函数绑定在一起,用语义化的前缀区分,比如商品列表页面的count可以叫productListCount,total叫productListTotal,show改成isShowProductDialog,布尔值变量最好加is/has/can这类前缀,一眼就能看出是状态判断,比如isLoggedIn、hasAuthError、canEditProduct;常量要全大写加下划线分隔,比如PRODUCT_LIST_PAGE_SIZE、DEFAULT_USER_AVATAR,一眼就知道不能随便改。

还有props和emit的命名,这个很容易踩坑!props在父组件传的时候用短横线,子组件接收的时候可以自动转成小驼峰,但为了统一,建议子组件定义props的时候也用短横线,虽然TypeScript或者TypeScript in Composition API里会自动处理,但看文档的时候更清晰,比如父组件,子组件defineProps(['product-id', 'is-hot']),用的时候用this.productId或者const { productId } = defineProps(...),emit的命名也要注意,最好用短横线+动词+名词的格式,传递事件的时候更明确,比如子组件触发删除操作,defineEmits(['delete-product']),父组件监听的时候就是<product-card @delete-product="handleDeleteProduct" />,千万不要直接传delete,因为delete是JS的保留字,虽然不报错但容易混淆,而且语义化也不够。

Composition API怎么用才不乱?别只会堆在setup里

Composition API是Vue3最大的亮点之一,但很多刚入门的小伙伴只会把所有变量、函数、生命周期钩子都堆在setup函数里,几百行的代码下来,找个逻辑要翻半天,还不如Vue2的Options API清晰,其实官方早就给了我们解决方案——组合式函数(Composables),就是把相关的逻辑抽成一个独立的函数,放在单独的文件里,文件名最好用use开头,比如useProductList.js、useUserAuth.js、useThemeToggle.js。

那怎么判断一段逻辑应该抽成组合式函数呢?有三个小标准:一是这段逻辑涉及多个Vue核心特性,比如ref、computed、watch、onMounted,单独抽出来更清晰;二是这段逻辑在多个组件里会用到,比如登录状态的判断、列表的分页加载;三是这段逻辑本身比较复杂,比如图表的数据处理、文件的上传逻辑,举个简单的例子,商品列表的分页加载,涉及到productList、currentPage、pageSize、total、isLoading、error这些变量,还有fetchProductList、handlePageChange这些函数,完全可以抽成一个useProductList.js的文件:

在useProductList.js里,我们可以接收父组件传过来的初始参数,比如pageSize、sortType,然后定义内部的变量,实现fetchProductList的逻辑,最后把需要暴露给外部的变量和函数return出去,在组件里,我们只需要import这个组合式函数,然后调用它,接收返回值,就可以直接用了,setup函数里瞬间变得清爽,只需要处理组件特有的逻辑,比如产品卡片的点击事件、筛选条件的切换。

除了组合式函数,Composition API里的代码顺序也很重要,建议按照“依赖引入→props/emit定义→响应式变量定义→计算属性→监听器→生命周期钩子→内部函数→暴露给模板的变量和函数”的顺序来写,这样其他同事读代码的时候,能按照逻辑顺序一步步往下走,不会东找西找,比如先引入vue的ref、computed、watch,引入组合式函数useProductList,然后defineProps、defineEmits,接着定义组件特有的响应式变量,比如isShowFilterDialog,再写计算属性,比如filteredProductList(如果有的话),再写监听器,比如监听searchKeyword的变化调用fetchProductList,再写生命周期钩子,比如onMounted的时候调用一次fetchProductList,再写内部函数,比如handleFilterSubmit,最后return需要的变量和函数。

插槽该怎么用?不是所有地方都要传props

很多刚学Vue3的小伙伴,习惯用props传递所有内容,比如产品卡片的标题、图片、价格,这些用props没问题,但如果要传递一些复杂的内容,比如产品卡片的自定义按钮、标签区域,用props就会很麻烦,甚至会让组件变得臃肿,这时候就该用插槽了,Vue3的插槽和Vue2相比,有很大的升级,比如默认插槽可以直接用,具名插槽和作用域插槽的写法更统一,还新增了动态插槽名。

首先说默认插槽,默认插槽是最简单的,就是在组件里用占位,父组件使用的时候,在组件标签里写内容,这些内容就会填充到的位置,比如一个简单的Card组件,模板可以是

,父组件使用的时候就是,这样Card组件就可以灵活地展示任何内容,不需要传任何props。

然后是具名插槽,当组件需要多个占位的时候,就需要用具名插槽了,具名插槽用来定义,父组件使用的时候用v-slot:xxx或者简写#xxx来指定内容的位置,比如上面的Card组件,我们可以加上header和footer插槽,模板变成

,这里的默认插槽其实就是,父组件使用的时候就是<template #header><template #footer>,这样Card组件的结构就更清晰了,父组件可以灵活地控制每个部分的内容。

作用域插槽,作用域插槽是Vue3最强大的特性之一,它可以让子组件把自己内部的数据传给父组件,父组件再根据这些数据来渲染插槽内容,比如一个ProductList组件,里面循环渲染ProductCard,我们可以给ProductList加一个作用域插槽,让父组件自己决定每个产品卡片的样式和内容,不需要把ProductCard的逻辑硬编码在ProductList里,ProductList的模板可以是

,父组件使用的时候就是<template #item="{ product, isHot }"><template #tag>热卖,这样ProductList组件就只负责数据的获取和列表的渲染,具体的展示完全交给父组件,复用性大大提高。

性能优化要从日常做起,不是只有上线前才做

很多小伙伴觉得性能优化是一件很复杂的事情,只有上线前才需要做,其实不然,性能优化要从日常写代码的细节做起,这样才能写出性能稳定的代码,Vue3本身已经做了很多性能优化,比如虚拟DOM的优化、编译时的静态提升、事件监听的缓存,但我们自己写代码的时候也可以做一些优化,让代码的性能更好。

首先说说响应式数据的定义,不要把所有数据都定义成响应式的,只有那些会变化、并且需要在模板里展示的数据才需要定义成ref或者reactive,比如一些常量、不会变化的配置项,直接用普通的JS变量或者const定义就行,这样可以减少Vue的响应式追踪,提高性能,reactive最好用来定义复杂的对象或者数组,ref可以用来定义基本类型数据、复杂的对象或者数组——虽然ref也可以定义复杂数据,但Vue内部会自动把它转成reactive,所以基本类型数据用ref,复杂数据如果需要整个替换的话用ref,不需要的话用reactive,这样写起来更顺手,性能也差不多。

然后说说v-for和v-if的使用顺序,这个是Vue2和Vue3都要注意的问题!千万不要把v-if写在v-for的同一个元素上,因为v-for的优先级比v-if高,Vue会先循环渲染所有元素,然后再判断v-if,这样会浪费很多性能,正确的做法是把v-if写在v-for的父元素上,或者用计算属性先过滤掉不需要的数据,然后再循环渲染,比如我们要渲染所有热卖的产品,不要写成

...
,而是用计算属性先过滤:const hotProductList = computed(() => productList.value.filter(product => product.sales > 1000)),然后再写成
...
,这样Vue只会循环渲染热卖的产品,性能会好很多。

再说说key的使用,key是Vue用来追踪DOM节点的唯一标识,千万不要用index作为key!因为如果列表的顺序发生变化,或者有元素被添加、删除,index就会发生变化,Vue会错误地复用DOM节点,导致渲染错误或者性能问题,正确的做法是用数据的唯一标识作为key,比如product.id、user.userId,如果实在没有唯一标识,可以用Math.random()生成一个,但最好还是让后端返回唯一标识。

最后说说组件的懒加载,对于一些不常用的组件,比如弹窗、侧边栏、图表,我们可以用Vue的异步组件或者路由的懒加载来加载,这样可以减少首屏的加载时间,提高用户体验,异步组件的写法很简单,Vue3里用defineAsyncComponent函数,比如const ProductDialog = defineAsyncComponent(() => import('./ProductDialog.vue')),这样只有当ProductDialog组件被使用的时候,才会加载它的JS文件,路由的懒加载更简单,直接在路由配置里用() => import('./xxx.vue')就行, path: '/product', component: () => import('./ProductList.vue') },这样只有当用户访问/product路由的时候,才会加载ProductList.vue的文件。

其他一些容易被忽略的小细节

除了上面说的这些,还有一些容易被忽略的小细节,做好了也能让你的代码更规范,不被同事吐槽。

比如注释,虽然我们一直强调代码要语义化,尽量用代码本身来表达逻辑,但有时候一些复杂的逻辑、特殊的处理还是需要加注释的,注释不要写废话,定义一个变量count”这种,而是要写“为什么这么做”“注意事项是什么”“参数和返回值是什么”,对于组合式函数、复杂的函数,最好加JSDoc注释,这样编辑器可以自动提示,其他同事读代码的时候也更方便。

比如代码格式,一个项目里的代码格式一定要统一,不要有的用2个空格缩进,有的用4个空格缩进,有的加分号,有的不加分号,我们可以用ESLint和Prettier来自动规范代码格式,ESLint用来检查代码的语法和逻辑错误,Prettier用来自动格式化代码,两者配合使用效果最好,我们可以在项目的根目录里创建.eslintrc.js和.prettierrc.js文件,配置好规则,然后在package.json里添加lint和lint:fix脚本,这样每次提交代码之前,可以自动检查和修复代码格式。

比如TypeScript的使用,虽然Vue3支持纯JavaScript开发,但TypeScript可以提供更好的类型检查,减少运行时的错误,提高代码的可维护性,如果是团队项目,建议尽量用TypeScript开发,TypeScript in Composition API的写法也很简单,defineProps、defineEmits、ref、reactive都可以加类型注解。

比如测试,虽然很多小伙伴觉得测试很麻烦,浪费时间,但测试可以保证代码的质量,减少后期的bug修复成本,Vue3支持很多测试框架,比如Vitest、Jest,我们可以写一些单元测试、组件测试,覆盖核心的逻辑。

刚学Vue3写出不被同事吐槽的代码,不是一蹴而就的事情,需要我们在日常开发中不断积累经验,不断优化自己的代码,核心就是抓住“可维护、可复用、性能稳”这三个底层逻辑,再结合官方推荐的主流规范来落地,这样写出的代码不仅自己读起来舒服,同事读起来也舒服,团队协作起来也更高效。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门