精读《源码学习》

1. 引言

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

  • 精读《Immer.js》源码
  • 精读《sqorn 源码》
  • 精读《Epitath 源码 - renderProps 新用法》
  • 精读《Htm - Hyperscript 源码》
  • 精读《React PowerPlug 源码》
  • 精读《syntax-parser 源码》
  • 精读《react-easy-state 源码》
  • 精读《Inject Instance 源码》

笔者自己的感悟是,读过大量源码的程序员有以下几个特质:

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

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

2. 概述

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

阅读源码的好处

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

阅读源码的技巧

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

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

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

Redux Connect 案例研究

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

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

  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 }) => (<><Selectlabel="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: Bpublic name = 'aaa'say() {console.log('A inject B instance', this.b.name)}
}

详细源码解读可以阅读 这里

主要对我们有两个启发,第一可以利用装饰器为对象存储一些额外信息,这些信息在必要的时候我们可以用到;第二是依赖注入并不复杂,通过提前实例化后,可以解决循环依赖的问题,即所有循环依赖问题都可以通过加一个父级解决。

4. 总结

阅读代码不是目的,读懂源码背后要表达的核心设计思路才是目的。比如写脚手架,阅读了大量脚手架源码的人写出的代码,与一个没有经验的人写出的代码会有天壤之别,这之间的差距就是对一些设计模式、三方库、结构设计的经验差距。

只学习理论太空洞,只看代码又太局限,学会从代码中看出理论才是最佳学习方式。

讨论地址是:精读《源码学习》 · Issue #179 · dt-fe/weekly

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/701641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

并查集例题(食物链)C++(Acwing)

代码&#xff1a; #include <iostream>using namespace std;const int N 50010;int n, m; int p[N], d[N];int find(int x) {if(p[x] ! x){int t find(p[x]);d[x] d[p[x]];p[x] t;}return p[x]; }int main() {scanf("%d%d", &n, &m);for(int i 1…

阿里大文娱前端一面

引言 我目前本科大四&#xff0c;正在春招找前端&#xff0c;有大厂内推的友友可以聊一聊&#xff0c;球球给孩子的机会吧。 我整理了一份10w字的前端技术文档&#xff1a;https://qx8wba2yxsl.feishu.cn/docx/Vb5Zdq7CGoPAsZxMLztc53E1n0k?fromfrom_copylink&#xff0c;对…

【蓝桥杯入门记录】静态数码管例程

目录 一、补充 &#xff08;code&#xff09; 二、例程 &#xff08;1&#xff09;例程1&#xff1a;数码管显示某一位&#xff08;某一杠&#xff09;。以点亮8段数码管最上面的横杠为例。 &#xff08;2&#xff09;例程2&#xff1a;数码管的8个段依次点亮&#xff08;其他…

linux前端部署

安装jdk 配置环境变量 刷新配置文件 source profile source /etc/profile tomcat 解压文件 进去文件启动tomcat 开放tomcat的端口号 访问 curl localhsot:8080 改配置文件 改IP,改数据库名字&#xff0c;密码&#xff0c; 安装数据库 将war包拖进去 访问http:…

C++递归

角谷猜想 #include<bits/stdc.h> using namespace std; int n,sum0; void f(int); int main() {cin>>n;f(n);cout<<sum;return 0; } void f(int n){if(n1) return;if(n%20) nn/2;if(n%21) nn*31;sum; } 求两个数M和N的最大公约数

【Python笔记-设计模式】代理模式

一、说明 代理模式是一种结构型设计模式&#xff0c;提供对象的替代品或其占位符。代理控制着对于原对象的访问&#xff0c;并允许在将请求提交给对象前后进行一些处理。 (一) 解决问题 控制对对象的访问&#xff0c;或在访问对象前增加额外的功能或控制访问 (二) 使用场景…

[c++] public, private, protected, friend

