Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2273e72f6 | ||
|
|
386b605c32 | ||
|
|
b60cca25d7 | ||
|
|
68e6c23f82 | ||
|
|
a9d2c1d47a |
@@ -149,9 +149,9 @@ Fiber 利用分片的思想,把一个耗时长的任务分成很多小片,
|
||||
因此,在组件更新时有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。所以 React Fiber 把一个更新过程分为两个阶段:
|
||||
|
||||
- 第一个阶段 Reconciliation Phase,Fiber 会找出需要更新的 DOM,这个阶段是可以被打断的;
|
||||
- 第二个阶段 Commit Phase,是无法别打断,完成 DOM 的更新并展示;
|
||||
- 第二个阶段 Commit Phase,是无法被打断的,完成 DOM 的更新并展示;
|
||||
|
||||
在使用 Fiber 后,需要要检查与第一阶段相关的生命周期函数,避免逻辑的多次或重复调用:
|
||||
在使用 Fiber 后,需要检查与第一阶段相关的生命周期函数,避免逻辑的多次或重复调用:
|
||||
|
||||
- componentWillMount
|
||||
- componentWillReceiveProps
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# 1. 引言
|
||||
|
||||
前端展望的文章越来越不好写了,随着前端发展的深入,需要拥有非常宽广的视野与格局才能看清前端的未来。
|
||||
|
||||
笔者根据自身经验,结合下面几篇文章发表一些总结与感悟:
|
||||
|
||||
- [A Look at JavaScript’s Future](https://www.toptal.com/javascript/predicting-javascript-future)
|
||||
- [前端开发 20 年变迁史](https://mp.weixin.qq.com/s/yNg7Q0XNLJMnqffTIJhNUg)
|
||||
- [前端开发编程语言的过去、现在和未来](https://johnhax.net/2019/fe-lang/article1)
|
||||
- [绕过技术纷争,哪些技术决定前端开发者的未来?](https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247491704&idx=1&sn=95ad66f7fe606801cdac74e296a41783)
|
||||
- [未来前端的机会在哪里?](https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247490769&idx=1&sn=7ee6e01045a6fe7e15f16aa33afcc2ad&chksm=e92921dede5ea8c8e93489271e8877d2e8688bd511b32e22c287b6c468904c5466b40f6a2bec&xtrack=1&scene=90&subscene=93&sessionid=1562200039&clicktime=1562)
|
||||
|
||||
读完这几篇文章可以发现,即便是最资深的前端从业者,每个人看前端未来也有不同的侧重点。这倒不是因为视野的局限,而是现在前端领域太多了,专精其中某几个领域就足够了,适量比全面更好。
|
||||
|
||||
同时前端底层也在逐渐封闭,虽然目睹了前端几十年变迁的开发者仍会对一些底层知识津津乐道,但通往底层的大门已经一扇扇逐渐关闭了,将更多的开发者挤到上层区域建设,所以仅学会近几年的前端知识依然能找到不错的工作。
|
||||
|
||||
然而上层建设是不封顶的,有人看到了山,有人看到了星球,不同业务环境,不同视野的人看到的东西都不同。
|
||||
|
||||
有意思是的国内和国外看到前端未来的视角也不同:国内看到的是追求更多的参与感、影响力,国外看到的是对新特性的持续跟进。
|
||||
|
||||
# 2. 精读
|
||||
|
||||
前端可以从多个角度理解,比如规范、框架、语言、社区、场景以及整条研发链路。
|
||||
|
||||
看待前端未来的角度随着视野不同也会有变化,比如 Serverless 是未来,务实的思考是:前端在 Serverless 研发链路中仅处于使用方,并不会因为用了 Serverless 而提升了技术含量。更高格局的思考是:怎么推动 Serverless 的建设,不把自己局限在前端。
|
||||
|
||||
所以当我们读到不同的人对前端理解的时候,有人站在一线前端研发的角度,有人站在全栈的角度,也有人站在业务负责人的角度。其实国内前端发展也到了这个阶段,老一辈的前端开拓者们已经进入不同的业务领域,承担着更多不同的职能分工,甚至是整个大业务线的领导者,这说明两点:
|
||||
|
||||
1. 前辈已经用行动指出了前端突破天花板的各种方向。
|
||||
2. 同是前端未来展望,不同的文章侧重的格局不同,两个标题相同的文章内容可能大相径庭。
|
||||
|
||||
笔者顺着这些文章分析角度,发表一些自己的看法。
|
||||
|
||||
## 框架
|
||||
|
||||
在前端早期,也就是 1990 年浏览器诞生的时候,JS 没有良好的设计,浏览器也没有全面的实现,框架还没出来,浏览器之间就打起来了。
|
||||
|
||||
这也给前端发展定了一个基调:凭实力说话。
|
||||
|
||||
后面诞生的 Prototype、jquery 都是为了解决时代问题而诞生的,所以有种时代造就前端框架的感觉。
|
||||
|
||||
但到了最近几年,React、Angular、Vue 大有前端框架引领新时代的势头,前端要做的不再是填坑,而是模式创新。国内出现的小程序浪潮是个意料之外的现象,虽然群雄割据为开发者适配带来了一定成本,但本质上是中国在前端底层领域争取话语权的行为,而之所以各大公司不约而同的推出自己的小程序,则是商业、经济发展到了这个阶段的自然产物。
|
||||
|
||||
在原生开发领域,像 RN、Flutter 也是比较靠谱的移动端开发框架,RN 就长在 React 上,而 Flutter 的声明式 UI 也借鉴了前端框架的思路。每个框架都想往其他框架的领域渗透,所以标准总是很相近,各自的特色并没有宣传的那么明显,这个阶段只选用一种框架是明智的选择,未来这些框架之间会有更多使用场景争夺,但更多的是融合,推动新的开发方式提高生产力。
|
||||
|
||||
在数据驱动 UI 的方式上,具有代表性的是 React 的 Immutable 模式与 Vue 的 MVVM 观察者模式,前者模式虽然新颖,但是符合 JS 语言自然运行机制,Vue 的 MVVM 模式也相当好,特别是 Vue3.0 的 API 巧妙的解决了 React Hooks 无法解决的难题。如果 Vue 继续保持蓬勃的发展势头,未来前端 MVVM 模式甚至可能标准化,那么 Vue 是作为标准化的事实规范,还是和 JQuery 一样的命运,还需观察。
|
||||
|
||||
## 语言
|
||||
|
||||
JS 语言本身有满多缺陷的,但通过 babel 前端工程师可以提前享受到大部分新特性,这在很大程度上抵消了早期语言设计带来的问题。
|
||||
|
||||
横向对比来看,我们还可以把编程语言分为:前端语言、后端语言、能编译到 JS 的语言。
|
||||
|
||||
之所以有 “能编译到 JS 的语言” 这一类,是因为 JS Runtime 几乎是前端跨平台的通用标准,能编译到 JS 就代表了可跨平台,然而现在 “能编译到 JS 的语言” 除了紧贴 JS 做类型增强的 TS 外,其他并没有火起来,有工具链生态不匹配的原因,也有各大公司之间利益争夺的原因。
|
||||
|
||||
后端语言越来越贴场景化,比如 Go 主打轻量级高并发方案,Python 以其易用性占领了大部分大数据、人工智能的运算场景。
|
||||
|
||||
与此对应的是前端语言的同质化,前端语言绑定在前端框架的趋势越来越明显,比如 IOS 平台只能用 OC 和 Swift,安卓只能用 JAVA 和 Kotlin,Flutter 只支持 Dart,与其说这些语言更适合这些平台特性,不如说背后是谷歌、苹果、微软等巨头对平台生态掌控权的争夺。Web 与移动端要解决的问题是类似的:如何高效管理 UI 状态,现在大部分都采用数据驱动的思路,通过 JSX 或 Template 的方式描述出 UI DSL(更多可参考 [前端开发编程语言的过去、现在和未来](https://johnhax.net/2019/fe-lang/article1) UI DSL 一节)、以及性能提升:渲染和计算分离(这里又分为并发与调度两种实现思路,目的和效果是类似的)。
|
||||
|
||||
所以编程语言的未来也没什么悬念,前端领域如果有的选就用 JS,没得选只能依附所在平台绑定的语言,而前端语言最近正在完成一轮升级大迁徙:JS -> TS,JAVA -> Kotlin,OC -> Swift,前端语言的特性、易用性正在逐步趋同。需要说明的是,如果仅了解这些语言的语法,对编程能力是毫无帮助的,了解平台特性,解决业务问题,提供更好的交互体验才是前端应该不断追求的目标,随着前端、Native 开发者之间的流动,前端领域语言层面差异会会来越小,大家越关注上层,越倾向抹平语言差异,甚至可能 All in JS,这不是因为 JS 有多大野心,而是因为在解决的问题趋同、业务优先的大背景下,大家都需要减少语言不通带来的障碍,最好的办法就是统一语言,从人类语言的演变就可以发现,要解决的问题趋同(人类交流)、与国家绑定的小众语言一直都有生存空间、语法大同小异,但不同语言都有一定自己的特色(比如法语表意更精确)、跨语言学习成本高,所以当国际化协作频繁时,一定会催生一套官方语言(英语),而使用基数大的语言可能会发展为通用国际语言(中文)。
|
||||
|
||||
将编程语言的割裂、统一比作人类语言来看,就能理解现状,和未来发展趋势了。
|
||||
|
||||
## 可视化
|
||||
|
||||
前面也说过,前端的底层在逐渐封闭,而可视化就是前端的上层。
|
||||
|
||||
所以笔者很少提到工程化,原因就是未来前端开发者接触工程化的机会越来越少,工程化机制也越来越完善,前端会逐渐回归到自己的本质 - 人机交互,而交互的重要媒介就是图形,无论组件库还是智能化设计稿 To Code 都为了解放简单、模式化的交互工作,专业前端将更多聚集到图形化领域。
|
||||
|
||||
图形和数据是分不开的,所以图形化还要考虑性能问题与数据转换。
|
||||
|
||||
可视化是对性能要求最高的,因此像 web worker、GPU 加速都是常见处理手段,WASM 技术也会用到可视化中。具体到某个图表或大屏的性能优化,还会涉及数据抽样算法,分层渲染等,仅仅性能优化领域就有不少探索的空间。性能问题一般还伴随着数据量大,所以数据序列化方案也要一并考虑。
|
||||
|
||||
可视化图形学是非常学术的领域,从图形语法到交互语法,从一图一做的简单场景,到可视化分析场景的灵活拓展能力,再到探索式分析的图形语法完备性要求,可视化库想要一层层支持不同业务场景的需求,要有一个清晰的分层设计。
|
||||
|
||||
仅可视化的图形学领域,就足够将所有时间投入了,未来做可视化的前端会越来越专业,提供的工具库接口也越来越有一套最佳实践沉淀,对普通前端越来越友好。
|
||||
|
||||
BI 可视化分析就是前端深造的一个方向,跟随 BI 发展阶段,对前端的要求也在不断变化:工程化、组件化、搭建技术、渲染引擎、可视化、探索式、智能化,跟上产品对技术能力的要求,其实是相当有挑战性的。
|
||||
|
||||
## 编辑器
|
||||
|
||||
编辑器方向主要有 IDE(Web IDE)、富文本编辑器。
|
||||
|
||||
**IDE 方向** 国产做的比较好的是 HBuilder,国际上做的比较好的是 VSCode,由于微软还同时推出了 Web 版 MonacoEditor,让 Web IDE 开发的门槛大大降低。
|
||||
|
||||
作为使用者,现在和未来的主流可能都是微软系,毕竟微软在操作系统、IDE 方面人才储备和经验积累很多。但随着云服务的变迁,引导着开发方式升级,IDE 游戏规则可能迎来重大改变 - 云化。云化使得作为开发者拥有更多竞争的机会,因为云上 IDE 市场现在还是蓝海,现在很多创业公司和大公司内部都在走这个方向,这标志着中国计算机技术往更底层的技术发展,未来会有更多的话语权。
|
||||
|
||||
从发展阶段来说,前端也发展到了 Web IDE 这个时代。对大公司来说,内部有许许多多割裂的工程化孤岛,不仅消耗大量优秀的前端同学去维护,也造成内部物料体系、工程体系难以打通,阻碍了内部技术流通,而云 IDE 天生的中心化环境管理可以解决这个问题,同时还能带来抹平计算机环境差异、统一编译环境、源码不落盘、甚至实现自动的多人协作也成为了可能,而云 IDE 因为在云上,也不止于 IDE,还可以很方便的集成流程,将研发全链路打通,因此在阿里内部也成为了今年四大方向之一。
|
||||
|
||||
所以今年可以明显看到的是,前端又在逐步替代低水平重复的 UI 设计,从设计稿生成代码,到研发链路上云,这种顶层设计正在进一步收窄前端底层建设,所以未来会有更多专业前端涌入可视化领域。
|
||||
|
||||
**富文本编辑器方向** 是一个重要且小众的领域,老牌做的较好的是 UEditor 系列,现在论体验和周边功能完善度,做得最好的是语雀编辑器。开源也有很多优秀的实现,比如 Quill、DraftJS、Slate 等等,但现在富文本编辑器核心能力是功能完备性(是否支持视频、脑图、嵌入)、性能、服务化功能打通了多少(是否支持在线解析 pdf、ppt 等文件)、交互自然程度(拷贝内容的智能识别)等等。如果将眼光放到全球,那国外有大量优秀富文本编辑器案例,比如 Google Docs、Word Online、iCloud Pages 等等。
|
||||
|
||||
最好用的富文本编辑器往往不开源,因为投入的技术研发成本是巨大的,本身这项技术就是一个产品,卖点就是源码。
|
||||
|
||||
富文本编辑器功能强度可以分为三个级别:L0~L2:
|
||||
|
||||
- L0:利用浏览器自带的输入框,主要指 `contenteditable` 实现。
|
||||
- L1:在 L0 的基础上通过 DOM API 自主实现增删改的功能,自定义能力非常强。
|
||||
- L2:从输入框、光标开始自主研发,完全不依赖浏览器特性,如果研发团队能力强,可以实现任何功能,典型产品比如 Google Docs。
|
||||
|
||||
无论国内外都鲜有进入 L2 强度的产品,除了超级大公司或者主打编辑器的创业公司。
|
||||
|
||||
所以编辑器方向中,无论 IDE 方向,还是富文本编辑器方向,都值得深入探索,其中 IDE 方向更偏工程化一些,考验体系化思维,编辑器方向更偏经验与技术,考验基本功和架构设计能力。
|
||||
|
||||
## 智能化
|
||||
|
||||
笔者认为智能化离前端这个工种是比较远的,智能化最终服务前后端,给前后端开发效率带来一个质的提升,而在此之前,作为前端从业者无非有两种选择:加入智能化开拓者队伍,或者准备好放弃可能被智能化替代的工作内容,积极投身于智能化解放开发者双手后,更具有挑战性的工作。这种挑战性的工作恰好包括了上面分析过的四个点:语言、框架、可视化、编辑器。
|
||||
|
||||
类比商业智能化,商业智能化包括网络协同和数据智能,也就是大量的网络协同产生海量数据,通过数据智能算法促进更好的算法模型、更高效的网络协同,形成一个反馈闭环。前端智能化也是类似,不管是自动切图、生成图片、页面,或者自动生成代码,都需要算法和前端工程师之间形成协同关系,并完成一个高效的反馈闭环,算法将是前端工程师手中的开发利器,且越规模化的使用功效越大。
|
||||
|
||||
另一种智能化方向是探索 BI 与可视化结合的智能化,通过功能完备的底层图表库,与后端通用 Cube 计算模型,形成一种探索式分析型 BI 产品,Tableau 就是典型的案例,在这个智能化场景中,需要对数据、产品、可视化全面理解的综合性人才,是前端职业生涯另一个突破点。
|
||||
|
||||
# 3. 总结
|
||||
|
||||
本文列举的五点显然不能代表前端的全貌,还遗漏了太多方面,比如工程化、组件化、Serverless 等,但 **语言、框架、可视化、编辑器、智能化** 这五个点是笔者认为前端,特别是国内前端值得持续发力,可以做深的点,成为任何一个领域的专家都足以突破前端工程师成长的天花板。
|
||||
|
||||
最后,前端是最贴近业务的技术之一,业务的未来决定了前端的未来,创造的业务价值决定了前端的价值,从现在开始锻炼自己的商业化思考能力与产品意识,看得懂业务,才能看到未来。
|
||||
|
||||
> 讨论地址是:[精读《前端未来展望》 · Issue #178 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/178)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
# 1. 引言
|
||||
|
||||
[javascript-knowledge-reading-source-code](https://www.smashingmagazine.com/2019/07/javascript-knowledge-reading-source-code/) 这篇文章介绍了阅读源码的重要性,精读系列也已有八期源码系列文章,分别是:
|
||||
|
||||
- [精读《Immer.js》源码](https://github.com/dt-fe/weekly/blob/v2/048.%E7%B2%BE%E8%AF%BB%E3%80%8AImmer.js%E3%80%8B%E6%BA%90%E7%A0%81.md)
|
||||
- [精读《sqorn 源码》](https://github.com/dt-fe/weekly/blob/v2/073.%E7%B2%BE%E8%AF%BB%E3%80%8Asqorn%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
- [精读《Epitath 源码 - renderProps 新用法》](https://github.com/dt-fe/weekly/blob/v2/075.%E7%B2%BE%E8%AF%BB%E3%80%8AEpitath%20%E6%BA%90%E7%A0%81%20-%20renderProps%20%E6%96%B0%E7%94%A8%E6%B3%95%E3%80%8B.md)
|
||||
- [精读《Htm - Hyperscript 源码》](https://github.com/dt-fe/weekly/blob/v2/082.%E7%B2%BE%E8%AF%BB%E3%80%8AHtm%20-%20Hyperscript%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
- [精读《React PowerPlug 源码》](https://github.com/dt-fe/weekly/blob/v2/092.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20PowerPlug%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
- [精读《syntax-parser 源码》](https://github.com/dt-fe/weekly/blob/v2/093.%E7%B2%BE%E8%AF%BB%E3%80%8Asyntax-parser%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
- [精读《react-easy-state 源码》](https://github.com/dt-fe/weekly/blob/v2/098.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-easy-state%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
- [精读《Inject Instance 源码》](https://github.com/dt-fe/weekly/blob/v2/110.%E7%B2%BE%E8%AF%BB%E3%80%8AInject%20Instance%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
|
||||
笔者自己的感悟是,度过大量源码的程序员有以下几个特质:
|
||||
|
||||
1. 思考具有系统性,主要体现在改一处代码模块时,会将项目所有文件串联起来整体考虑,提前评估影响面。
|
||||
2. 思考具有前瞻性,对已实现的方案可以快速评价所处阶段(临时 or 标准 or 可拓展),将边界情况提前解决,将框架 BUG 降低到最小程度。
|
||||
3. 代码实现更优雅,有大量源码经验做支撑,解决同样问题时,这些程序员可以用更短的行数、更合适的三方库解决问题,代码可读性更好,模块拆分更合理,更利于维护。
|
||||
|
||||
既然阅读源码这么重要,那么怎么才能读好源码呢?本周精读的文章就是一篇方法论文章,告诉你如何更好的阅读源码。
|
||||
|
||||
# 2. 概述
|
||||
|
||||
原文分三个部分:阅读源码的好处、阅读源码的技巧、以及 Redux Connect 的案例研究。
|
||||
|
||||
## 阅读源码的好处
|
||||
|
||||
阅读源码有助于理解抽象的概念,比如虚拟 DOM;有助于做方案调研,而不仅仅只看 Github star 数量;了解优秀框架目录结构的设计;看到一些陌生的工具函数,还可能激发你对 JS 规范的查阅,这种问题驱动的方式也是笔者推荐的 JS 规范学习方式。
|
||||
|
||||
## 阅读源码的技巧
|
||||
|
||||
最好的阅读源码方式是看文章,如果源码的作者有写源码解读文章,这就是最省力的方式。虽然直接看代码可以了解到所有细节,但当你不清楚设计思路时,仅看源码可能会找不到方向,而读源码的最终目的是找到核心的设计理念,如果一个框架没有自己核心设计理念,这个框架也不值得诞生,更不值得被阅读。如果框架的作者已经将框架核心理念写成了文章,那读文章就是最佳方案。
|
||||
|
||||
还有一种方式是断点,写一个最小程序,在框架执行入口出打下断点,然后按照执行路径一步步理解。虽然执行路径中会存在大量无关的函数干扰精力,但如果你足够有耐心,当断点走完时一定会有所收获。
|
||||
|
||||
原文还提到了一种看源码方式,即没有目的的寻宝。在寻找框架主要思路的过程中,遇到一些有意思的函数,可以停下来仔细阅读,可能会发现一些对你有启发的代码片段。
|
||||
|
||||
## Redux Connect 案例研究
|
||||
|
||||
原文以 Redux Connect 作为案例介绍研究思路。
|
||||
|
||||
首先看到 Connect 的功能 “包装组件” 后,就要问自己两个问题:
|
||||
|
||||
1. Connect 是如何实现包装组件后原样返回组件,但却增强组件功能的?(高阶组件知识)
|
||||
2. 了解这个设计模式后,如何利用已有的文档实现它?
|
||||
|
||||
通过创建一个使用 Connect 的基本程序:
|
||||
|
||||
```js
|
||||
class MarketContainer extends Component {
|
||||
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today))
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(MarketContainer);
|
||||
```
|
||||
|
||||
比如从生成 connect 函数的 [createConnect](https://github.com/reduxjs/react-redux/blob/master/src/connect/connect.js#L46) 我们就可以学习到 [Facade Pattern](http://jargon.js.org/_glossary/FACADE_PATTERN.md) - 门面模式。
|
||||
|
||||
从 `createConnect` 函数调用处:
|
||||
|
||||
```js
|
||||
export function createConnect({
|
||||
connectHOC = connectAdvanced,
|
||||
mapStateToPropsFactories = defaultMapStateToPropsFactories,
|
||||
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
|
||||
mergePropsFactories = defaultMergePropsFactories,
|
||||
selectorFactory = defaultSelectorFactory
|
||||
} = {})
|
||||
```
|
||||
|
||||
我们可以学习到解构默认函数参数的知识点。
|
||||
|
||||
总之,在学习源码的过程中,可以了解到一些新的 JS 特性,一些设计模式,这些都是额外的宝藏,不断理解并学会运用到自己写的框架里,就实现了源码学习的目的。
|
||||
|
||||
# 3. 精读
|
||||
|
||||
原文介绍了学习源码的两个技巧,并利用 Redux Connect 实例说明了源码学习过程中可以学到许多周边知识,都让我们受益匪浅。
|
||||
|
||||
笔者结合之前写过的八篇源码分析文章,把最重要的设计思路提取出来,以实际的例子展示阅读源码能给我们思维带来哪些帮助。
|
||||
|
||||
## Immerjs 源码的精华
|
||||
|
||||
Immer 可以让我们以 Mutable 的方式更新对象,最终得到一个 Immutable 对象:
|
||||
|
||||
```js
|
||||
this.setState(produce(state => (state.isShow = true)))
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/048.%E7%B2%BE%E8%AF%BB%E3%80%8AImmer.js%E3%80%8B%E6%BA%90%E7%A0%81.md#3-%E7%B2%BE%E8%AF%BB)。
|
||||
|
||||
核心思路是利用 Proxy 把脏活累活做掉。上面的例子中,`state` 已经是一个代理(Proxy)对象,通过自定义 `setting` 不断递归进行浅拷贝,最后返回一个新引用的顶层对象作为 `produce` 的返回值。
|
||||
|
||||
从 Immerjs 中,我们学到了 Proxy 可以化腐朽为神奇的用法,比看任何 Proxy 介绍文章都直观。
|
||||
|
||||
## sqorn 源码的精华
|
||||
|
||||
sqorn 是一个 sql orm,举例来看:
|
||||
|
||||
```js
|
||||
const sq = require("sqorn-pg")();
|
||||
|
||||
const Person = sq`person`,
|
||||
Book = sq`book`;
|
||||
|
||||
// SELECT
|
||||
const children = await Person`age < ${13}`;
|
||||
// "select * from person where age < 13"
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/073.%E7%B2%BE%E8%AF%BB%E3%80%8Asqorn%20%E6%BA%90%E7%A0%81%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
核心思路是在链式调用过程中创建 context 存储结构,并在链式调用的时候不断填充 context 信息,最终拿到的是一个结构化 context 对象,生成 sql 语句也就简单了。
|
||||
|
||||
从 sqorn 中,我们学到了如何实现链式调用 `init().a().b().c().print()` 最后拿到一个综合的结果,原理是内部维护了一个不断修改的对象。不论前端 React Vue 还是后端框架 Koa 等,一般都有内置的 context,一般实现这种优雅语法的框架内部都会维护 context。
|
||||
|
||||
## Epitath 源码的精华
|
||||
|
||||
Epitath 在 React Hooks 之前出来,解决了高阶函数地狱的问题:
|
||||
|
||||
```js
|
||||
const App = epitath(function*() {
|
||||
const { count } = yield <Counter />
|
||||
const { on } = yield <Toggle />
|
||||
|
||||
return (
|
||||
<MyComponent counter={count} toggle={on} />
|
||||
)
|
||||
})
|
||||
|
||||
<App />
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/075.%E7%B2%BE%E8%AF%BB%E3%80%8AEpitath%20%E6%BA%90%E7%A0%81%20-%20renderProps%20%E6%96%B0%E7%94%A8%E6%B3%95%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
其核心是利用 `generator` 的迭代,将 React 组件的平级结构还原成嵌套结构,将嵌套写法打平了:
|
||||
|
||||
```
|
||||
yield <A>
|
||||
yield <B>
|
||||
yield <C>
|
||||
// 等价于
|
||||
<A>
|
||||
<B>
|
||||
<C />
|
||||
</B>
|
||||
</A>
|
||||
```
|
||||
|
||||
从 epitath 中,我们了解到 `generator` 原来可以这么用,正因为其执行是多次迭代的,因此我们可以利用这个特性,改变代码运行结构。
|
||||
|
||||
## Htm - Hyperscript 源码的精华
|
||||
|
||||
Htm 将模版语法很自然的融入到了 html 中:
|
||||
|
||||
```js
|
||||
html`
|
||||
<div class="app">
|
||||
<${Header} name="ToDo's (${page})" />
|
||||
<ul>
|
||||
${todos.map(
|
||||
todo => html`
|
||||
<li>${todo}</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<button onClick=${() => this.addTodo()}>Add Todo</button>
|
||||
<${Footer}>footer content here<//>
|
||||
</div>
|
||||
`;
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/082.%E7%B2%BE%E8%AF%BB%E3%80%8AHtm%20-%20Hyperscript%20%E6%BA%90%E7%A0%81%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
其核心是怎么根据模版拿到 dom 元素的 AST?拿到 AST 后就方便生成后续内容了。
|
||||
|
||||
作者的办法是:
|
||||
|
||||
```js
|
||||
const TEMPLATE = document.createElement("template");
|
||||
TEMPLATE.innerHTML = str;
|
||||
```
|
||||
|
||||
这样 TEMPLATE 就自带了 AST 解析,这是利用浏览器自带的 AST 解析拿到了 AST。从 Htm 中,我们学到了 `innerHTML` 可以生成标准 AST,所以只要有浏览器运行环境,需要拿 AST 的时候,不需要其他库,`innerHTML` 就是最好的方案。
|
||||
|
||||
## React PowerPlug 源码的精华
|
||||
|
||||
React PowerPlug 是一个利用 render props 进行状态管理的工具库。
|
||||
|
||||
它可以在 JSX 中对任意粒度插入状态管理:
|
||||
|
||||
```js
|
||||
<Value initial="React">
|
||||
{({ value, set, reset }) => (
|
||||
<>
|
||||
<Select
|
||||
label="Choose one"
|
||||
options={["React", "Preact", "Vue"]}
|
||||
value={value}
|
||||
onChange={set}
|
||||
/>
|
||||
<Button onClick={reset}>Reset to initial</Button>
|
||||
</>
|
||||
)}
|
||||
</Value>
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/092.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20PowerPlug%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
这个库的核心就是利用 render props 解决 JSX 局部状态管理的痛点,通过读源码了解 render props 的使用方式是这个源码带给你的最大价值。
|
||||
|
||||
## syntax-parser 源码的精华
|
||||
|
||||
syntax-parser 是一个 JS 版语法解器生成器,笔者也是作者,使用方式:
|
||||
|
||||
```js
|
||||
import { createParser, chain, matchTokenType, many } from "syntax-parser";
|
||||
|
||||
const root = () => chain(addExpr)(ast => ast[0]);
|
||||
|
||||
const addExpr = () =>
|
||||
chain(matchTokenType("word"), many(addPlus))(ast => ({
|
||||
left: ast[0].value,
|
||||
operator: ast[1] && ast[1][0].operator,
|
||||
right: ast[1] && ast[1][0].term
|
||||
}));
|
||||
|
||||
const addPlus = () =>
|
||||
chain("+"), root)(ast => ({
|
||||
operator: ast[0].value,
|
||||
term: ast[1]
|
||||
}));
|
||||
|
||||
const myParser = createParser(
|
||||
root, // Root grammar.
|
||||
myLexer // Created in lexer example.
|
||||
);
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/093.%E7%B2%BE%E8%AF%BB%E3%80%8Asyntax-parser%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
syntax-parser 的核心是利用双向链表实现了可回溯的语法解析器,了解了这个库,你可以自己实现 JS 调用堆栈,并在任意时候返回某个之前的执行状态重新执行。同时这个库的源码也会加强你对链表的理解,以及拓展你对链表使用场景的想象。
|
||||
|
||||
## react-easy-state 源码的精华
|
||||
|
||||
react-easy-state 利用 Proxy 创建了一个简易的全局数据流管理方式:
|
||||
|
||||
```js
|
||||
import React from "react";
|
||||
import { store, view } from "react-easy-state";
|
||||
|
||||
const counter = store({ num: 0 });
|
||||
const increment = () => counter.num++;
|
||||
|
||||
export default view(() => <button onClick={increment}>{counter.num}</button>);
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/098.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-easy-state%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
|
||||
|
||||
react-easy-state 利用了 [observer-util](https://github.com/nx-js/observer-util) 实现主要功能,从中我们能学到最有价值的就是 Proxy 与 React 结合的设计理念,即利用 `getter` `setter` 实现数据与视图的双向绑定,或者叫依赖追踪,更多细节就不在这里展开,感兴趣可以阅读笔者之前写的 [抽丝剥茧,实现依赖追踪](https://github.com/dt-fe/weekly/blob/master/35.%E7%B2%BE%E8%AF%BB%E3%80%8Adob%20-%20%E6%A1%86%E6%9E%B6%E5%AE%9E%E7%8E%B0%E3%80%8B.md#%E6%8A%BD%E4%B8%9D%E5%89%A5%E8%8C%A7%E5%AE%9E%E7%8E%B0%E4%BE%9D%E8%B5%96%E8%BF%BD%E8%B8%AA) 一节。
|
||||
|
||||
## Inject Instance 源码的精华
|
||||
|
||||
inject-instance 是一个 Class 实现依赖注入的库:
|
||||
|
||||
```js
|
||||
import {inject} from 'inject-instance'
|
||||
import B from './B'
|
||||
|
||||
class A {
|
||||
@inject('B') private b: B
|
||||
public name = 'aaa'
|
||||
|
||||
say() {
|
||||
console.log('A inject B instance', this.b.name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/110.%E7%B2%BE%E8%AF%BB%E3%80%8AInject%20Instance%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
|
||||
|
||||
主要对我们有两个启发,第一可以利用装饰器为对象存储一些额外信息,这些信息在必要的时候我们可以用到;第二是依赖注入并不复杂,通过提前实例化后,可以解决循环依赖的问题,即所有循环依赖问题都可以通过加一个父级解决。
|
||||
|
||||
# 4. 总结
|
||||
|
||||
阅读代码不是目的,读懂源码背后要表达的核心设计思路才是目的。比如写脚手架,阅读了大量脚手架源码的人写出的代码,与一个没有经验的人写出的代码会有天壤之别,这之间的差距就是对一些设计模式、三方库、结构设计的经验差距。
|
||||
|
||||
只学习理论太空洞,只看代码又太局限,学会从代码中看出理论才是最佳学习方式。
|
||||
|
||||
> 讨论地址是:[精读《源码学习》 · Issue #179 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/179)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
Reference in New Issue
Block a user