從前端開發看面向未來的敏捷學習法
註:本文第一版我首發於我的GitChat,這一版我在原有基礎上更新並增加了一些章節,後請 Shize Zhou 整理並重新排版,在這裡也發布一下。本文以前端開發為載體講解在新形勢下的程序員的學習方法,因自己水平有限,內容接受大家批評,歡迎讀者和我深入討論。本文還發布過個人的 (jianshuhttp://www.jianshu.com/p/fd7055705c62)。
大綱
- 選題背景
- 敏捷學習法
- 知識遷移
- 信息獲取與篩選
- 新形勢下的閱讀方式
- 社區化學習
- 工具&實驗田
- 時間管理
- 畫圖與做題
- 總結
1.0 選題背景
在糾結選題時候,我一直在思考分享什麼知識比較好。平時在工作中確實有許多經驗和技術積累可以分享,但我發現這些技術在互聯網上或多或少都有相應資料,自己平時遇到各種問題也是去探尋的。於是我覺得不如聊聊面向未來的學習方法,有時候具體講一個知識點不如告訴你如何自己在資料海洋中自我敏捷學習。
每個開發者都是從小白走過來的,這條路非常坎坷。有的人花了較少時間就達到了架構師級別並且帶領小團隊有序開發,有的開發者則花了5、6年時間還在基礎崗折騰。拋開個人努力程度看,學習方法也是至關重要的。說到學習方法,從小到大我們學習過很多知識,可真正在工作中用到的卻是很小的子集,留下的是學習知識的能力。
目前行業趨勢迫使大家不斷跟進新技術,需要較高學習能力的人。我們研發 WebIDE 時,團隊並沒有所謂的 IDE 工程師,國內同類產品就是空白,一切只能靠我們上海幾個人各種摸索學習在短時間內做出產品。產品很出色但是過程卻是痛苦的,新問題新難點不斷湧現。這時除了努力更需要的是解決問題的能力。如果你能擁有這樣的能力,即使你不會某個具體技術點,那並不重要。因為你可以短時間學會,並同時夯實基礎,這才是面向未來的人才。
在技術瞬息萬變的今天,原來那些只靠學歷、人脈、經驗升職已經過時了,要想保持自己的職場競爭力,練好一件本事就夠了,那就是學習敏捷力。——美國著名企業高管教練,瓦爾庫。
我以前端開發為例,社區技術派別已達到了百家爭鳴階段,眾多公司推出了自己的最佳實踐以及相應的開源庫,學校、培訓班、出版社儘力的跟隨。在這種情況下,很多朋友很困惑,按照傳統一般的學習方法,似乎很難應對。選資料或書籍就是一個大難題,讀完別人又告訴此版本已升級或者書中有很多錯誤,甚至工作中已經不再使用了。再者,平實工作任務很緊,沒時間讓你慢慢啃。還有很多知識要你同時去學。
這時候很多人開始煩躁、抵觸,迷茫,懷疑技術,懷疑自己。而恰恰這時候需要的是耐心,走一點,停下來,仔細思考自己的學習方法是不是需要調整一下了。以上的思考過程,讓我明白第一次 GitChat 的分享內容,以前端開發為載體和大家聊聊面向未來的社區化敏捷學習方法。必須要說明的是,這塊知識量很多,本次分享只能從面上給大家介紹無法太細緻。
2.0 敏捷學習法
敏捷學習方法並沒有專業的文獻資料做背書,我只是結合自身理解和大家分享,受限於自身水平,並不一定是對的,只望給大家有參考意義。本節從基本的理論、知識樹以及兩個案例開始。
2.1 理論
敏捷學習的核心是敏捷的通過外部刺激更新自己的知識樹。
所謂的外部刺激主要包括被動解決各類問題以及主動研究各類課題。解決新問題時,利用自己已積累的所有相關知識判斷可能的最優解決方法並拿它排序。其後對解決方案做拆解得出一些知識關鍵詞,逐條快速通過知識獲取途徑得到相關資料,迅速實驗田驗證,過程中需要實時更新自己的知識樹,後期不斷完善整理知識樹,成為新的積累,便於之後新的問題突破。主動研究課題時,則通過閱讀各類資料迅速整理知識點,更新知識樹,重建新知識與老知識的關係鏈,弄清楚每一個知識之間的聯繫。
2.2 知識樹基本概念
我們把自己的知識想像成是一顆帶有關係箭頭的樹(思維導圖)。如圖:
思維導圖demo知識關係圖在線mindnode
知識樹項目此圖只是 demo,每個知識結點都有自己的關聯和上下文。我們可以給每個子模塊添加顏色,深淺代表我們的對他的記憶程度,越實則使用越頻繁,記憶也越深,越虛則越淡。甚至可能只是一個軟鏈接在這裡佔位,遇到這個問題知道怎麼搜索怎麼查,但是不一定深刻理解。我們每天做各類項目和平時閱讀交流的過程中,只要遇到新的知識,就應該先他掛到我們的知識樹上,成為一顆虛樹,而後繼續整理,填充知識關係與知識深度使樹更完善。這裡所描述的知識樹是一個抽象的概念,具體表現形式大家可以有自己的發揮。我一般使用思維導圖,複雜的知識需要多個 sheet 來表達,並做好關聯工作。
下面結合前端開發工作中實際的解決問題與主動學習兩個案例讓大家熱熱身。
2.3 敏捷解決問題案例
本節介紹自己曾經解決過的一個問題,告訴大家我的思考方式和學習方式。
2.3.1 問題分析
Issue: 解決一個 RN 項目開機經常直接閃退的問題。
直接靜態觀察代碼,並看不出什麼問題來,簡單的 debug 也反映不出問題。假定我是一個 RN 初學者,但是 KPI 要求我迅速完成工作。
我通過其他編程經驗告訴我,對閃退和性能的問題,需要對開機過程每一步啟動過程做一個分析,打合適斷點觀測,看看哪一步閃退了,確定問題。而後則方案無非就是
- Catch 相應的 error,不能讓他影響整個 APP
- 解決相應業務的問題。
2.3.2 知識拆解
我把問題第一步監測大點拆解為以下三個問題。
- RN 開啟到首頁渲染的關鍵幀(關鍵重要的可測結點)。
- 監測與表達。
- 監測內容。
2.3.2.1 第一個問題
先從自己的知識樹去找,假設我對 RN 的啟動順序,只停留在「先載入需要用的資源,然後調用 JS 邏輯」的層面。為了解決問題,必須獲取這方面知識,在知識樹上建立虛樹。技術術語主要就是: React native start order, React native debug 之類。從 Google 搜索得到了一些網頁信息,從 Stack Overflow 也得到了一些相關問題。經過整理然後我得出了解決方案,斷點應該打在 Native 啟動入口,JS-Bridge,JS 入口點,JS 初始化操作(redux store 初始化)、JS view mount 的起點等...同時我把剛才的幾篇文章 link 和內容做了一個速記,補充在這顆虛樹中,提醒我之後可以完善它。關於獲取信息的方法與篩選在之後的章節會詳細講解。
2.3.2.2 第二個問題
基本上思路就是把斷點處的各種監測數據 dump 到統一的 dashboard 中。我聯想到在其他編程體驗中,我也許會用自帶的斷點工具,或者 console.log 把相關信息打在控制台。當然也可能會丟給 data collector server 統一觀測,這取決於不同的業務場景。
在這個案例中,JS 和兩個 native 以及中間的一些庫的 service 並不在一個 scope,很難能跨服務的跟蹤,追查哪一步出了什麼問題導致啟動慢而容易 crash。於是就用了做微服務監測的知識點,用 fluentd 做一個 log center,它能把 logger 匯聚到一起展現出來。
2.3.2.3 第三個問題
回到一開始的需求,我需要解決啟動慢且容易閃退的問題。需要檢測兩個主要信息點,一個是 time duration;一個是 health check。所以每一層斷點需要做的就是給我反饋出耗時(當前時間與第一個斷點時間差)、當前健康情況。
2.3.3 實驗
我們必須迅速做實驗判斷此方案的可行性,本案例中實驗環境的搭建很簡單。fluentd 用 Docker 版本地先布,代碼庫則建一個 multiple worktree,在 fake 節點上做實驗。先測幾個最重要的斷點,最快速度判斷此方案是否可行,並初步縮小範圍。經過檢驗,方案可行,在 native 層的 health 情況正常,時間在允許範圍內。接下來只需要測試 JS 部分即可。
2.3.4 實施
經過實驗,我們確定這個監測方案是可行的,接下來按照流程一步一步實施。實施過程和之前一樣。實施的結論:最終我們發現,redux-persist-store 這個庫耗時過長並有一定幾率崩潰。
2.3.5 確診問題
我們看到的監測結果類似醫院的化驗結果,確診需要拿結果結合經驗以及別人的實踐找出原因,目的是為了尋找測試用例。根據這庫功能猜可能是緩存數據沒存對,導致一進來的時候就奔潰了。此時關鍵詞描述變成了 redux-persist-store crash 問題,看了一圈別人的 issue 之後,更加堅定這個想法。這時候需要尋找必現問題的測試用例。ADB 進安卓系統,發現 persist store 在本地存的是文件。這時候我通過對他的增、刪、改等手段,觀察我們的 log 與 APP 情況。發現當數據結構不一致時它必然崩潰,所以猜想被印證是對的。此時立馬更新我的知識樹,並加深了我對 redux-persist 的印象。
2.3.6 對症下藥
這個步驟需要對症下藥,此時癥狀能表達的關鍵詞更加明確:
-
- 對這個錯誤類型做 catch
-
- Persist store 需要調整數據緩存中間過程,不應該留有錯誤數據。
這個問題先看看社區是怎麼做的,於是繼續進入資料查找模式,找到此庫有 custom transform 的 API 可以讓我處理 incomming 和 outcomming 的流,於是我可以在那個 scope 去做 data check。至於 check 這個知識點,根據以前編程知識我們很容易想到結構驗證,和數據篩選。這裡繼續用敏捷學習法,學到結構驗證可用 JS-Schema 做,數據篩選則直接寫規則 filter。Schema 的規則根據實際數據情況定,filter 規則根據做 Code split 概念的影響,應該是有按需緩存,按使用比例緩存,按 size 緩存的策略。對 error handler 這個知識點,我們很容易想到 try, catch, filter error type 這些詞語,但是這裡是 native 報錯而出問題的庫在 JS 里,RN 肯定做了一些事。對著這個查詢 RN 的 error 機制,獲取到社區推薦的 ErrorUtil 全局對象 catch fatal error 的方式,於是下一步依然事實驗與實踐,發現起效。最後則把剛才的代碼配上前面的測試用例,寫成測試安心交付。同時把這個問題研究結果子樹,render 到自己的知識樹上,並把知識相應的關聯體系補上,比如以前這塊知識只是 redux-persist 是解決 store 數據持久化的,為了下一次打開還原現場。這次新增對持久化的數據做 transform 以解決什麼數據需要持久化,多少數據需要持久化,存儲校驗,異常處理等等。
2.3.7 總結
我們在解決問題的思維過程應該和 CI 流程一樣,一步一步非常清晰。不斷的遷移知識分析問題,不斷拆解問題到更小的原子問題,不斷對每個問題深化關鍵詞,不斷獲取此關鍵詞的信息並從信息獲取更準確的關鍵詞,不斷抽象解決方案模型做實驗驗證可行性弄清楚實現機制,每一步則注意 Single responsibility principle 的應用,最後過程中的知識增長需要把 diff 記錄進自己的知識樹。
2.4 敏捷主動學習案例
本節結合敏捷學習介紹主動學習 Redux 的案例,由於這裡篇幅會太長,只能濃縮介紹關鍵部分。如果讓你快速學會 Redux 並著手重構曾經的項目,我們會怎麼做?把 Redux 書和相關背景都讀一遍恐怕是來不及的,由於知識是有背景和上下文的,再講這個案例時我只能假定學習者已經深入理解 React,初步理解 Flux,此時學習 Redux。
2.4.1 帶著目標閱讀
閱讀是必不可少的,至少我要知道此技術提出什麼理念解決什麼問題。就如上一段所說,我們不可能從頭讀而是帶著知識經驗結合目標閱讀。學習新知識首先讀的一定是官方文檔的 getting start,以及文檔中的 demo,了解清楚這個知識屬於哪一個領域,解決什麼問題,背後的理念大概是什麼。帶著這幾個問題讀一遍官方的介紹,切記帶著目標讀,讓資料跟著你走,而不是一上來就看怎麼 install,或者深入細節。顯然 Redux 介紹一上來就說了提供一個可預測的全局 state 管理數據。解決的問題依然是 data-view-action,只不過每個過程匯流排化管理。接著往下看一點案例結合我們的經驗不難理解 Redux 是侵入了 React 本身的 props,用外層的 store 接管了 this.state,setState 方法變成了需要觸發被 dispatch bind 的 action,而後 reducer 接收而後改變了 state 而後被 connect 的訂閱者聽到影響 props 改變下一版數據的試圖。如果你對 React 本身很熟悉,這些在初步閱讀 Redux 官方文檔以及 demo 的過程就可以體會到。通過知識對比則更清晰,就是把 React 自己的 setState --> this.state --> view,父 state 通過 props 單嚮往下傳的模式,變成了 setState 的過程在外面通過 action-reducer 完成,state 變了則 connect 訂閱者會往受體注入 props。這些信息在初步閱讀中結合 React 的知識遷移就能大概體會到。最後把心得體會更新到自己的知識樹上,哪怕他只是我的聯想,未來會被更正。
2.4.2 實踐學習
計算機是工科,實踐和理論需要並行,互相加深理解。目前對 Redux 初步有點概念了,但是具體怎麼實踐並沒頭緒,此時要做的就是玩開源項目並自己做實驗。一般選擇官方推薦的學習項目,把項目拖下來後做一個分類。比如有的就只是 todolist,為了說明數據流的機制,有的增加了非同步事件處理,有的增加了全局數據緩存 feature。記住不要糾結 demo 的功能有多 low,關鍵是提取技術點,然後結合自己的知識樹互相對比學習。項目的學習順序一定要從越簡單開始往難走,越簡單越可以單元化的看問題,當你掌握了之後,以這個為基礎再去看比他技術點多一點的項目。學習項目中最重要的是,學習寫法並分析他的用意,遇到不懂的,立馬查補。比如對 ES6 的一些知識不夠熟練,看到某個寫法有點懵。此時應該立馬記下來,看看能不能大概查閱他的意義跳過先看,如果確實是一個重要知識點無法跳過,則必須弄清楚後繼續。這裡強調實驗的重要性,學習別人項目絕不能只看代碼,而是自我去做功能變異與代碼變異。功能變異是指增減改同類功能。比如他做了一個加法器,那我用同樣的方法在下面做一個乘法器,走一遍新 feature 的流程,不會就 debug, diff……代碼變異則更關注實現細節,比如你可以換個寫法實現某個方法為達到同樣的測試結果。
2.4.3 快速深化學習
實踐學習基本上是在別人的項目上做實驗,是需要忍過去的。因為你還沒有開始完成我們這節的目標親自去做工作。當你確實看了好幾個項目,並做過好些實驗了,我們可以開始做快速深化學習。這個過程是一個循環,參考理論和別人的項目來寫自己的程序。遇到不懂立馬中斷,回去做實驗查資料更新知識樹,深化完繼續前進。比較痛苦的是,往往一開始你的項目進度非常緩慢,查資料和做實驗的實踐耗了很久,但是我的經驗告訴我,知識的持續翻疊和聯想對比很快能讓你走出困境。
2.4.4 夯實基礎,緩慢遞增
這時候你工作的項目已經能應付,確實採用了一種社區推薦的方案重構了你的項目,配上了 Redux,也踩過一些坑,大概體會到它帶來的好處與麻煩的地方。此時最容易放鬆,但是千萬不能放鬆,因為你的理論和判斷能力還是很弱,換個業務邏輯也許又無從下手。但是,經過這過程,你對整個知識骨幹已經有理解了,對知識關鍵詞也很清晰。這時需要對知識做拓展,和其他知識做關聯,補充之前快速學習忽略掉的一些細節知識。這個過程是緩慢的,隨著時間越長經驗越足積累越深。
3.0 知識遷移
我們把學習知識比做一個疊紙遊戲,有的人在不斷學習的過程中,只是往上疊紙,那麼學習10個知識點也就10層。有的人則是把知識混合,相當於對摺了10次,最後哪個人的作品高度高呢?很明顯,能把紙做對摺的人是壓倒性優勢。很多知識他是有內在關聯的,當我今天聽說一個新知識,首先要對這個知識做歸類,一般就是屬於哪塊的知識,解決什麼問題,與現有的知識關係是什麼,然後產生新的疑問和聯想,然後再去循環研究。接下來從解決問題和主動學習兩方面講解一下。
3.1 解決問題
在解決問題時,很多思路並不是這個新知識告訴我的,而是我在其他地方的編程經驗告訴我的,於是我很自然的聯想到在當前的場景下,是否這個方法是否也會有實現,然後把未知問題做轉換。
以前在開發前端時,我會在啟動開發項目時做這樣的事:
APP=v1 PACKAGE_SERVER=... yarn run dev
其實就是啟動時給當前的語句配置環境變數,這樣我能在裡面能抓到 process.env,然後通過 Webpack DefinePlugin,傳遞到 JS 里,存放在全局單例 config 中,供其他 module 引用。
但是我們思考一下,如果這裡的環境變數非常多,那麼處理起來就會很難受,必須藉助 Dash 或者一些 Snippets 工具記錄。於是遷移一下在寫 Ruby 時用到的解決方案,叫 dotenv 他會解析 .env 文件里的環境,在載入時,他自動把 env 灌進去,對裡面代碼實現無侵入,好處則是我可以把這個 .env 文件 gitignore 掉,然後在本地開發時簡單編輯文件切換不同的 API 等編譯時環境變數。於是自然而然去搜索 .env in JavaScript 的實現,果然 JS 社區造就有人幹了事。於是立馬就引過來用解決問題。
這樣的例子還很多,比如我在做前端 route 切換動畫的時候,我會想的是以前寫 iOS 前端時,他有一個切換演算法很不錯,於是對著 Apple 文檔對著用 JS 實現一遍;比如我學習 React,看到一堆生命周期,就覺得非常眼熟,只要你學習過 Android 或者 iOS,生命周期就是最核心最基礎的東西,這些知識點都是相通的,可以大把節省你的學習時間;比如在實踐 Java 的 Spring 框架時,我也會和 JS 對比著想,了解了動態語言和 Java 這類的區別,比如對許可權的表達,對類型的深刻理解等等。
漸漸大家就會發現,各語言的設計思想就是在不斷借鑒與融合,對語言和框架的設計我個人始終它覺得是一個哲學問題。大家細心觀察,就會發現很多開源項目的 title 後都有一個 inspired by xxx, 這表明它也是受到了其他技術的啟發,在自己的項目中利用這些思想,並向原項目致敬。
3.2 主動學習
比如我今天學習垃圾回收(gc)這個知識。對這個知識結點的新增是在學 C 的時候就完成,後來學 Java 垃圾回收機制時增加了知識子節點,並和 C 做對比。最明顯的就是 C 里對象所佔的內存在程序結束運行之前一直被佔用,在明確釋放之前不能分配給其它對象;Java 則當沒有對象引用指向原先分配給某個對象的內存時,該內存便成為垃圾,這個垃圾能被自動清理。那下一個問題就是 JVM 怎麼做自動清理的,自然也就有變數生命周期、清理調度機制的問題。下一個問題就是了解清楚後,我要怎麼去節省 gc 開銷,比如就會有對象不用時最好顯式置為 Null 啊等實踐。
這些學習的問題同樣會帶到 JS 上,我也會擔心 JS 啊。後來知道 JS 也是屬於自動回收,就自然也有變數生命周期一說。於是就會去思考什麼情況變數算不用了,得出函數的執行結束,那怎麼算函數執行結束,閉包呢?一路往下想。JS 垃圾回收的調用者是瀏覽器,目前機制主要是按照固定的時間間隔周期性的執行,但是 IE 早版本就不是這樣,用的引用計數法,那麼當存在循環依賴就會出現內存泄漏,需要你手動置 null。在知識不斷聯想和遷移過程中,我就會去思考在 C, Java, Ruby, Python, Go 分別是怎麼做的,有什麼共同點,我要注意什麼。這次分享不對這塊深入展開了。
說到這裡大家應該深有感觸,當我把知識交織在一起,就完成了這個疊紙遊戲中的對摺,而不只是往上加一層,能迅速了解一個方面的知識,而不是一個點。
4.0 信息獲取與篩選
與以往的教科書或視頻的學習方式不同,前端開發從開源項目源頭迅速傳播過來,版本也以天的單位在更新,社區非常年輕,不斷湧現新的 issue 與新解決方案。這些知識被整理歸納成冊或者教學視頻是嚴重滯後的,我們需要更主動的獲取知識並自我歸納整理,本節重點介紹信息的獲取與篩選。
4.1 信息獲取
對開發而言,信息獲取的載體主要包括:
- 社區
- 博客&文章
- 文獻資料、書籍
- 社交
4.1.1 Github(社區典型)
非常感謝 Github 給我們營造了一個很好的技術中心,自然在這裡也是檢索信息最直接的途徑。我們可以利用github的項目搜索,代碼搜索等功能,對自己感興趣的項目進行檢索,觀察別人是如何實踐如何解決問題的。
code search exampleissue search example
天下代碼一大抄,看你會不會抄,會不會學會不會及時改。Github 上也有豐富的社區資源,會不會尋找到小夥伴一起學習,至關重要。這會在在社區化學習一節中深入講解。
4.1.2 Google
Google 的重要性似乎大家都明白,但是能用好 Google 的人卻很少,會不會找到合適的關鍵詞描述你的問題就是一個難點。在之前講過的案例中,關鍵詞是隨著我對問題的深入,不斷抽象成更貼切的描述原子問題的檢索詞。甚至我從信息結果中看到有價值的信息靈感術語拼接作為關鍵詞再度搜索,這將會是你綜合能力的體現。平時在訓練時,可以你可以利用迅速它幫助別人,解決各種朋友的問題,你立馬就是大神,因為其實身邊能用的好 Google,並能迅速總結概括的人真的很少。
4.1.3 Stack Overflow & Medium &翻譯
問答社區以及精品資料推送站往往也是重要信息渠道,社區提供很多機會讓開發者參與問答。尤其在寫代碼的時候,Stack Overflow 和 Dash 一樣必備,可以讓我迅速了解其他小夥伴問的同類問題,自己也可以提問。而 Medium、優秀博客則成為主動學習的重要讀物。如果你的英語不夠好,我們可以試試chrome自帶的翻譯功能,看了下面兩張圖,大家是不是可以感慨一下當下的技術翻譯組是不是會被google的AI翻譯給替代了,只要做簡單的梳理就可以很專業,在這個工具足夠強大的時代每個人都可以輕鬆閱讀。
原文翻譯4.1.4 書籍和文獻
書一定是必不可少的,我依然會去泡圖書館、下載電子書。但是讀書是有方法的,不一定有時間讓你從頭看到尾,需要有選擇性的閱讀,邊讀邊整理。比如我在學函數式編程的時候,我確實已經看明白了社區的案例,也已玩過很多 demo 並自己實驗,但是我對函數式思維基礎知識和架構的理論知識還不太理解,此時我需要去看一些相關書籍,比如函數式編程思維一書,邊看邊整理筆記,優化我的知識樹,讓我弄清楚它。資料來源其實非常多,比如大名鼎鼎的awesome-X系列
awesome-reactawesome-redux
awesome-mobx.....幾乎想到一個知識點,就可以去搜索關鍵字 awesome+x然後獲取相關學習資料,其中有paper,有document,有book和視頻,有知名開源項目,有創作者的blog等。具體怎麼讀怎麼整理會在後面章節介紹。
4.1.5 社交
社交大家想的最多的就是娛樂聊天,其實這是我幾年來最看重的學習方式之一。一個技術之所以能發展起來,對這些技術的貢獻者和社區的活躍分子是重重之重,他們經常在關注的,討論的,往往是這個技術的核心,畢竟他們會比我們更拿捏得准方向。因此我們需要去 follow 一些人,參與一些他們技術討論聊天室,比如 slack, gitter, discord 之類,在聊天室里多問多聽即可迅速成長。平時 follow 的重點則是灣區,矽谷的一些朋友,一些知名大公司開源組織,一些書的作者,一些技術的分享達人,一些技術的重要貢獻者,往往這些人也會被整理在 awesome + x 系列中的 who to follow 里。
關注的另一好處在於當我遇到難以解決的問題,我需要向社區求指點。這比我問大學老師或者高價找培訓班答疑等靠譜多了,社區有很多小夥伴都會很願意和你分享他們的知識,有together的感覺之後,很容易進步。
關於參與社區分享 meetup,建議大家可以參加一些高質量的分享小會。一些大會偶爾去一次就行了,它更多是概覽和方向,給小白普及知識以及介紹自己的成果居多,很多 google 也能搜到,未必能解決你實際問題,給你思路上的靈感。不過你可以獲得一些公司的技術路線。一些小的 meetup 、圓桌問答、技術讀書會還是很有價值。比如 aws 的架構師和合作夥伴論壇,我曾經去過幾次,基本就是解決問題為主,思路引領為主,我就覺得受益匪淺。這樣大家一起就某個具體問題展開討論碰撞,會對學習直接提供幫助。
最後必須補充一點,國內的技術社區最近萌生了一個現象,大量技術網紅大v,自媒體技術人一夜之間如雨後春筍般冒出,似乎我們都在積極的分享技術。這在一定程度確實提高了大家對社區和知識討論的關注度,確實是一件好事。但是另一方面我也看到了大家的浮躁,以及為「名聲」故意做出的一些不應該出現在技術圈的行為,比如惡意刷粉,技術文以及項目的抄襲,言語輕狂跑火車,以及一些粉絲們的無腦跪舔,神化一個技術的極端現象。我很遺憾以前端開發居多。也許有人會覺得這是走捷徑上位的方法,這是非常危險的信號,需要引起整個中文社區重視的,不然可以想像未來會被污染成什麼樣,還會有人好好寫代碼嗎。
4.2 信息的篩選
在上述那麼多信息獲取方式中,我們已經得到巨量的信息。這可不是好事,信息越多,篩選信息就成為了最重要也是最難的事。我們的思路是尋找搜索項然後做類 SQL 的思維過程。然而這些搜索項的建立是比較難的,他會隨著我對一部分信息的理解程度變化而變化。這裡我簡單講自己的做法,我會帶著不同的目標看待每次的篩選過程,比如我需要有代碼 example,我需要時間是比較新的,發布在比較權威的資料站,大家有討論……
這些表層硬指標可以簡單做一層 filter,第二次 filter 則要從我對知識的理解而定,甚至從某些信息上發現這此搜索方向就有問題,那就立馬切換。當然如果我確實找到了我要的資料集,接下來要做的就是 map,把每一個子資料中我要的部分 highlight ,加入我自己的想法,同時去掉無關因素,最後則是 reduce,把它匯聚成我要的知識集。
5.0 新形勢下的閱讀方式
這節讓我們深入思考面向未來的閱讀方式。
5.1 邊讀邊整理
我們需要有邏輯有目標的去讀書,簡而言之就是資料跟著你走,而不是你跟著資料走。舉個例子我在讀函數式編程思維一書的時候,我很清楚我的目標就是了解函數式編程的核心思想、核心概念、各種寫法,及解決的問題。於是我在讀書的時候也會跟著自己的主線選擇要看的內容,當然有時內容也會讓我增加預定的主線節點。看技術書,其實和信息的篩選挺像的,不僅僅要讀完,更要寫完。利用 margin note 這樣的工具,我邊讀邊做摘抄,寫自己的評論,做思維導圖。例如:
margin-note閱讀demo對於好的資料集,我們還可以利用Flash cards把一個問題做記憶卡片,不斷溫習。當然現在,對於網頁的內容,比如medium上的知識,我們可以利用 chrome的diigo插件做一個在線annotate,或者利用他保存成PDF然後交給margin note處理diigo demo當然,如果是技術書就會有很多實踐類內容,更需要做實驗,比如書上只是用 Java 語言為例講解了 curry 的概念,那麼我一定會在邊上把 JS, Python 的實現補上。看了這些後,有沒有買ipad,瘋狂買電子書,轉換電子書的衝動?
5.2 同時多本書一起讀
同時讀幾本書在以前看來是很難,然而現在對同類書籍用的非常多,比如學習函數式編程的時候,我就同時開了函數式編程思維,JS 函數式編程,Java 函數式編程。然後我對這部分採用敏捷學習法,其實就是對這些我要學的知識樹不斷做 merge,並關注 diff 和互相的關係。比如,在 Java 函數式編程里提到了範疇學和組合,但是在那本書只是提及並沒有深化,於是我去找函數式編程思維和 JS 函數式裡面對這塊做了解釋,那麼我就會整理在一起。這樣的案例還很多,比如同樣搞語法糖,我在學習 ES6 語法時看到了 arrow function,我立馬去找 Coffee Script,因為在 Coffee 里有胖箭頭和瘦箭頭的區別,主要就是 bind 不 bind this 的區別,而 ES 只有胖箭頭,當我在一個閉包里要創建非 bind this 的方法還得去用老方法。
margin note 書籍分屏6.0 社區化學習
之前我有提到過社區的重要性,本節更深入的講解一下。
6.1 你會用 Github 么
社區並不僅僅是被動解決問題,更是主動學習的好地方。
6.1.1 Github 項目生命周期
簡單的說,一個新的 feature 會經歷:
- Issue 被提出,大家參與討論;
- Issue 被分配者或其它開發者向約定分支提 PR,並指向這個 issue;
- 之後大家都可以 review PR,對其中的代碼做疑問;
- 隨後有許可權的 core 開發者則可以確定是否合併;
- 合併完後看放在哪個 release 發布;
- 作為管理者,則會通過 label 排好 issue 的優先順序,並建立好 milestone,然後利用 kanban 管理進度。一旦有人提了 PR 需要去審閱他的功能,代碼風格,代碼文檔,代碼測試,並關注一下 ci 反饋的結果,此時也會在 issue 下面和大家做更深入的討論。
- 最後,當一個 milestone 包含的 issue 都被 kill 掉了,就會新建 release 分支,然後在上面做一些 Bug fix,改版本號,寫 changelog,最後發布到 master。
6.1.2 學習
這些周期中,我們主要參與的是:
- 對 issue 的深入討論
- 對項目的直接代碼貢獻
- 代碼的 review
Review 不僅僅只是糾錯,更是一種學習。我們可以了解別人的寫法用意和實現方式,我經常在社區 review 別人的代碼,很多時候只是提問 + 學習,效果絕對比自己讀源碼好得多,畢竟這裡有大家一起討論。
Issue 則是讓我知道大家會遇到哪些問題,現在是怎麼解決的,往往能學到很多知識。每個 Issue 的建立初衷一般都是為了得到 PR 改善這個問題,當然很多問題不一定需要 PR 就被別人解決了或者被關聯的 Issue一起解決。
舉個例子,比如我在學習 React 的 createElement(null) 在 DOM 中的表現,當然我可以做實驗,我也會去 React 的 Github 搜一下 release log和相關的issue,於是我發現在15.0.0的版本中提到。
github pr example於是我追溯進去看了一下 PR 和大家對此的討論 [Use comment nodes for empty components],通過它我又知道了這個 issue 的歷史,曾經 chenglou 同學在解決 [React.EmptyComponent (NullComponent?)] 這個 issue 是通過返回元素,但是這導致了 nest dom 處理時的兼容性問題,於是15.0.0版本返回<!-- react-empty: 1 -->這樣的注釋節點更 make sense。搞清楚了理論後,我就自己做實驗,確實發現如此。為了更進一步學習,我去看這個 PR 的 [file changes],看看人家是怎麼實現的,有沒有我能學習的。最後把這些知識點 persist 進我的知識樹。有的同學問我要不要去啃源碼,其實對著源碼啃是很難的,也很枯燥。但是對著 issue 以及 PR 搭配我們的 debug 工具去啃,邊實驗邊參與討論,才是社區化敏捷學習的精髓。
對於代碼 review 目前除了我們可以在 github 參與review別人的代碼,我們也可以在stack overflow review中提交自己的代碼給別人 review 或者 review 別人的代碼。通過對代碼和其實現的預期的探討,就能碰撞出智慧的火花,互相取長補短。
6.2 你會聊天么
社區除了 Github 陣營外,團隊有比較知名的 Slack, Gitter, Discord 甚至 Reddit 等討論區。個人則是 Twitter 和 Facebook 以及自己的博客居多。往往我們經常在用的技術都有自己的官方討論區。
reactiflux facebook 官方技術chat社區mobx Gitter社區elm slack社區比如上圖中看到 react-motion 的大神 chenglou 正在回答大家的使用問題,並且我們仔細看左側的列表,會發現 React 經常遇到的問題都有相應的解決方案。於是還等什麼,趕快敏捷學習更新你的知識樹吧,聰明的人搜搜聊天記錄就能學到不少!當然這裡不得不提,聊天是有禮儀的,我看到 Gitter 上一些國人開發者有素質低下的溝通表現表示很氣憤,這裡的技巧有機會再給大家詳細分享。
6.3 你知道 follow 么
有的同學很迷茫我每天該看什麼,結果就看知乎的心靈雞湯,或者是脈脈里無聊的工資的討論。真正需要看的並沒看,只是捧著技術廁所讀物在那邊反覆咀嚼,還覺得是金子。那麼我們該看什麼?信息推送站比較知名的是 Medium, Hacknoon 等,個人部分則 follow 那些大神的 Twitter 就好。這裡 RSS 閱讀器就非常重要的,我習慣用 Reeder, Chrome 的話用 Panda,用他們訂閱一些感興趣的 feed,包括 Medium, Egghead,掘金以及一些 follow 的朋友的博客。
chrome panda tab examplereeder 訂閱example7.0 工具&實驗田
7.1 工具
對工具的依賴其實很多同行有自己的看法,我是一個比較務實的人,我覺得工具就是為了提升效率的,多用也沒什麼問題。
必備開發輔助工具比如 Dash,幫我快速的查詢 API 以及使用 Snippet;SnippetsLab,對經常用的代碼片段做一個 notes 管理;Alfred workflow 之類,幫我做一些自動化的操作; Kaleidoscope 做一個文件 diff 工具;marginnote, xmind,freshcard, lucidchat 等學習繪圖工具;還有 Chrome 上的一堆插件等等。
比如,我經常會用 kaleidoscope 做 diff,之前我在逆向一個編輯軟體的工程文件格式時,我就會去操作這個軟體,然後對每一個版本的 snapshot 做 diff,以此觀測該軟體的演算法,我在分析 ci 報錯時,我也會拿成功和不成功的兩份 log 做 diff,判斷出現問題的地方。
前端測試ci成功-失敗對比比如,我經常會用 Dash 的 snippets 做一些 Shell 命令的快捷操作。
dash snippets demo上圖中,我可以簡單在shell里打 delete docker container,就可以給我反饋這一長串刪無效 container 的方法。
同樣,我使用 SnippetsLab可以收入經常用但容易忘的一些snippets
Snippets Demochrome插件部分就舉一個常用的例子吧,開發前端,經常會遇到跨域問題,此時我們可以簡單的用mod header插件修改request和response的header。
主力要注意的是,插件用多了就還需要一個管理插件的插件,管理好每個插件每個profile。
chrome plugin demo
這篇分享並不是工具篇就不再深入,具體還有哪些可以去看 awesome-mac,我只是讓大家知道工具在敏捷學習中的意義。
7.2 實驗田
當我腦海中有一個技術思維或者從資料中獲得一些信息,我並不是很確定他是否正確,我需要快速的隔離問題並得到答案。於是我會利用工具建立自己的實驗田,實驗環境是隔離於工程並 mock 完需要的上下文或者把工程 mirror 後作為上下文變成一個獨立容器,或者利用 online 的實驗田,例如 codepen, 在上面搭配測試寫實驗。例如當我在做移除 react-auth-wrapper 地址欄的 redirect 參數時。我把問題歸結於 hack 掉他的 push 配置,寫一個 util function,接受一個 url 參數: http://www.coding.net?aa=1&redirect=xxx...然後丟掉 redirect 的信息並返回。這些就可以隔離在實驗田做完,寫完測試,然後把正確結果打 patch 丟回自己分支。在做實驗或者單元化開發的過程中有兩個核心點。1. 問題的模型抽象與上下文mock 2.關鍵在於「creator should be able to see what theyre doing immediately」理念。對於第二點很重要,編程應該是創造,創造你就需要更多思路,思路來源及時的看各種反饋,最快時間判斷思路是否可行是否符合預期,並最快時間切換思路。這裡提供這個觀點的一個介紹 BRET VICTOR – INVENTING ON PRINCIPLE
演算法實驗田這是一個我經常用的演算法實驗田,我們可以在上面配置常用差不多的近似演算法,並第一時間根據圖獲得靈感
regex實驗田bash 實驗田8.0 時間管理
敏捷學習的關鍵詞是敏捷,要求在短時間內掌握知識,解決各類問題,同時不斷加深知識點的深度和維度。這其中就需要對時間做管理。如果我看一個資料太陷入細節,一層一層往裡追溯,最終的結果就是在那一個問題耗時太長,工作任務完不成了。於是除了聰明的學習外,我們還要管理自己、約束自己的學習。比較推薦的做法是使用 Omini Focus,學習一下 GTD 時間管理法。對要做的知識學習事件做一個整理,寫進 inbox ,並對每一個事項做一些配置,包括輕重緩急,需要用到的資源、上下文,需要協作與否等。
9.0 畫圖與做題
9.1 畫圖
第一版中我沒有提這塊。後來和讀者交流下來,覺得這快必須加上。作為程序員,必須有對問題的抽象和畫圖打草稿的能力,這裡的畫圖指的是對自己的思路做整理,對代碼邏輯做整理。最簡單的例子就是UML圖,當面向對象編程,我們對代碼需要做一定程度的分析,了解設計模式,清楚的知道自己在做什麼。現在非常好的工具能幫助你,比如starUML,process on,lucidchat,以及我最喜歡用的plantuml,除了它用簡潔的類markdown語言表達UML之外,他還允許你定製UML方言,當然就可以用它定製一套react的UML規則,見plantuml in react
react uml demo當然很多時候,函數式編程我們可能更需要 viskell函數式類圖的幫助。image.png有時,我們也要學會用更自然的語言來表達需求,比如前端開發是的behave driven,也就是用戶故事主導,所以一般用Gherkin syntax來表達用戶故事和測試。一個feature的demo9.2 做題
刷題理論相信很多應聘者早已爛熟於心,以至於面試前不刷(背)幾遍leetcode不好意思出門,當然這個是有必要的,至少帶著一個我是準備過的誠意。類似的網站挺多的,做得比較好的還有kaggle,lintcode等。當然平時學習時,做題則是為了對一個課題更深入的理解,其實就是製造問題和解決問題的過程。我們也許應該做的是對問題的歸類,並發現其中的規律自我出題自我解決。如果有朋友,則可以互相出題互相解決。利用互聯網,我們也可以對一個issue查詢別人對他的各種問題和解答,並積极參与討論。
10 總結
新形勢下我們需要更敏捷的學習, 利用一切資源快速更新自己的知識樹,為了達到這點我們就必須要學會知識遷移能力,用對摺的方式交織性學習而不是孤立的往上鋪紙。新形勢下,我們需要利用好新媒體、新搜索工具以及社區,在得到大量信息後要學會篩選和整理。為了更深入的理解原理,我們依然需要讀書,只不過讀書應該帶著目的讀,邊讀邊整理,建議多本書對比著讀,對比著整理。當我們已經整理了很多知識,要積极參与社區的活動,去和他人分享自己的實踐,同時也從他人那邊獲取有價值的信息。得到各類信息後,需要在實踐中學習和檢驗,我們時不時需要在實驗田中做實驗,檢驗某一個方案是否可行,把可行的有測試保障的代碼很自信的搬回我們的倉庫。這一系列過程,我們都需要敏捷,必須對每個學習過程做好時間管控,不能深陷其中耗時太久。
關於語言能力,由於當下技術大多是從開源英文社區直接傳遞過來,以上所有的步驟都離不開英語,所以對語言的基本閱讀能力是敏捷學習的前提條件。如果你的語言不是特別好,我們也可以藉助chrome中自帶的google強大的翻譯功能輔助幫助你理解,目前google的翻譯越來越厲害了。
關於技術學習,並不能只學 JS,其他的編程語言,都需要學習和實踐,只是以一個語言為精,這樣才能更好的做知識遷移,遇到新知識才會有聯想有感覺。前端有太多的坑,需要一直跟進社區,保持重構,以及創造各種適合自己的實踐。別人的實踐很多情況只適合他們的業務場景並不具備通用意義,前端的庫都只是依賴包,也就是一些工具,提供了一些api解決特定的問題,可以說大家每個項目都能誕生幾個庫。所以用或不用,用部分然後自己改還是用全部,怎麼組合,還是需要大家根據自己的業務需求而定,不過優秀的開源項目給我們提供了很多實踐參考,讓我們思路更開闊。關鍵還是這面向未來的敏捷學習能力讓你任何時候立於不敗之地。
推薦閱讀: