小知识,大挑战!本文正在参与“程序员必备小知识”创作活动~
🥳🥳🥳P.S. 欢迎点赞,评论,收藏~
🥳🥳🥳如有不足,欢迎指正哦~
从我开始接触Vue,就学习到了“Vue中万物皆组件”的概念,秉持着这个原则,我在写代码过程中,能抽成组件的抽成组件,以组件为维度进行维护,逻辑复用,UI复用。但是随着项目的扩大,需求的迭代,我发现很多组件已经不能再复用了,怕出现动一处就牵动全身的问题,也怕无端的夸大影响范围,更怕不知从何下手解决。那出现该问题后,最简单的方法,就是再新建一个符合当前业务场景的组件,把原来的组件逻辑修改后paste进去,这也左粘粘,右贴贴,最后也就完成了。可是一旦这样做了,这个项目就离重构近了一步。
不是说以组件的维度去维护代码有啥错误,而是没有搞懂什么逻辑需要放在组件中实现,什么逻辑需要依赖注入进来。换句话说,就是在不知道如何实现组件的高内聚和低耦合的情况下,就开始开发了。明白组件该如何封装,才是“万物皆组件”的关键,才能更好的编写和维护Vue项目。
笔者曾经写的组件中,不妨出现下面的情况,也是因为有这些情况,使我的组件复用越来越难,维护起来越来越痛苦
- 组件中包含了API请求
- 组件中直接从请求中获取数据
- 组件依赖某一全局状态,或者依赖某一共享状态
- 组件依赖某业务场景的数据
- 组件承载的逻辑太多,代码数量多达300+
- 组件中混杂着UI逻辑和业务逻辑
- ......
下面,我就总结几点解耦组件的方法和小tips,这些方法有利也有弊,需要根据实际情况来balance,如有不对,欢迎指正~
划分container components
不是所有组件都是一个level的,有些人把组件分为3个层次,分别是容器组件,业务组件,UI组件/无状态组件,也有人简单的划分为智能组件和傻瓜组件,或者叫封装形组件和元组件,或者叫臃肿型组件和简单组件,或者叫容器型组件和展示型组件等,不过不管怎么划分,里面的目的是一样的,那就是为了更好的复用性,为了分离关注点,为了UI逻辑和业务逻辑分离。
那试着为组件划分层级,分为容器型组件和展示型组件,把API的请求,数据的格式化,数据的操作抽离到容器组件中去,让展示型组件功能独立和单一。说白了,也就是要明确一个组件的输入和输出都是什么,还有考虑中间状态如何转化和维护,然后去划分不同的组件level,如下图所示:
References:
ngte-web.gitbook.io/i/react/she…
juejin.cn/post/684490…
使用v-slot
slot支持动态的组合和替换组件,scoped slot支持访问子组件中的数据,有了它俩,可以复用组件的UI逻辑,创建复用程度高,弹性自由的组件。 举个例子,平时常见的列表场景,包括列表头,列表区和翻页区,这三个组件组合到一起,可以实现列表的筛选,列表的展示,列表的加载,列表的翻页等功能,每一部分有自己独立的状态和功能,如果有一天来了个需求,要求layout/style一致,但是请求的接口不同了,或者请求的接口还一致,但是样式有点区别了,该如何实现复用呢?
Reference:markus.oberlehner.net/blog/depend…
以上,通用ListingLayout组件负责列表的基本样式和布局。NavFilter、EntityList和NavPagination组件可以被轻松地重用,因为它们与任何数据获取逻辑完全分离。
provide/inject
这个api主要实现了依赖注入和状态共享。在Vue2中,provide/inject不能提供reactive数据,但是不影响我们用它来做依赖注入,就类似于组件从上下文中获取数据,比如注入一个请求的api共组件使用。
provide/inject的方式可以系统的、跨越层级的传递数据,provide来提供依赖,inject来使用依赖,一定程度上,组件可以面向provide/inject接口编程。
P.S. 笔者之前写过一篇provide/inject的文章,欢迎阅读哦~
继续上面列表的例子,虽然可以复用layout和NavFilter、EntityList、NavPagination组件,但是包裹这些组件的容器组件ProductListingContainer.vue是复用不了的,因为API请求的不同,后续有可能创建一个postListingContainer.vue,再创建一个commentListingContainer.vue,那就多了这么多逻辑相同的container组件,那这该如何优化呢?————使用provide/inject,如下图所示:
既然有使用inject,那必定有地方provide fetch方法,如下:
Reference:markus.oberlehner.net/blog/depend…
这样就实现了依赖注入,且组件是面向接口编程,也就是说你提供给我一个名为fetch的接口就行,我不管你fetch中的逻辑如何实现,我只管调用fetch。那可以根据情况,注册不同的provide给下层使用,但是暴露的接口一致。
vuex
适合把通用的logic,或者与UI逻辑无关的业务逻辑,或者格式化数据的逻辑抽离到vuex中,这样可以适度的抽离一些业务逻辑,并实现这部分业务逻辑的复用。
- 本身与页面的UI数据无关
- 本身与数据逻辑相关
- 本身与UI变化无关
- 本身与业务逻辑相关
- 本身无法获取vm实例,无法操作vm上的方法和属性,也就意味着和当前组件树解耦
vuex中有module的概念,要是一个组件依赖了某一个vuex module,那这个组件和store就形成了强依赖关系,后续要想复用UI,不复用vuex中的logic,那就变得麻烦了,要么重写一个,要么修改组件,要么修改store,要么考虑划分容器组件等,这样对比起来没有使用依赖注入来的简单,易扩展。
plugins
这个与vuex有点类似,那就是与组件形成了紧密耦合,这些组件在理想情况下不应和API强耦合在一起,也不应该与某些导致副作用的功能耦合在一起。不管与vuex不同的是,借助vue的plugin系统,可以访问vm了,意味着可以使用lifecycle hook等和组件息息相关的api了。
那plugins最佳的使用场景是像vue-router,vuex这样的,为vue提供全局的高级功能。
mixin & 高阶组件 & renderless components
这里我就不说这三个了,网上有很多资料,我把我觉得易懂的列出来,大家可以参考~
Why Are Mixins Considered Harmful?
奇技淫巧 - Vue Mixins 高级组件 与 Vue HOC 高阶组件 实践
VUE高阶组件解析
Vue 进阶必学之高阶组件 HOC
Renderless Components in Vue.js
Vue3 composition API
它的出现掀起了一股热浪,堪称Vue组件复用的,逻辑关注点分离的绝绝子~
那这份精彩的内容留给我后续的更新吧,就敬请关注啦~