vue中拆分组件的实战案例

目录
  • 一、组件化诞生的历史
  • 二、为什么业务组件越开发越难维护
    • 人的问题
    • 技术问题
    • 2.1 项目现状
    • 2.2 理想目标
  • 三、举一个实际的例子
    • 3.1 需求背景
    • 3.2 开发之前: 前端设计文档
      • 数据流向图
      • 目录结构
      • 逻辑控制
      • 拆分的原则
    • 3.3 受控组件和非受控组件
    • 3.4 开发进行: 逻辑变量和UI变量
  • 四、持续的优化
  • 五、可能的问题
  • 五、实践是学习前端的捷径
  • 总结

组件化是一种思维的表现,这种技能映射到人的本质是,一个人是否有能力把一个复杂的问题拆解、简单化的能力。

一、组件化诞生的历史

我们在讨论如何拆分组件之前,是有必要简单的了解一下组件化诞生的一个历史。

前端娱乐圈有一个独有的生态:框架。每年出现的框架层出不穷,根本学不完。但是总的来说还是可以分成两个阶段。

第一阶段: JQ和PrototypeJS。 该阶段解决了浏览器的兼容性问题以及API的遍历程度

第二阶段: Vue、React、Angular。解决了组件化、解耦、复用等问题

在大陆,主要讨论的是Vue和React。 有些人说Vue是framework,而React是library,前者有更多的约束和更加齐全的工具链。而后者更加的自由。但是真的要投入生产的话,依旧需求认为的给React添加很多的约束,而且Vue也是支持jsx的,所以我一直不太赞同React更加自由这样的说话。

在我看来,它们在实际生产开发过程中,在那一堆工具链中,只是API的不同而已

它们都为前端提供了很好的组件化。而且近一年来两者都不约而同朝着函数式跟进。它们带来的各种hook,给我们带来了不一致的组件化的写法。

二、为什么业务组件越开发越难维护

人的问题

当然是人的问题. 或许产品的问题,或许整个工作流程的问题,或许上面的问题. 这些我们暂且不提,我作为开发, 首先是要管好自己的代码组织.

再次我们先排除其他外界的因素,比如产品经常改需求. 仅从编码阶段来说.

以我们团队为例,我们团队内部员工2个,8个外包,外包兄弟们的招聘标准是远低于内部的。团队人员每个人的编码能力差距还是很大的。项目都是长期维护的,一个业务模块就会有很多人维护,在上面不断的填尿加屎。

在这里并不是说外包人员的编码能力差,我们组就有一个外包的兄弟编码能力、解决问题的能力相当厉害的,比很多内部的都好很多。这里只是从平局值上面来说。

团队成员的水平参差不齐, 顾及到团队协作, 我们在拆分组件的时候需要更加的简单和清晰.

技术问题

业务逻辑和交互逻辑的纠缠不清

2.1 项目现状

以该图为例, A B C 分别是父子孙组件. 当我们要控制其中一个组件的状态的是, 可以通过很多方式来进行控制. 这些方式的来源有可能是全局变量vuex时间总线来自自己父组件或子组件的改变等等.

可以看出, 改变它组件内部状态的来源非常的多, 维护或者修改的时候,需要翻阅的文件目录和范围就很广. 自然就很难维护.

举一个mixins的例子:

假设它混入了这么多功能。

export default {
  mixins: [ a, b, c, d, e, f, g],
  mounted() {
    console.log(this.whoAreYou)
  }
}

这个this.whoAreYou你能够知道来源于哪一个么? 而如果改成hook的写法来引入某个JS中的变量:

const { IamI } = myHome()

const { IamI as me } = myHome()

这就很简洁干净。在你维护代码的时候,可以很好的进行溯源。 而上面的一切,导致难以维护的原因总结来说有两个:

  • 混用业务变量和UI变量
  • 不区分受控组件和非受控组件

下面我会实际例子分别介绍这个两个概念。而基于hooks的复用才是我们现在解决组件化复用的更好的选择。

2.2 理想目标

基于hook的理想模型

依旧是A B C 三个组件.但是A B C三个组件外边飘的那些箭头不存在了. 所有能够控制它们的内部状态的方式都集中 在了controllers上面.

其中controllers部分的组织形式和vue的composition api宣传图表现一致。

将相似的功能以及用到的变量都封装在一个函数当中。这一切也更加好的迎合了

实际代码如下:

<template>
  <div>
    <B setC={setC} />
  <div>
