javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是:javascript
筆者本身的感悟是,度過大量源碼的程序員有如下幾個特質:html
既然閱讀源碼這麼重要,那麼怎麼才能讀好源碼呢?本週精讀的文章就是一篇方法論文章,告訴你如何更好的閱讀源碼。前端
原文分三個部分:閱讀源碼的好處、閱讀源碼的技巧、以及 Redux Connect 的案例研究。java
閱讀源碼有助於理解抽象的概念,好比虛擬 DOM;有助於作方案調研,而不只僅只看 Github star 數量;瞭解優秀框架目錄結構的設計;看到一些陌生的工具函數,還可能激發你對 JS 規範的查閱,這種問題驅動的方式也是筆者推薦的 JS 規範學習方式。react
最好的閱讀源碼方式是看文章,若是源碼的做者有寫源碼解讀文章,這就是最省力的方式。雖然直接看代碼能夠了解到全部細節,但當你不清楚設計思路時,僅看源碼可能會找不到方向,而讀源碼的最終目的是找到核心的設計理念,若是一個框架沒有本身核心設計理念,這個框架也不值得誕生,更不值得被閱讀。若是框架的做者已經將框架核心理念寫成了文章,那讀文章就是最佳方案。git
還有一種方式是斷點,寫一個最小程序,在框架執行入口出打下斷點,而後按照執行路徑一步步理解。雖然執行路徑中會存在大量無關的函數干擾精力,但若是你足夠有耐心,當斷點走完時必定會有所收穫。程序員
原文還提到了一種看源碼方式,即沒有目的的尋寶。在尋找框架主要思路的過程當中,遇到一些有意思的函數,能夠停下來仔細閱讀,可能會發現一些對你有啓發的代碼片斷。github
原文以 Redux Connect 做爲案例介紹研究思路。sql
首先看到 Connect 的功能 「包裝組件」 後,就要問本身兩個問題:redux
經過建立一個使用 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 特性,一些設計模式,這些都是額外的寶藏,不斷理解並學會運用到本身寫的框架裏,就實現了源碼學習的目的。
原文介紹了學習源碼的兩個技巧,並利用 Redux Connect 實例說明了源碼學習過程當中能夠學到許多周邊知識,都讓咱們受益不淺。
筆者結合以前寫過的八篇源碼分析文章,把最重要的設計思路提取出來,以實際的例子展現閱讀源碼能給咱們思惟帶來哪些幫助。
Immer 可讓咱們以 Mutable 的方式更新對象,最終獲得一個 Immutable 對象:
this.setState(produce(state => (state.isShow = true)))
詳細源碼解讀能夠閱讀 這裏。
核心思路是利用 Proxy 把髒活累活作掉。上面的例子中,state
已是一個代理(Proxy)對象,經過自定義 setting
不斷遞歸進行淺拷貝,最後返回一個新引用的頂層對象做爲 produce
的返回值。
從 Immerjs 中,咱們學到了 Proxy 能夠化腐朽爲神奇的用法,比看任何 Proxy 介紹文章都直觀。
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 在 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 將模版語法很天然的融入到了 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 是一個利用 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 是一個 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 利用 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 是一個 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) } }
詳細源碼解讀能夠閱讀 這裏
主要對咱們有兩個啓發,第一能夠利用裝飾器爲對象存儲一些額外信息,這些信息在必要的時候咱們能夠用到;第二是依賴注入並不複雜,經過提早實例化後,能夠解決循環依賴的問題,即全部循環依賴問題均可以經過加一個父級解決。
閱讀代碼不是目的,讀懂源碼背後要表達的核心設計思路纔是目的。好比寫腳手架,閱讀了大量腳手架源碼的人寫出的代碼,與一個沒有經驗的人寫出的代碼會有天壤之別,這之間的差距就是對一些設計模式、三方庫、結構設計的經驗差距。
只學習理論太空洞,只看代碼又太侷限,學會從代碼中看出理論纔是最佳學習方式。
討論地址是: 精讀《源碼學習》 · Issue #179 · dt-fe/weekly
若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。
關注 前端精讀微信公衆號
<img width=200 src="https://img.alicdn.com/tfs/TB...;>
版權聲明:自由轉載-非商用-非衍生-保持署名( 創意共享 3.0 許可證)