摘要:精读原文介绍了学习源码的两个技巧,并利用实例说明了源码学习过程中可以学到许多周边知识,都让我们受益匪浅。讨论地址是精读源码学习如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。
1. 引言
javascript-knowledge-reading-source-code 这篇文章介绍了阅读源码的重要性,精读系列也已有八期源码系列文章,分别是:
精读《Immer.js》源码
精读《sqorn 源码》
精读《Epitath 源码 - renderProps 新用法》
精读《Htm - Hyperscript 源码》
精读《React PowerPlug 源码》
精读《syntax-parser 源码》
精读《react-easy-state 源码》
精读《Inject Instance 源码》
笔者自己的感悟是,度过大量源码的程序员有以下几个特质:
思考具有系统性,主要体现在改一处代码模块时,会将项目所有文件串联起来整体考虑,提前评估影响面。
思考具有前瞻性,对已实现的方案可以快速评价所处阶段(临时 or 标准 or 可拓展),将边界情况提前解决,将框架 BUG 降低到最小程度。
代码实现更优雅,有大量源码经验做支撑,解决同样问题时,这些程序员可以用更短的行数、更合适的三方库解决问题,代码可读性更好,模块拆分更合理,更利于维护。
既然阅读源码这么重要,那么怎么才能读好源码呢?本周精读的文章就是一篇方法论文章,告诉你如何更好的阅读源码。
2. 概述原文分三个部分:阅读源码的好处、阅读源码的技巧、以及 Redux Connect 的案例研究。
阅读源码的好处阅读源码有助于理解抽象的概念,比如虚拟 DOM;有助于做方案调研,而不仅仅只看 Github star 数量;了解优秀框架目录结构的设计;看到一些陌生的工具函数,还可能激发你对 JS 规范的查阅,这种问题驱动的方式也是笔者推荐的 JS 规范学习方式。
阅读源码的技巧最好的阅读源码方式是看文章,如果源码的作者有写源码解读文章,这就是最省力的方式。虽然直接看代码可以了解到所有细节,但当你不清楚设计思路时,仅看源码可能会找不到方向,而读源码的最终目的是找到核心的设计理念,如果一个框架没有自己核心设计理念,这个框架也不值得诞生,更不值得被阅读。如果框架的作者已经将框架核心理念写成了文章,那读文章就是最佳方案。
还有一种方式是断点,写一个最小程序,在框架执行入口出打下断点,然后按照执行路径一步步理解。虽然执行路径中会存在大量无关的函数干扰精力,但如果你足够有耐心,当断点走完时一定会有所收获。
原文还提到了一种看源码方式,即没有目的的寻宝。在寻找框架主要思路的过程中,遇到一些有意思的函数,可以停下来仔细阅读,可能会发现一些对你有启发的代码片段。
Redux Connect 案例研究原文以 Redux Connect 作为案例介绍研究思路。
首先看到 Connect 的功能 “包装组件” 后,就要问自己两个问题:
Connect 是如何实现包装组件后原样返回组件,但却增强组件功能的?(高阶组件知识)
了解这个设计模式后,如何利用已有的文档实现它?
通过创建一个使用 Connect 的基本程序:
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 我们就可以学习到 Facade Pattern - 门面模式。
从 createConnect 函数调用处:
export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {})
我们可以学习到解构默认函数参数的知识点。
总之,在学习源码的过程中,可以了解到一些新的 JS 特性,一些设计模式,这些都是额外的宝藏,不断理解并学会运用到自己写的框架里,就实现了源码学习的目的。
3. 精读原文介绍了学习源码的两个技巧,并利用 Redux Connect 实例说明了源码学习过程中可以学到许多周边知识,都让我们受益匪浅。
笔者结合之前写过的八篇源码分析文章,把最重要的设计思路提取出来,以实际的例子展示阅读源码能给我们思维带来哪些帮助。
Immerjs 源码的精华Immer 可以让我们以 Mutable 的方式更新对象,最终得到一个 Immutable 对象:
this.setState(produce(state => (state.isShow = true)))
详细源码解读可以阅读 这里。
核心思路是利用 Proxy 把脏活累活做掉。上面的例子中,state 已经是一个代理(Proxy)对象,通过自定义 setting 不断递归进行浅拷贝,最后返回一个新引用的顶层对象作为 produce 的返回值。
从 Immerjs 中,我们学到了 Proxy 可以化腐朽为神奇的用法,比看任何 Proxy 介绍文章都直观。
sqorn 源码的精华sqorn 是一个 sql orm,举例来看:
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"
详细源码解读可以阅读 这里
核心思路是在链式调用过程中创建 context 存储结构,并在链式调用的时候不断填充 context 信息,最终拿到的是一个结构化 context 对象,生成 sql 语句也就简单了。
从 sqorn 中,我们学到了如何实现链式调用 init().a().b().c().print() 最后拿到一个综合的结果,原理是内部维护了一个不断修改的对象。不论前端 React Vue 还是后端框架 Koa 等,一般都有内置的 context,一般实现这种优雅语法的框架内部都会维护 context。
Epitath 源码的精华Epitath 在 React Hooks 之前出来,解决了高阶函数地狱的问题:
const App = epitath(function*() { const { count } = yieldconst { on } = yield return ( ) })
详细源码解读可以阅读 这里
其核心是利用 generator 的迭代,将 React 组件的平级结构还原成嵌套结构,将嵌套写法打平了:
yield yield yield// 等价于
从 epitath 中,我们了解到 generator 原来可以这么用,正因为其执行是多次迭代的,因此我们可以利用这个特性,改变代码运行结构。
Htm - Hyperscript 源码的精华Htm 将模版语法很自然的融入到了 html 中:
html`<${Header} name="ToDo"s (${page})" />`;${todos.map( todo => html`
<${Footer}>footer content here/>- ${todo}
` )}
详细源码解读可以阅读 这里
其核心是怎么根据模版拿到 dom 元素的 AST?拿到 AST 后就方便生成后续内容了。
作者的办法是:
const TEMPLATE = document.createElement("template"); TEMPLATE.innerHTML = str;
这样 TEMPLATE 就自带了 AST 解析,这是利用浏览器自带的 AST 解析拿到了 AST。从 Htm 中,我们学到了 innerHTML 可以生成标准 AST,所以只要有浏览器运行环境,需要拿 AST 的时候,不需要其他库,innerHTML 就是最好的方案。
React PowerPlug 源码的精华React PowerPlug 是一个利用 render props 进行状态管理的工具库。
它可以在 JSX 中对任意粒度插入状态管理:
{({ value, set, reset }) => ( <> > )}
详细源码解读可以阅读 这里
这个库的核心就是利用 render props 解决 JSX 局部状态管理的痛点,通过读源码了解 render props 的使用方式是这个源码带给你的最大价值。
syntax-parser 源码的精华syntax-parser 是一个 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. );
详细源码解读可以阅读 这里
syntax-parser 的核心是利用双向链表实现了可回溯的语法解析器,了解了这个库,你可以自己实现 JS 调用堆栈,并在任意时候返回某个之前的执行状态重新执行。同时这个库的源码也会加强你对链表的理解,以及拓展你对链表使用场景的想象。
react-easy-state 源码的精华react-easy-state 利用 Proxy 创建了一个简易的全局数据流管理方式:
import React from "react"; import { store, view } from "react-easy-state"; const counter = store({ num: 0 }); const increment = () => counter.num++; export default view(() => );
详细源码解读可以阅读 这里
react-easy-state 利用了 observer-util 实现主要功能,从中我们能学到最有价值的就是 Proxy 与 React 结合的设计理念,即利用 getter setter 实现数据与视图的双向绑定,或者叫依赖追踪,更多细节就不在这里展开,感兴趣可以阅读笔者之前写的 抽丝剥茧,实现依赖追踪 一节。
Inject Instance 源码的精华inject-instance 是一个 Class 实现依赖注入的库:
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) } }
详细源码解读可以阅读 这里
主要对我们有两个启发,第一可以利用装饰器为对象存储一些额外信息,这些信息在必要的时候我们可以用到;第二是依赖注入并不复杂,通过提前实例化后,可以解决循环依赖的问题,即所有循环依赖问题都可以通过加一个父级解决。
4. 总结阅读代码不是目的,读懂源码背后要表达的核心设计思路才是目的。比如写脚手架,阅读了大量脚手架源码的人写出的代码,与一个没有经验的人写出的代码会有天壤之别,这之间的差距就是对一些设计模式、三方库、结构设计的经验差距。
只学习理论太空洞,只看代码又太局限,学会从代码中看出理论才是最佳学习方式。
讨论地址是:精读《源码学习》 · Issue #179 · dt-fe/weekly
如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105851.html
摘要:精读源码一共行,我们分析一下其精妙的方式。更多讨论讨论地址是精读新用法如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。 1 引言 很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的。 前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念、库也越来越多。虽然内容越来越多,但作为个体的...
摘要:引言前端精读手写编译器系列介绍了如何利用生成语法树,而还有一些库的作用是根据语法树生成语句。对,有利就有弊,这些库不遵循语法树,但利用简化的对象模型快速生成,使得代码抽象程度得到了提高。 1 引言 前端精读《手写 SQL 编译器系列》 介绍了如何利用 SQL 生成语法树,而还有一些库的作用是根据语法树生成 SQL 语句。 除此之外,还有一种库,是根据编程语言生成 SQL。sqorn 就...
showImg(https://segmentfault.com/img/bVbw3tK?w=1240&h=827); 前端工程师这个岗位,真的是反人性的 我们来思考一个问题: 一个6年左右经验的前端工程师: 前面两年在用jQuery 期间一直在用React-native(一步一步踩坑过来的那种) 最近两年还在写微信小程序 下面一个2年经验的前端工程师: 并不会跨平台技术,他的两年工作都是Reac...
摘要:会自动触发函数内回调函数的执行。因此利用并将依赖置为使代码在所有渲染周期内,只在初始化执行一次。同时代码里还对等公共方法进行了包装,让这些回调函数中自带效果。前端精读帮你筛选靠谱的内容。 1. 引言 react-easy-state 是个比较有趣的库,利用 Proxy 创建了一个非常易用的全局数据流管理方式。 import React from react; import { stor...
摘要:今天,我我的后端书架后端掘金我的后端书架月前本书架主要针对后端开发与架构。尤其是对称加密,非对称加密,私钥加密,公钥加密滴滴动态化方案的诞生与起航掘金这是滴滴架构组发布的第一篇公共技术文章,本文将介绍自研的动态化方案。 android 阿里面试题锦集 - Android - 掘金前几天突然就经历了阿里android实习内推的电面,感觉有好多以前看过的东西都忘记了,然后又复习了一下,找了...
阅读 2563·2021-09-02 15:40
阅读 1568·2019-08-30 15:54
阅读 1081·2019-08-30 12:48
阅读 3400·2019-08-29 17:23
阅读 1047·2019-08-28 18:04
阅读 3666·2019-08-26 13:54
阅读 607·2019-08-26 11:40
阅读 2393·2019-08-26 10:15