</template>
<script setup>
  import B from 'B.js'
  import cController from 'cController.js'

  const { setC } = cController(props)
</script>

// cController.js
export default c(props) {
  const c = ref('')
  const setC() {
    c.value = 'I an cController'
  }

  return {
    c,
    setC,
  }
}

cController.js就是controllers中的一个void. 引入到A组件当中,然后将里面的方法通过props传给B组件.

<template>
  <div>
    <C setC={setC} />
  <div>
</template>
<script setup>
	import C from 'C.js'

  props: {
  	setC: {
      type: Number,
    }
  }
</script>

也就是说,控制C组件内部状态的是通过引入到A组件中的controller来进行通过,中间的B组件不做任何的处理,仅仅作为一个中转站. 操作起来和理论都很简单。但是想要更好的拆分的话,还需要了解三个概念:

  • 业务变脸和UI变量
  • 受控组件和非受控组件
  • 控制反转ioc

下面我通过一个实际的业务场景来描述。

三、举一个实际的例子

3.1 需求背景

简单的截两张图. 需求大致如下:

  • 功能就是典型的笔记软件的功能,右边可以放各种类型的文件,点击就可以在右边渲染出对应的内容.
  • 目录树有两个彩蛋,会根据当前文件类型出现不同的操作
  • 目录树下面有一个固定的收藏夹,目录树可以在这其中滚动

3.2 开发之前: 前端设计文档

数据流向图

功能还是很清楚的,但是功能其实很多. 我认为我们团队在开发之前是必须要有的. 作为一个前端, 可以没有流程图,但是一定要下面这样的图. 我在别的地方没有见过这样的图,所以自己给这样的图做了一个定义,叫数据流向图.

关于完整的工作流程,之后再写一篇文章进行描述

它是有两部分构成:

  • 组件的模块
  • 组件之间的控制关系

第一点, 还是比较清楚,就是这个需求可以拆成哪几个模块.

第二点, tree组件和content组件是同级组件, tree可以控制content组件内的状态, content组件也可以改变tree内的状态. 再深入一点说,就是tree点击不同的文件类型, content组件部分就会渲染不同的模块; 而当在content组件内对当前阅读的文件进行删除操作的时候,tree作为目录树自然是要刷新最新的目录信息的.

目录结构

通过上面的结构图,可以得到下面这样目录结构.

逻辑控制

数据流向图中的各个组件都放在根目录下index.vue中挂载. 如下入

控制目录树的相关逻辑都放在listTreeController控制器里边, 和右边内容content相关逻辑都放入到renderContentController的方法当中.

随后将controller中公共方法都传进到组件当中. doc-aside是包括searchtree已经other三个模块的中转组件. 不在这个组件中做任何的逻辑处理. 如下图:

举一个例子, 控制按钮的权限. [背景]

  • 所有功能点都受控挂载在vuex的store上面的一个变量, 没有权限的话,就直接通过v-if来隐藏对应的入口

[之前实现]

  • 直接找到对应的按钮在v-if上,通过root.docAuth('createDoc')来判断

[修改之后]

  • 创建来一个authoControllers.jsindex.vue引入, 需要用的地方是应用的是

[具体实现]

export default function authController({ root }) {
  const menuAuth = {
    [MENTY_TYPE.rename]: root.docAuth('rename'),
    [MENTY_TYPE.delete]: root.docAuth('delete')
    // ....
  }
}

虽然在Index.vue中引入,不管是通过props,还是通过依赖注入来给子组件来使用,都不重要.重要的是,它统一管理, 并在index.vue中引入是唯一一个入口. 当我们维护的时候, 只需要通过子组件一路找到对应的controller就可以找到对应的逻辑了.

拆分的原则

  • 对于组件的拆分一开始不需要太细
  • 拆分好受控组件和非受控组件

3.3 受控组件和非受控组件

我们使用的任何UI框架都是受控组件, 受控组件的概念就是它里面的状态都是受调用它的组件来控制的. 非受控组件反之.

3.4 开发进行: 逻辑变量和UI变量

UI变量其实很好理解. 像element-ui的组件中所需要的属性就是UI变量. 但是对于我们实际业务当中, 会对这些进行一定扩展.

举一个例子, 在上面的目录中dialog组件的显示或隐藏,是通过model-value / v-model来进行控制的, true就显示, false就隐藏起来.

隐藏和显示的渐入渐出效果是elementUI框架内置的.

