探讨前端组件化

2019-11-16 19:44:42 +08:00
 hfcc8685

一. 现状

现在我接手一个项目”好货网”, 该项目已经上线两年, 我的 title 是前端技术负责人, 团队现有三名前端工程师, 其中一人刚入职.
项目用原生 JS+CSS 编写, 没有使用任何框架, 整个网站共有 10 个一级页面, 20 个子页面, 页面风格一致.
当有新需求时, 开发人员会新建一个页面, 如果新页面的部分模块和已有页面雷同, 会拷贝代码到新页面中, 基本不考虑复用和抽取.

二. 问题

  1. 新页面开发周期长, 产品经理痛苦不堪
  2. 页面难以维护, 比如有一个模块是商品模块, 当商品结构调整时,所有的商品信息展示都要更改, 开发和测试要对 15 个页面进行调整和测试
  3. 页面可读性差, 因为已经换了很多工程师, 每个人的风格可能都不一样, 导致代码风格千奇百怪
  4. 重复代码非常多, 而且同一种业务逻辑会有多种实现方式, 但是效果是一样的
  5. 不敢重构, 动一发牵全身

三. 目标

为了解决上述五个问题, 我决定开展一个技术项目, 即实现项目组件化

四. 方案

4.1 框架的选择 有两种方案可以选择, 一种是原生 JS 组件化, 另一种是引入 react,vue 等框架. 考虑到团队成员的水平, react 和 vue 基本没用过, 这样会有学习成本, 而且这些框架入门容易,用好难, 考虑到团队成员的 js 基础还不错, 保险起见, 决定采用 js 实现组件化.

4.2 制定方案 分析所有页面后发现, 30 个页面的风格一致, 按钮,下拉框,弹出层等都一致,商品展示模块 ui 一致, 用户信息模块 ui 一致, 所以打算分为基础组件和业务组件两部分进行封装, 为了保证线上功能稳定性和迭代的正常开发, 按如下步骤完成最终的上线:

  1. 跟 UI 和产品沟通, 确认网站风格, UI 设计基础组件和业务组件
  2. 封装基础组件
  3. 替换线上页面的基础组件, 一个个上线, 保证稳定性
  4. 封装业务组件
  5. 替换页面的业务组件, 一个个页面上线, 保证稳定性
  6. 重构页面, 一个个页面上线

4.3 组件的实现过程 组件分为两大类, 一类是基础组件, 一类是业务组件, 业务组件使用基础组件+业务逻辑进行封装, 除了一些简单的基础组件,比如 button 等, 其他组件都继承一个组件基类 Component

4.3.1 基类 Component 的实现 1. Component 有三个最重要的生命周期: init: 组件实例被创建, 我们在这里去创建一个 div mounted: 在 div 挂在到 dom 树上, 该方法会触发, 供子类去重写 unmounted: 把 div 移除 dom 树 2. 提供一个 attributes 数组, 用于存放组件的属性值, 比如 style 属性,data 属性, 由使用者传递进来 3. 组件之间需要互相挂载, 所以提供两个方法 appendTo 和 appendChild. 考虑到大多数组件都是被挂载到 Component 上, 提供一个 render 方法供重写 4. 很多时候, 组件的逻辑很复杂, 不同状态下展示不同的 div,这时候需要有状态去标识, 所以我们可以封装一个 states 数组到 components 中 5. Component 要暴露出去 div 的事件供子类设置, 封装 addEventListener, removeEventListener, triggerEvent 方法, 子类可重写

4.3.2 基础组件的实现 基础组件包括 TabContainer, ScrollContainer 组件等, 带有业务特性, 需继承 Component 并按组件要求重写某些方法, 举例 1. TabContainer 组件会重写 render 实现 tab 头的切换效果. 2. ScrollContainer 组件的最外层 div 要设置为可滚动, 一种方法是每次使用 ScrollContainer 时传入 style 属性, 但对 ScrollContainer 来说这种属性是固定的, 应该封装到 ScrollContainer 更合适, 所以我采用重写 init 方法,里边(先调用super.init())

4.3.3 业务组件的实现 业务组件继承 Component, 可以使用基础组件, 一般是用于通用业务模块的抽取, 比如商品模块,ui 和交互都一样,就抽取出来, 商品信息作为属性传递进来, 根据需求去展示商品信息, 内部交互自己处理,有的交互需要通知父组件时通过事件向外传递

4.3.4 页面 页面由各个业务组件+基础组件+业务逻辑封装, 页面一方面负责取服务端数据, 并把部分数据传递到业务组件中, 另一方面需要处理子组件之间的交互

4.3.5 过程中的点 1. 应该会有不少人在子类中直接调用 component 创建的 div, 这种是否合理呢? 我的想法是不太合理, div 应该由 component 自己维护, 但是开发的过程中发现有的子类真的需要调用,比如 scrollContainer 需要知道 div 的高度, 所以在 Component 中提供了一个 get 方法供子类调用, 纠结… 其实也可以考虑不能被子类调用, scrollContainer 自己定义一个最大的 div, 挂载到 component 的 div 上,然后 scrollContainer 取自己的 div 高度 2. 如果有埋点的需求, 比如大部分组件的 mounted 后都需要发送后端埋点, 可以考虑封装到 component 的 mounted 中, 提供一个 attribute 作为是否发送埋点的开关, 控制 mounted 里要不要加载 3. 子类组件封装自己能提供的事件, 对于使用者来说只需要知道事件的名称, 然后把事件函数传入. 比如 scrollContainer 在滚动条触底时要对外抛出一个事件, 不具备通用性, 需封装到 scrollContainer 中, 所以它需要提供一个 scrollEnd 事件. 一开始我直接调用this.container.addEventListener(‘scroll’)去做的, 但是思考了一下觉得应该通过重写 addEventListener 方法去实现比较合理

