精读《源码学习》

1. 引言

javascript-knowledge-reading-source-code 这篇文章介绍了阅读源码的重要性,精读系列也已有八期源码系列文章,分别是:javascript

笔者本身的感悟是,度过大量源码的程序员有如下几个特质:html

  1. 思考具备系统性,主要体如今改一处代码模块时,会将项目全部文件串联起来总体考虑,提早评估影响面。
  2. 思考具备前瞻性,对已实现的方案能够快速评价所处阶段(临时 or 标准 or 可拓展),将边界状况提早解决,将框架 BUG 下降到最小程度。
  3. 代码实现更优雅,有大量源码经验作支撑,解决一样问题时,这些程序员能够用更短的行数、更合适的三方库解决问题,代码可读性更好,模块拆分更合理,更利于维护。

既然阅读源码这么重要,那么怎么才能读好源码呢?本周精读的文章就是一篇方法论文章,告诉你如何更好的阅读源码。前端

2. 概述

原文分三个部分:阅读源码的好处、阅读源码的技巧、以及 Redux Connect 的案例研究。java

阅读源码的好处

阅读源码有助于理解抽象的概念,好比虚拟 DOM;有助于作方案调研,而不只仅只看 Github star 数量;了解优秀框架目录结构的设计;看到一些陌生的工具函数,还可能激发你对 JS 规范的查阅,这种问题驱动的方式也是笔者推荐的 JS 规范学习方式。react

阅读源码的技巧

最好的阅读源码方式是看文章,若是源码的做者有写源码解读文章,这就是最省力的方式。虽然直接看代码能够了解到全部细节,但当你不清楚设计思路时,仅看源码可能会找不到方向,而读源码的最终目的是找到核心的设计理念,若是一个框架没有本身核心设计理念,这个框架也不值得诞生,更不值得被阅读。若是框架的做者已经将框架核心理念写成了文章,那读文章就是最佳方案。git

还有一种方式是断点,写一个最小程序,在框架执行入口出打下断点,而后按照执行路径一步步理解。虽然执行路径中会存在大量无关的函数干扰精力,但若是你足够有耐心,当断点走完时必定会有所收获。程序员

原文还提到了一种看源码方式,即没有目的的寻宝。在寻找框架主要思路的过程当中,遇到一些有意思的函数,能够停下来仔细阅读,可能会发现一些对你有启发的代码片断。github

Redux Connect 案例研究

原文以 Redux Connect 做为案例介绍研究思路。sql

首先看到 Connect 的功能 “包装组件” 后,就要问本身两个问题:redux

  1. Connect 是如何实现包装组件后原样返回组件,但却加强组件功能的?(高阶组件知识)
  2. 了解这个设计模式后,如何利用已有的文档实现它?

经过建立一个使用 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 } = yield <Counter />
  const { on } = yield <Toggle />

  return (
    <MyComponent counter={count} toggle={on} />
  )
})

<App />
详细源码解读能够阅读 这里

其核心是利用 generator 的迭代,将 React 组件的平级结构还原成嵌套结构,将嵌套写法打平了:

yield <A>
yield <B>
yield <C>
// 等价于
<A>
  <B>
    <C />
  </B>
</A>

从 epitath 中,咱们了解到 generator 原来能够这么用,正由于其执行是屡次迭代的,所以咱们能够利用这个特性,改变代码运行结构。

Htm - Hyperscript 源码的精华

Htm 将模版语法很天然的融入到了 html 中:

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>
`;
详细源码解读能够阅读 这里

其核心是怎么根据模版拿到 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 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>
详细源码解读能够阅读 这里

这个库的核心就是利用 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(() => <button onClick={increment}>{counter.num}</button>);
详细源码解读能够阅读 这里

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

若是你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

<img width=200 src="https://img.alicdn.com/tfs/TB...;>

版权声明:自由转载-非商用-非衍生-保持署名( 创意共享 3.0 许可证
相关文章
相关标签/搜索