平时工作中很多人是这样传的:

<el-dialog :v-model="data.id === XXXX">
  // code
</el-dialog>
props = {
  data: {
    type: Object
  }
}

通过通过接口拿到的,或者自己组件的数据传进来之后,再进行对v-model的控制. data.id这样的变量就是业务变量, 通过业务变量来直接控制UI的组件的显示和隐藏,就是业务变量和UI变量的混用. 或者说**业务逻辑和交互逻辑的混用. **

混用之后的后果,就是我们进行维护的时候, 需要查看的变量或者说字段就成倍的增加, 交互变量和业务变量交织在一起. 这部分的代码同时承载了业务逻辑和交互逻辑.

DDD领域模型也是可以解决这个问题, 之后我会再开篇幅聊一聊.

所以我们就需要将业务逻辑和交互逻辑给拆开. 如下:

<template>
  <el-dialog :v-model="isShow">
    <template slot="header">
      {{ dialogTitle }}
    </template>
    <template slot="content">
      // type === 创建表单
      // type === 移动文件夹目录
    </template>
  </el-dialog>
</temaplte>
props = {
  isShow: {
    type : Boolean,
    desc: '是否显示弹窗'
  },
  type: {
    type: String,
    desc: '弹窗的类型'
  }
}

其中ishowtype 就可以视为UI变量, 它们不关心外界是通过了什么判断, 只关系传进来的是true还是false.

四、持续的优化

不管一开始代码是如何规划的,如何组织的.最重要的还是要持续的去维护. 屎山到了之后, 前面的维护者没有一个人是无辜的. 但是也不需要过早的去维护.什么时候到了维护重构的时机呢?

  • 当碰到这里用的代码别的地方也用到的时候
  • 这个变量出现在好几个地方,被好几个地方都set的时候, 而自己搞不懂它们set的顺序的时候
  • 函数复杂到自己看了半天都看不明白的时候

五、可能的问题

问题一: 中转的组件没有挂载任何逻辑,为什么还存在?

  • 为了之后可能的拆分
  • 让结构更加的清晰

问题二: 中转的组件要挂载这么多办法, 或许太难看?

  • 实在是太多可以使用vue的$attr$listeners
  • 为了维护对于数据的溯源

五、实践是学习前端的捷径

前端是一门手艺活,只有实践才能够提高技术. 前端的天花板确实相比其他方向的低,但是也不是我这样的普通人说能够触碰就能触碰到的. 就算很多高端大佬嗤之以鼻的业务代码, 写的时候如果不多思考如何写的简洁,怎么写优雅,写十年和写三年也是没有差别的.

业务才能创造价值, 有了价值才能有我们前端工程师生存的空间. 所以为了提升自己的价值, 提升自己的工资. 平时写业务代码的时候,想想这样写会有什么问题, 如何写才能够更加好. 在这个基础上, 才能看明白那些框架存在的意义. 业务是在轮子之上的,如果对业务的代码都不理解, 又怎么能够真正的写好轮子呢?

所以我们在保障业务按时完成的情况下,应该多尝试,多实践.

总结