4.4 组件化的工具 组件化的过程中, 会发现很多问题, 比如:

  1. css 样式都写在 js 中, css 维护起来非常不方便, 而且代码可读性变差. 解决方式有很多种, 比如 npm 去引入 css-loader, 让 css 文件可以 import 到 js 中
  2. 用 js 代码去生成 dom 树比较繁琐, 不够清晰和直观, 可以引入 JSX 的写法, npm 上找现成儿的, 或者可以自己去实现解析 jsx

4.5 保证项目可维护性的其他措施

  1. 项目大了, 无论新老员工都需要知道项目中目前有哪些组件, 省的去找代码看, 开源的 storyBook 非常好, 可以很方便安装到项目中, 项目启动后, 在浏览器中就能看到目前项目的所有组件了.
  2. 确定一种好的代码风格, 通过 codeReview 的方式让团队的代码风格趋于一致,大家也能互相学习各自的好的编程风格, 提高个人的编程能力
  3. 我觉得不停的重构是保证团队质量的唯一方式, 而如何保证重构又能完成迭代任务呢, 搭建自动化测试是一种好的方式

五. 结果

这是个计划, 我还没做完 4.3.2, 没有结果, 中间肯定还有很多坑…. , 这是个漫长的过程, 但是会有完成的那天的, 毕竟时间如白驹过隙, 岁月又曾绕过谁呢

5877 次点击
所在节点    JavaScript
36 条回复
learningman
2019-11-17 03:58:01 +08:00
纯手写。。。我初中那会儿可能会这么写吧
seki
2019-11-17 05:08:18 +08:00
我觉得 9102 年的前端负责人如果还感觉不到 mvvm 框架相对于原生 js 在工程方面的优势的话,那还是挺可怕的
wunonglin
2019-11-17 07:34:55 +08:00
楼主牛逼,楼主万岁
yesvods
2019-11-17 13:27:47 +08:00
上百人团队都不敢这么干,太闲了吧,开头一股劲,维护火葬场,后面又是一堆坑。
a852695
2019-11-17 13:49:54 +08:00
讲真 还好我没在这种坑爹公司。。。什么年代了还在用原生 js 搞,用 Vue React 组件库哪里不爽了呢,前后端分离节约的事件去约妹哪里不好了
g0thic
2019-11-17 15:04:15 +08:00
jQuery 也没用么 赖是真的牛逼
connection
2019-11-17 20:51:33 +08:00
业务代码原本是 shit。你怎么可能期望换个框架就不是 shit 了。。

比较正常的方法,是基于你现有的 沉淀出可用组件。将现有的可复用抽离。
shit 就让它 shit 吧。

并不是很推荐基类。
记得以前接过一个老的 react 项目,也是基于基类+mixin 的方法,后面维护起来 根本没人敢动基类内容。维护性并不高。
angel001ma
2019-11-17 23:14:18 +08:00
支持#1
Solael
2019-11-18 01:12:23 +08:00
对团队水平没信心还要重新造轮子,自求多福
cuzfinal
2019-11-18 08:28:41 +08:00
重新招点会 vue 的人比较容易。
MikeFeng
2019-11-18 10:02:36 +08:00
花个几天学 Vue 比你花十几天写自己的原生 js 框架效率不是更高?你自己写的框架有 Vue 的三分之二稳?
Chenamy2017
2019-11-18 12:52:34 +08:00
4.1 你再考虑一下。本人 C 程序员一个,很快就能上手 vue,何况你们是前端出生,这是要给前端抹黑吗?
cocolate
2019-11-18 13:45:30 +08:00
纯 js 写成这样就别折腾了吧
你的需求已经算半个框架了,不用现成的框架是何苦呢
gaigechunfeng
2019-11-18 14:07:54 +08:00
我一个 android 底层开发,写 C 的,都学会 vue 了。你们 js 基础那么好,一周的时间也掌握了 vue 了吧。
react 也行。
感觉这么写造轮子,又是一个坑
charlie21
2019-12-22 20:49:36 +08:00
@seki mvvm 框架的劣势 开销 在哪里,或者说 它 在什么情况下应该是最优选项,纠结的人就会纠结这个。其实和技术无关,仅仅是 纠结的人对什么都纠结,逮着个机会了,就要好好纠结一番。好事
wanguorui123
2019-12-27 16:06:34 +08:00
楼主手撸框架也是勇气可佳,有组件化意识是好事,楼主时间多的话可以先研究下已有框架或者传统 JQ、Ext、YUI 的组件化方案。开始坑肯定比较多的,只要毅力够,一个个钉子都会被躺平。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/620247

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX