前言
The most incomprehensible thing about the world is that it is comprehensible.
世界上最不可了解的中央就是它居然是能够了解的。– 阿尔伯特·爱因斯坦
开源(Open-Source)造就了现在凋敝沉闷的软件行业。开源让全世界的开发者都可能协力编写出优良的工具类我的项目,也就是所谓的 “ 轮子 ”,在造福大大小小的公司集体的同时,也能够展示创作者或贡献者的技术实力。现在很多开发者都在大量应用开源我的项目作为本人我的项目的第三方库或依赖,更快更高效的实现开发工作。
笔者也不例外。我最近在用 Vue 3 重构 Crawlab 前端的时候,用到了 Element 团队开发的升级版的 ElementUI,也就是 Vue 3 重构的新 UI 框架 Element Plus。Element 团队在 Element Plus 中将该我的项目用 Vue 3 齐全重构,全面拥抱了 TypeScript;而且相比于之前的 Vue 2 版本丰盛了局部组件;而整体格调和应用形式跟之前的版本统一;一些 API 在应用上还变得更精简了。因而,笔者在重构 Crawlab 前端初期过程中没有遇到太大的阻碍,再加上之前的编写教训,开发过程中显得轻车熟路。然而,好景不长,随着我的项目的一直开发,笔者遭逢到一些技术上的艰难。更精确的说,在实现一些简单性能时遇到了来自于 Element Plus 框架自身的限度。尽管最终千方百计将问题解决了,然而我也粗浅领会到了硬啃(Hacking)开源我的项目源代码的艰难。因而,也心愿借此机会将本人 驾驭开源代码 的教训分享给读者。
本篇文章将以解决 Element Plus 问题的经验开始,循序渐进探讨开源我的项目或开源框架的问题,进一步探讨驾驭开源我的项目源代码的办法和技巧,分享本人浏览、了解和更改源代码的思路。本篇文章次要是方法论的探讨,不波及太多技术细节,任何业余背景的读者都可浏览。
硬啃 Element Plus
框架简介
如果你是应用过 Vue 的前端工程师,你必定据说过 ElementUI。这是一个 UI 框架,也就是说它是帮忙你构建 Web 我的项目的工具类框架,其中蕴含很多罕用的组件(Component)、布局(Layout)以及主题(Theme)等。晚期的 ElementUI 是用 Vue 2 写的,是 Vue 中最受欢迎的 UI 框架,在 Github 上有 49k 标星,是第二名 iView(24k)的两倍多。随着 Vue 作者尤雨溪公布 Vue 3 版本,宣告全面拥抱 TS 之后,原 Element 团队在 Vue 3 的根底上开发了新版本 Element Plus,也就是这个故事的配角。如果对 Vue 3 甚至 Vue 不理解,能够浏览本博客之前的技术文章《TS 加持的 Vue 3,如何帮你轻松构建企业级前端利用》。
初试牛刀
Element Plus 的应用办法跟之前的 Vue 2 版本一样简略易用,文档格调也跟老版本统一,十分全面。依照官网文档激励的,笔者也尝试用组件化的形式来优化之前的 Crawlab 老组件,例如表格(Table)。除了将列表(Column)、数据(Data)、分页(Pagination)等进行简略的封装以外,笔者还心愿退出一些新的实用功能,例如自定义表格的筛选(Filter)和排序(Sort),以及表格可能容许用户自定义(Customize)要展现的列和调整列的程序,等等。尽管 Element Plus 框架有排序和筛选性能,但笔者集体感觉太根底了,通常用户须要既简略又好用的 UI 组件。
下图是筛选表格的前端截图,比拟相似 Excel 的展现和操作形式。
实现这个性能的问题不是十分大,依照官网的文档都能解决。其中次要利用了列组件中的 el-table-column
的 header
插槽(Slot),在其中退出弹出框(Popover)。很好,So far so good!接下来就是退出列的自定义性能了,看上去很快就要功败垂成了嘛。
问题初显
要实现列的自定义,最完满的实现办法就是让用户点击操作按钮,弹出对话框(Dialog),外面能够抉择要展示的列,用拖拽的形式将其排序,而后点击确认。而后我在 Element Plus 官网上疾速找到了一个新组件,穿梭框(Transfer),成果如下图。实践上,只有我在对话框中援用该弹出框来管制展示列的数组就能够了,例如将要展现的列放到 “ 列表 2 ” 中。锵锵!目前看起来仿佛所有十分顺利。
不过,在仔细阅读文档之后我发现该组件并不反对拖拽,因而临时无奈实现列排序的性能。尽管有点丧气,但这个影响不大,咱们次要想实现的是列抉择性能。排序尽管也重要,但临时先搁置一下,先实现组件抉择再说!
然而,事实总是出其不意。笔者在进一步试验中发现,这个组件仿佛存在一个重大 Bug:当穿梭框中全选候选元素时,居然无奈抉择或勾销抉择了(向左挪动或向右挪动)!连忙认真调试查找起因,重复确认是不是我本人写的代码有问题。而后发现是 Vue 在更新组件时在 runtime-core.esm-builder.js
中的 patchBlockChildren
办法下呈现了 oldChildren
为 null
的状况。进一步浏览 Element Plus 中穿梭框组件的相干源代码之后,狐疑是 transfer-panel.vue
这个子组件出了问题,它更新渲染的时候 el-checkbox-group
不存在,因而导致更新时出错。随后我在 Element Plus 的 Github 仓库中提交了 Bug Issue。不过维护者并不认为我反映的问题是 Bug,因为在 CodePen 上仿佛没有问题。但这个 Bug 的确在我的我的项目中客观存在,能够在 crawlab-frontend 的 历史提交 中复现。
再遇窘境
无奈之下,自行推敲半天无果之后,果决决定本人造轮子。很快从头写了一个相似穿梭框的组件进去,组件成果如下图。新写的组件在外观上与之前的十分相似,不过自由度很高,因而也顺便反对了拖拽性能。看上去问题仿佛快要解决了。
不过,当我满怀冀望筹备测试拖拽排序功能时,意想不到的事件产生了:列程序数据扭转之后,表格列居然没有任何扭转!扭转列组件 el-table-column
的程序对界面展现的居然没有作用!而在官网文档的定义中,el-table-column
的程序决定着列的理论展现程序。通过反复屡次试验之后,笔者只好抵赖原先的实现形式有问题。简略的扭转模版中的列组件程序并不会影响界面上的展现。
笔者过后想到一个 “ 聪(yu)明(chun)” 的方法。我试图用强制刷新表格组件的形式让其数据从新渲染,达到扭转列程序的目标。但通过测试,发现这样的暴力形式存在很大的性能问题,每次点击 “Apply” 之后会卡顿近一秒。
于是,笔者又 陷入了失望的地步。
抽丝剥茧
在进退维谷的状况下,笔者停下来认真思考。是不是我对 Element Plus 的框架还不够理解?表格组件自身是如何实现的?它有什么限度和毛病?这些问题都推动我进一步来 拆解 Element Plus 框架自身 ,也就是去 浏览它的源代码 ,了解组件自身的代码逻辑和工作原理。于是,笔者 克隆了 Element Plus 代码仓库 到本地。
十分侥幸,Element Plus 我的项目的代码品质相当高,代码组织构造和命名形式都十分清晰。尽管正文相对来说偏少,但它 清晰的逻辑构造和良好的命名标准让可读性变得很强。在惊叹于大厂工程师职业素养的同时,我也疾速定位到了表格组件的源码地位 packages/table
。整个 Element Plus 我的项目是用 MonoRepo 的形式治理的,简略来说是一个 Git 仓库里有很多个 NPM 我的项目。管理工具应用了 Lerna。上面是 el-table
组件 NPM 我的项目的代码组织构造。
.
├── tests
│ └── table.spec.ts
├── index.ts
├── package.json
└── src
├── config.ts
├── filter-panel.vue
├── h-helper.ts
├── layout-observer.ts
├── store
│ ├── current.ts
│ ├── expand.ts
│ ├── helper.ts
│ ├── index.ts
│ ├── tree.ts
│ └── watcher.ts
├── table
│ ├── style-helper.ts
│ └── utils-helper.ts
├── table-body
│ ├── events-helper.ts
│ ├── index.ts
│ ├── render-helper.ts
│ ├── styles-helper.ts
│ └── table-body.d.ts
├── table-column
│ ├── index.ts
│ ├── render-helper.ts
│ └── watcher-helper.ts
├── table-footer
│ ├── index.ts
│ ├── mapState-helper.ts
│ └── style-helper.ts
├── table-header
│ ├── event-helper.ts
│ ├── index.ts
│ ├── style.helper.ts
│ ├── table-header.d.ts
│ └── utils-helper.ts
├── table-layout.ts
├── table.type.ts
├── table.vue
├── tableColumn.ts
└── util.ts
从这个构造能够看到,整个 el-table
表格组件由几个子组件组成,例如 table-body
、table-column
、table-footer
等。而整个我的项目只有 table.vue
这个一个组件。依据 Vue 开发的教训,笔者很快意识到这就是整个组件的 入口。咱们先去瞅瞅看吧!
整个 table.vue
文件有 469 行代码,算是比拟大的文件,限于篇幅起因就不在这里具体解释了。其中,最重要的发现是一个叫 store
的变量。认真钻研后发现这是由 Vuex 创立的 状态管理器。好家伙!原来表格组件是用 Vuex 来治理数据的啊。这下分明了,只有能搞定 Vuex 的局部,剩下的问题应该就能够迎刃而解了。
话不多说,盘它!
问题解决
我开始一直尝试查找跟列相干的代码,开始 全局搜寻 “columns” 等相似的关键字。在施展了名侦探柯南的洞察能力之后,我逐步留神到 src/store/index.ts
这个文件中的 useStore
办法,这就是整个问题的要害!好了,整个问题起因找到了,关键在于store.states._columns
这个外部状态变量,它是渲染列数据的要害。但它只有初始化或增加删除列的时候才会被赋值,调整 el-table-column
的程序基本不会扭转这个变量!
找到起因,解决办法就简略了。笔者增加了 setColumns
这个 Mutation 办法,以更不便的设置列数组。具体实现过程限于篇幅起因就不详述了。感兴趣的敌人能够看 Crawlab Frontend 的 源码。
上面是残缺的表格自定义列的效果图。功败垂成!
开源框架的利与弊
世界上没有完满的货色,优良开源框架也不例外。而这一次硬啃(Hacking)源码的经验让笔者粗浅领会到这个情理。笔者认为十分有必要探讨对于应用和开发开源框架或开源我的项目的相干问题。首先咱们来看看它的劣势。
劣势
很多出名优良的开源我的项目,例如 Nginx 和 Redis,都成为了软件开发的核心技术。那么,咱们为什么要应用优良的开源框架?笔者认为开源框架次要有以下几个劣势:
- 收费。谁能回绝不要钱的货色呢,况且很多收费的开源框架曾经足够优良了;
- 通明。开源框架的所有源代码都是公开的,任何人都能够看到;
- 可更改。大部分开源我的项目都是自由度很高的 MIT 或 BSD 开源版权,能够按需定制开发;
- 可合作。Github 是最大的开源我的项目平台,寰球的开发者都能够参加迭代开源我的项目;
- 影响力。优良的开源我的项目能够晋升作者或贡献者在行业内的知名度和影响力。
劣势
尽管开源我的项目有不少突出劣势,但在应用开源我的项目的时候却常常会遇到各种各样的问题。笔者认为应用开源框架存在肯定的危险。笔者认为开源框架次要有以下几个劣势:
- 安全隐患。尽管很多优良开源我的项目都由企业或资深专家开发保护,但因为不齐全是本人应用,导致贡献者容易对安全性造成忽略,出名开源我的项目爆出安全漏洞的例子多不胜数,例如 OpenSSL Heartbleed、Fastjson 近程代码破绽、Antd 圣诞彩蛋等;
- 参差不齐。开源我的项目开发者、贡献者和维护者能够是任何人,他们各自的经验和业余背景不同,所以必然导致代码或开源我的项目的品质存在肯定的差别;尽管代码标准(Coding Standard)能够躲避一些问题,但优良的我的项目毕竟是多数,看看托管了几百万我的项目的 NPM 或 Maven 公共仓库吧;
- 学习老本。笔者抵赖有一部分优良开源框架有很成熟和欠缺的文档体系,但大部分还是不足无效的文档教程反对;即便有了详尽的文档,开发者要浏览学习也会投入很多工夫老本;而大部分付费产品则蕴含业余技术支持,能够无效帮忙开发者节省时间;
- 持续性问题。优良程序员能够开发十分高质量的开源我的项目,但因为开源我的项目自身并不带来现金收益,因而很多作者不违心长期投入在开源我的项目下面,导致优良开源我的项目得不到继续的保护和迭代。
- 未知危险。再优良的框架都会存在危险,因为开源框架初期并没有经验太多的理论业务测试,很多问题无奈失去及时修复,因而在应用开源框架的过程中或多或少都会遇到一些出其不意的问题,解决它们会花大量工夫,甚至有些问题还无奈解决。
问题复盘
就笔者后面提到的解决开源框架问题的经验来说,未知危险 和学习老本 尤为突出。笔者在采纳 Element Plus 的穿梭框(Transfer)组件时,想当然的认为它能够无效满足我的需要。然而,没想到的是它既不能反对我打算的列排序需要,又存在重大 Bug 导致无奈应用。这些问题都让我放弃应用该组件,从而寻求从新造轮子的高老本解决方案。
在之后硬啃 Element Plus 表格组件的过程中,我没有放弃应用该组件,而是通过 浏览该组件的源代码,依据逻辑和教训判断出了外围代码的地位以及其可能产生问题的起因 ,并针对性的做了调整,最终顺利的解决了问题。 这次浏览源代码而进行调整的决定,给我节俭了大量的工夫和精力,因为要反复造表格组件这样的简单轮子要做大量的工作,这是十分不划算的。
上图是官网文档其中一个组件的截图。Element Plus 的文档其实曾经十分欠缺了,对于组件的应用办法、例子、API 等都写得很具体,每当开发时遇到不确定的间接去官网文档查看即可。不过,ElementUI 最早是诞生于饿了么,因而其中的组件次要是为了反对饿了么的外卖业务,因而它有肯定的局限性。作为 ElementUI 的 升级版,Element Plus 尽管是同一个团队的产品,但它的性能以及 API 相比于老版本并没有太大的变动。作为比照,诞生于蚂蚁金服的 UI 框架 Ant Design 相对来说就要灵便自在很多,因为它反对的业务线很广,而阿里推广的中台零碎也要求 UI 框架须要设计得很通用。因而,Element Plus 的局限性很可能来自于它的开发团队背景。Crawlab 前端框架抉择 Element Plus 次要是因为老版本是 ElementUI,迁徙老本相对来说比拟低,但因而也带来了一些因框架不成熟导致的问题。甚至,咱们也必须要晓得即便是优良的开源我的项目也会存在这样那样的问题。从这方面来看,理解开源框架自身的长处和毛病,对于如何正确应用来说十分重要。为了让开源框架能为我所用,咱们须要晓得如何正确驾驭开源我的项目的源代码。
接下来,笔者将联合本人的我的项目教训,为读者介绍如何无效驾驭开源我的项目中的源代码。
源代码驾驭技巧
如果一个现代的人穿梭到古代,当他看到各式各样的科技产品时,肯定会惊呼它们为 魔法。然而,他们不晓得的是,这些所谓的 “ 魔法 ” 都是利用科学技术发明进去的,它们背地的原理是通过科学实验总结进去的,并不是什么神秘的巫术。这个情理同样实用于软件行业。
程序员用代码发明了魔法般的互联网社会,这对于非技术人员来说十分神秘。他们晓得那不是真正的魔法,但他们齐全不晓得这些是如何实现的,背地的原理是什么。对于程序员来说,利用一些优良的开源我的项目,能够疾速让他们开发出产品原型、验证产品可行性、甚至开发出生产可用的零碎。不过,如果程序员只是把开源产品当作 工具 ,齐全不了解外面的原理是什么,那就等于是在应用魔法,而你也不晓得它为何会失效。这样的做法会给你带来麻烦。 要正确应用一个开源框架,你必须把握它;要把握它,你必须了解其工作原理;要了解其工作原理,你必须浏览其源代码;要让源代码听命于你,你必须学会如何驾驭它,也就是如何批改和优化源代码。
接下来笔者将介绍几个无效形式来帮忙你驾驭源代码。
克服挫败情绪
读者可能会诧异于这个看上去没什么用的倡议。然而,我看到很多敌人想学习一个开源我的项目时,常常因为我的项目过于简单而中途放弃了,包含很多年前的本人。其中就是挫败感在作怪。我抵赖,要短期了解一个大型开源我的项目是不太事实的。这导致了很多敌人被玲琅满目标技术难点给吓跑了,留下一句 “ 只有会用就行 ”,而后持续搬砖拧螺丝。其实,他们都 太低估本人的后劲 了。笔者并不拥护程序员的实用主义,但要留神过于实用主义会导致功利主义,最终原地踏步的将是本人。
开源框架最棒的一点就是它对来说齐全是 通明 的,任何人都能够看到其中的代码,包含底层实现逻辑、代码组织构造、我的项目部署形式等等。这不是晋升本人驾驭源代码能力的绝佳机会么?通过浏览开源框架源代码,你不仅能够理解其中的工作原理,还能够 学习更好的编程形式 ,从而 晋升本人的职业素养。我真的不提倡 “ 从入门到放弃系列 ” 这句戏言,你至多应该这样暗示本人,“ 我目前看不懂是因为我的基础知识有余,等我晋升根底实力之后肯定能驾驭它 ”。笔者在硬啃完一些简单的开源框架源代码之后,发现浏览源码其实也没那么难。就像锻炼身体一样,撑过这一个山坡、这一个冲刺,之后就十分轻松了,而且身体素质也进步了。
因而,克服挫败情绪 对于驾驭开源我的项目源代码来说十分重要,而且你要暗示本人浏览源码并不难。
本人写开源我的项目
人们不违心浏览源代码的其中一个起因就是不理解。就像你不理解一个心仪的的时候,你会认为她是完美无瑕的女神;但当你真正谋求她,跟她一起约会交换生存的时候,你会发现她不过是一个一般女孩儿。因而,你能够尝试入手开始本人写一个开源我的项目。这对你了解开源我的项目作者的思路会十分有帮忙。这同样不是一个有任何理论操作性的倡议,但如果你真的开始动手做,就肯定会晋升得很快。俗话说:Learn by doing!
当然,要开始写开源我的项目并不是一件容易的事件。你首先可能就会陷入写什么的艰难中。这里笔者举荐之前写的对于开发开源我的项目的两篇文章《如何打造一个上千 Star 的 Github 我的项目》以及《播种人生第一个 5k Star 开源我的项目,经验教训分享给大家》,外面具体解说了笔者开发、保护、推广开源爬虫治理平台 Crawlab 的心得体会,会介绍如何定位痛点、调研用户、推广产品、项目管理等等。有趣味的读者能够深刻浏览。
定位入口文件
终于,这一条是一个略微有点干货的倡议了。在浏览源代码的时候,你必须首先找到该项目标 入口文件(Entry File)。所谓的入口文件,就是这个我的项目模块或零碎裸露给内部零碎的一个公共通道,整个我的项目的运行、调用、执行都是从这个文件开始的。而其余实现具体逻辑的代码就在入口文件中被援用,或者在其余文件中被援用。对于前端我的项目,个别就是 main.ts
;Python 我的项目个别是 app.py
;Golang 我的项目个别就是 main.go
,等等。如果把看似不可驾驭的大型开源我的项目比做战无不克的战神阿喀琉斯,那么入口文件就是他的致命之踵。
其实,不光是浏览开源我的项目,就是咱们浏览工作中其余共事写的代码时,也是先从入口文件开始钻研。把握了入口文件的外部逻辑,你能够看到它援用的其余子模块,而后就能够顺藤摸瓜找到其余外围模块,接着继续下去。当整个我的项目通过入口文件遍历一遍之后,你会更容易了解代码的逻辑构造,从而帮忙你深刻了解具体的工作原理。
在之前解决 Element Plus 表格问题的例子中,笔者定位到了 table.vue
这个入口文件,而后进一步找到了列数据的要害地位,最初轻松的解决了问题。看!驾驭源码其实并不是那么难,对么?
应用全局搜寻
这又是一个技术性倡议。因为大型项目通常来说要拆分成很多文件,因而对于这种状况来看,你如果穷举式的遍历所有文件会花大量的工夫。因而,你能够尝试 全局搜寻可能的关键字,从而疾速定位到外围代码局部。全局搜寻在支流 IDE 中性能十分弱小,通常反对正则表达式、含糊匹配、准确匹配、大小写等等。下图是 JetBrains 旗下 WebStorm IDE 中的全局搜寻后果截图。
对于像 TS 这样的动态类型语言来说,还能够像 Java、C#、Golang 那样查找办法或变量的调用地位,在 WebStorms 里是 “Find Usages”,在其余 IDE 中可能名称不一样,但它们都是一个意思。这些技巧都能无效帮忙你浏览了解源码。
浏览技术文档
好的文档包含我的项目的架构、原理、概念等,能够帮忙开发者疾速了解框架的代码构造。不过,并不是所有开源框架都有具体的文档,这种形式可遇不可求。不过,如果开源我的项目中一旦蕴含了技术文档,请肯定花工夫浏览一下。
当然,你也能够设法分割到开源我的项目作者。NPM 我的项目信息中通常会蕴含作者的邮箱地址;而且如果你看的是 Github 上的开源我的项目,还能够被动向其提 Issue,在 Issue 中表白你的疑难。
总结
本篇文章通过介绍笔者折腾开源 Vue 3 UI 框架 Element Plus 的开发经验,探讨了开源我的项目的劣势和劣势,得出优良开源框架也存在问题的论断。而后,笔者联合本人的我的项目教训提出了 5 个驾驭开源我的项目的技巧和办法,包含克服挫败情绪、本人写开源我的项目、定位入口文件、应用全局搜寻以及浏览技术文档。当初的软件开发离不开优良的开源我的项目,然而咱们也必须意识到开源我的项目并不是专门为你本人服务的,因而它存在肯定的局限性。为了能更好的应用开源框架,让它能兼容你的简单需要,很多状况下你必须仔细阅读它的源代码,这就少不了会产生肯定的学习老本。
咱们通常说程序员要晋升本人的技术实力,要会 “ 写 ” 出好代码;然而,在团队单干越来越重要的趋势下,笔者认为程序员更重要的能力是要会 “ 读 ” 他人的代码,不论好与坏。在编写代码的过程中保障正确正当的命名标准,尽量增加上正文以不便别人了解,这是写代码的能力;跟别人单干时,仔细阅读他人写的代码,尽可能了解他们的思路,思考实现形式是否正确,等等,这是读代码的能力。当然,写和读只是十分根底的局部,驾驭代码的能力还不止 “ 写 ” 和 “ 读 ”,应该还包含思维、逻辑、布局等能力,例如零碎架构、算法原理、可扩展性设计等等。
因而,当咱们在埋怨共事代码写得差的时候,千万别烦恼或怄气,因为那可能是因为你浏览代码的能力不够,没有了解分明共事的思路;同样,可能在共事看来,咱们的代码也蹩脚透了;或者,这是传说中的五十步笑百步?
社区
如果您对笔者的文章感兴趣,能够加笔者微信 tikazyq1 并注明 “ 码之道 ”,笔者会将你拉入 “ 码之道 ” 交换群。