到此这篇关于vue中拆分组件的文章就介绍到这了,更多相关vue拆分组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue管理系统前端之组件拆分封装详解

    组件封装 在上一篇记录中,首页中有太多的代码,为了避免代码的臃肿,需要对主要的功能模块拆分,来让代码看起来更简洁,且能进行复用. 拆分后还加了些小功能,加入了修改 title 的代码,修改方式参考vue 动态修改 title. 还增加了当前请求的页面缓存,使用状态管理器处理.监听路由,保存到 state 中,来处理的. 如何监听可参考vue 计算属性和监听属性. 完整效果图如下: 首页布局拆分后结构 拆分后的,布局结构图: 拆分后代码 布局最外层 index 代码,使用头部,侧边栏,主内容栏组成

  • vue中拆分组件的实战案例

    目录 一.组件化诞生的历史 二.为什么业务组件越开发越难维护 人的问题 技术问题 2.1 项目现状 2.2 理想目标 三.举一个实际的例子 3.1 需求背景 3.2 开发之前: 前端设计文档 数据流向图 目录结构 逻辑控制 拆分的原则 3.3 受控组件和非受控组件 3.4 开发进行: 逻辑变量和UI变量 四.持续的优化 五.可能的问题 五.实践是学习前端的捷径 总结 组件化是一种思维的表现,这种技能映射到人的本质是,一个人是否有能力把一个复杂的问题拆解.简单化的能力. 一.组件化诞生的历史 我们

  • Vue中UI组件库之Vuex与虚拟服务器初识

    一.日历组件 new Date()的月份是从0开始的. 下面表达式是:2018年6月1日 new Date(2018, 5, 1); 下面表达式是:2018年5月1日 new Date(2018, 4, 1); 或 new Date(2018, 5-1, 1); 下面表达式是:2018年5月31日(得到上个月的最后一天) new Date(2018, 5 , 0); 日的参数可以是0,也可以是负数,表示上个月底的那一天. 下面表达式是:2018年7月01日 new Date(2018, 5, 3

  • 浅析Vue中拆分视图层代码的5点建议

    一.框架的定位 框架通常只是一种设计模式的实现,它并不意味着你可以在开发中避免所有分层设计工作. SPA 框架几乎都是基于 MVC 或 MVVM 设计模式而建立起来的,这些模式都只是宏观的分层设计,当代码量开始随着项目增大而增多时,问题就会越来越多.许多企业内部的项目仍然在使用 angularjs1.X ,你会发现许多 controller 的体积大到令人发指,稍有经验的团队会利用好 angularjs1 构建的 controller , service , filter 以及路由和消息机制来完

  • Vue中父子组件通讯之todolist组件功能开发

    一.todolist功能开发 <div id="root"> <div> <input type="text" v-model="inputValue"> <button @click="handleSubmit">提交</button> </div> <ul> <li v-for="(item, index ) of list

  • vue中keep-alive组件的用法示例

    问题描述(什么是keep-alive) keep-alive顾名思义,保持活跃.保持谁活跃呢? 首先我们知道,因为vue就是组件化编程,一个.vue文件就是一个组件.就像万事万物一样,都有从出生到消亡的生命周期过程,vue的组件也是一样,也有自己的生命周期,比如create创建组件.mounted往组件上挂数据.update更新组件上挂的数据,destroy把组件实例销毁. 所以使用keep-alive就是保持组件活跃,不会被destroy销毁掉,就一直还活着,组件没有被销毁掉的话,组件上挂载的

  • Vue中router-link常用属性使用案例讲解

    目录 Vue中router-link常用属性使用 router-link属性 vue中的router-link属性详解 router-link标签 router-link属性值详解 Vue中router-link常用属性使用 在vue1.0版本的超链接标签还是原来的a标签,链接地址由v-link属性控制而vue2.0版本里超链接标签由a标签被替换成了router-link标签,但最终在页面还是会被渲染成a标签的至于为什么要把a换成router-link原因还是有的,比如我们之前一直惯用的nav导

  • Vue中父组件向子组件通信的方法

    Vue是一个轻量级的渐进式框架,对于它的一些特性和优点在此就不做赘述.下面通过本文给大家分享Vue中父组件向子组件通信的方法,具体内容详情如下所示: props 组件实例的作用域是孤立的.子组件的模板中是无法直接调用父组件的数据. 可以使用props将父组件的数据传给子组件.子组件在接受数据时要显示声明props 看下面的例子 <div id="app"> <panda here='China'></panda> </div> <s

  • vue中各组件之间传递数据的方法示例

    前言 本文主要给大家介绍了关于vue组件之间传递数据的相关资料,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 作用域 在vue中,组件实例的作用域是孤立的,父组件模板的内容在父组件作用域内编译:子组件模板的内容在子组件作用域内编译.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据. 下面几种方法可以实现组件之间数据的传递. 一.通过prop传递数据 1)在子组件中,使用prop属性,显示的表明,它所需要的数据. 2)在父组件中,需要引用子组件的地方,传入数据.

  • vue中component组件的props使用详解

    本文介绍了 vue中component组件的props使用详解,分享给大家,具体如下: props使用方法 Vue.component('my-component',{ props:['message'], template:'<div class="tem1">{{message}}</div>' }); <my-component message="hello"></my-component> 注意:props 的

  • vue中element组件样式修改无效的解决方法

    如下所示: <style> .detail{ .el-input__inner { height: 48px; } } </style> 直接写style注意不加scoped,然后用一个组件最外层的class包裹住,就不会改到所有的组件的样式了. 以上这篇vue中element组件样式修改无效的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: Vue 组件间的样式冲突污染 浅谈vue中改elementUI默认样式引发的st

随机推荐