权限管理是 c 的一大特点&#xff0c;面向对象语言封装的特性也给权限管理带了了方便。c 中的权限主要有 3 种&#xff1a;public&#xff0c;private&#xff0c;protected。类中的函数和属性默认是 private 的&#xff0c;类的继承关系默认也是 private 的。 public&#xf…

apidoc接口文档的自动更新与发布

文章目录 一、概述二、环境准备三、接口文档生成1. 下载源码2. 初始化3.执行 四、文档发布五&#xff0c;配置定时运行六&#xff0c;docker运行七&#xff0c;不足与优化分析 一、概述 最近忙于某开源项目的接口文档整理&#xff0c;采用了apidoc来整理生成接口文档。 apidoc…

深度学习系列59:文字识别

1. 简单文本&#xff1a; 使用google加的tesseract&#xff0c;效果不错。 首先安装tesseract&#xff0c;在mac直接brew install即可。 python调用代码&#xff1a; import pytesseract from PIL import Image img Image.open(1.png) pytesseract.image_to_string(img, lan…

MES管理系统生产过程控制的核心要素

MES&#xff08;制造执行系统&#xff09;是为优化制造业生产过程和管理而设计的软件系统&#xff0c;其核心要素包括&#xff1a; 工单管理&#xff1a;工单管理是MES系统最基本的功能之一&#xff0c;它可以跟踪和管理各种类型的工单&#xff0c;如生产工单、维修工单和质量…

解释 C++ 中的虚拟继承(Virtual Inheritance)

解释 C 中的虚拟继承&#xff08;Virtual Inheritance&#xff09; 在 C 中&#xff0c;虚拟继承&#xff08;Virtual Inheritance&#xff09;是一种特殊的继承方式&#xff0c;用于解决多继承带来的菱形继承&#xff08;Diamond Inheritance&#xff09;问题。虚拟继承主要通…

Spring篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、spring 支持集中 bean scope?二、spring bean 容器的生命周期是什么样的?三、什么是 spring 的内部 bean?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男…

Java EE改名Jakarta EE,jakarta对程序开发的影响

一、前言 很多Java程序员在使用新版本的Spring6或者springboot3版本的时候&#xff0c;发现了一些叫jakarta的包。我在阅读开源工作流引擎camunda源代码的时候&#xff0c;也发展了大量jakarta的工程包。 比如&#xff1a;camunda的webapps编译工程就提供了2种方式javax和jaka…

SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的多特征分类预测

SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的故障多特征分类预测 目录 SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的故障多特征分类预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍…

论文阅读:Ground-Fusion: A Low-cost Ground SLAM System Robust to Corner Cases

前言 最近看到一篇ICRA2024上的新文章&#xff0c;是关于多传感器融合SLAM的&#xff0c;好像使用了最近几年文章中较火的轮式里程计。感觉这篇文章成果不错&#xff0c;代码和数据集都是开源的&#xff0c;今天仔细读并且翻译一下&#xff0c;理解创新点、感悟研究方向、指导…

【杂谈】还能这么骗Github开源者?

起因 StarkNet给Github前5000的账户空投了一波STRK代币,一般有资格获得空投的开发者&#xff0c;大概能获得 110个 STRK 代币&#xff0c;按目前价格计算大概 1500人民币左右。 什么是有资格的开发者呢&#xff1f;按 Starknet要求&#xff0c;如果你给在 GitHub上排名前 5000…

【精选】网络安全大厂面试题 2.0

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

基于SSM的废品买卖回收管理系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的废品买卖回收管理系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spri…

Spring Boot+ShardingSphere+MySQL实现分库分表:高效数据库扩展

在构建现代Web应用程序时&#xff0c;数据库的性能和可扩展性是至关重要的。当应用程序的数据量逐渐增加时&#xff0c;传统的单一数据库可能无法满足需求。分库分表是一种有效的数据库水平扩展方法&#xff0c;可以显著提高数据库性能并实现负载均衡。 什么是分库分表 分库分…

Vue+SpringBoot打造开放实验室管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…