看到釘釘的功能愈來愈多了, 前段時間忽然想起來之前簡聊的事情來.
當前公司跟釘釘的一些風聲, 具體也不清楚, 到不少年後才聽到了收購的事情.
Slack 具體的玩法我並不清楚, 可是釘釘當前延伸出來的功能給我一些感觸,
當年簡聊在功能的擴展來講缺了太多圍繞聊天的擴展創新, 玩法也不溫不火,
固然若是當時有條件一步一步有條理地往外擴展, 也說不許後面是否是會有機會.
我當時也基本上一心在處理 React 的事情, 極少對整體的產品形態提出想法.css
單純從前端代碼來講, 咱們當時也算很早嘗試 React 而且穩定下來了.
通過這些年, React 改變也很多, class 組件到今天用的人比較少了,
並且隨着我到不一樣的公司遇到不一樣的場景, 代碼的側重也改變了很多.
再當時來講有些東西看着是迷霧, 還要不斷前行探索才能知曉,
如今回頭看的話, 是比起從前清晰了不少, 那些說是彎路說是妥協的東西.前端
CoffeeScript 在當年算是 alt js 當中最成熟的一門語言.
就功能來講, 它僅僅是讓人更容易些 js 的語法糖而已.
跟 ClojureScript 相比, CoffeeScript 沒有原生的不可變數據支持,
跟 TypeScript 相比, CoffeeScript 沒有類型系統加持.
上面說的這兩個 ReasonML 都有, 惋惜到如今 ReasonML 給人感受都還不夠成熟.react
就功能來講, CoffeeScript 確實是殘缺的,
當時雖然簡歷了代碼規範, 可是很大程度仍是依靠人自覺把格式統一塊兒來.
而如今, 不管 ClojureScript 仍是 TypeScript 都是經過自動化格式工具寫的,
Prettier 的使用基本追平了的 CoffeeScript 帶來的編寫效率的提高,
並且就團隊代碼習慣來講, Prettier 比起 CoffeeScript 固然是好多了.
不可變數據的殘缺, 致使了一系列的奇怪問題, 後面再說.
沒有類型系統確實也不如 TypeScript 適合多人團隊.git
不過當年 ClojureScript 跟 TypeScript 比較仍是不多出現的,
我印象裏公司卻是有那我的在研究 TypeScript 這些了,
而 ClojureScript 在當時工具鏈也還有各類問題, 即使今天也有一些.
不過即使是今天, 因爲招人的緣由還有工具鏈特殊, 我仍是不以爲能在多人開發當中用起來.
ClojureScript 更多的, 仍是讓我站在 FP 語言的角度審視 js 生態缺失的功能.程序員
前面說的 CoffeeScript 或者 js 沒有原生不可變數據的問題,
按照 React 的設計, 不可避免須要用到不可變數據, 不然優化起來會很繞.
如今來講, 有了 Hooks API, useState
把數據一份份拆開了,
這樣每一個數據更新的時候, 算是經過替換達到了不可變數據的效果,
可是也存在過於分散且觸發屢次更新的問題. 仍是有時候須要不可變的對象來處理.github
當時用 Immutable.js 的問題主要就是學習成本,
我最開始從瞭解這個東西到熟悉了敢用在項目當中也花了不短的時間.
除了寫法比較繞之外, 對於前端來講這也是比較陌生的一種用法.
後來即使我用了, 我印象裏同事仍是有很多的抱怨的, 比較維護着累.
這東西並非不能掌握, 可是當在 js 當中寫起來就是挺煩的.
並且對於第三方代碼來講, 只有 JSON 對象時它們接受的數據格式.
那麼 Immutable 數據就要在咱們代碼當中轉來轉去, 維護就很累.redux
我後面用 ClojureScript 就不是這樣的. Clojure 默認用不可變數據.
這樣我在開發當中幾乎無時無刻不是直接用不可變數據在編寫邏輯,
就是說沒有多少須要轉換的場景, 腦子裏對數據的理解統一併且清晰.
Lodash 當中 updateIn
setIn
就是 Clojure 中很日常的用法.
這些 API 在 Immutable 當中更是比比皆是, 只是說 Clojure 作到了語言核心中.小程序
如今咱們用的是 Immer, 語法比較貼近 JavaScript 原生的操做.
就推廣使用來講, 得益於語法簡單, 並且加上 Hooks API 封裝, 容易多了.
不過從 review 的結果來看, 仍是偶爾會出現遺漏.
Immer 畢竟是用了 freeze 強行設定數據不可變, 但看着輸出跟 js 又沒區別.
就無法保證在場景當中串來串去不會漏掉. 固然也還比如較少.segmentfault
不可變數據這個事情, 四五年了, 縈繞在 React 社區仍是沒有消散掉,
我以爲這個做爲長期的一個方向, 也不會很容易就有完美的結果了.
若是說期待, 我但願 ReasonML 到時候能把這事情統一掉.
由於 js 老是要兼容老代碼的, 無論怎樣引入不可變數據, 心理負擔總會在.後端
簡聊用的 CSS 預編譯方案是 LESS, 當時 teambition 統一的習慣.
總的來講我對這種層層重置的 CSS 規則也沒留下多好的印象.
當初 Vjeux 那個 CSS in JS 的演講發佈的時候我是深有感觸的,
用了兩三年的預編譯, 大部分的侷限性基本上也碰到了,
我是很期待直接用上 CSS in JS 的方案, 作細粒度的包含代碼邏輯的控制.
就當時來講, 我以爲方案是不夠成熟的, styled-components 也以爲走得太怪.
在我本身 cljs 的方案裏邊, 我後來用的是 inline styles, 局部場景夠用,
而 inline styles 最主要的問題是瀏覽器前綴和僞類的控制, 無法用在產品當中.
後來在朋友當中瞭解到 emotion 的方案相對成熟, 我就嘗試了一下,
我最後仍是沒有用 styled-components 那樣的寫法,
對於 emotion 我只是當作定義 className 寫法來用, 倒不是新版本官網推薦的用法.
若是參考官網, 反而咱們的寫法顯得並不規範, 也很差配合官方 Babel 的優化.
只能說堪堪到了一個解決掉我認爲痛點問題的狀態,
let styleA = css` color: red; ` let styleB = css` background-color: blue; ` <div className={cx(styleA, styleB)}>red</div>
可是這也倒是幫我規避了不少的問題, 也由於是變量因此很好用代碼進行控制.
問題主要在團隊當中, 以往 LESS 的寫法是你們都熟悉的, 有一套習慣.
加上 emotion 官網推薦的是 styled-components, 就形成了不統一.
而個人話沒有多少動力去推進這方面的改變了, 這些涉及到不止一個改變的地方.
簡聊遺留的代碼中組件複用的代碼使用的是 Mixin.
實際上能抽取出來的複用的方法並很少, 並且維護性並不算好.
在 Class 上動態注入的方法, 維護當中基本靠約定, 不容易排查.
Mixin 的用法後來對着 createClass 被廢棄也跟着廢棄了, 改成單繼承.
中間用高階組件進行邏輯抽象的事情你們都經歷過了,
如今的結果也都知道了, 隨着 Hooks 出來, HOC 基本沒人說了.
當初 HOC 咱們也試着寫過, 我仍是參考着同事的代碼寫的,
但我真心以爲 decorator 的代碼很容易寫出問題來, 並且我很容易漏掉各類東西.
就邏輯複用來講, HOC 主要是類庫做者用, 業務開發幾乎不會去亂搞.
如今有了 Hooks 再去審視當初的 Mixin, 就以爲很原始.
Mixin 幾乎只是粗暴地講方法一個個剝離到單個的文件, 事情只是作了一半.
飯館 Hooks 封裝的邏輯較爲完整, 複用的場景也多, 也認爲較爲可靠.
就咱們如今來講已經大量使用 Hooks 用在業務抽象當中了,
因此 Hooks 是實實在在對於產品業務有不少幫助的, 而不限於類庫開發者.
回顧簡聊的代碼, Mixin 也算抽了二十多個吧, 這數量是不如 Hooks 的.
簡聊代碼當中主要用 PropTypes 進行的組件參數的管理.
後來這些東西漸漸廢棄掉了, 有點不知不覺的.
如今應該大部分人都使用的 TypeScript 聲明組件的類型了吧,
有了類型的輔助, 即使沒用動態的參數校驗, 也很難出現那方面的錯誤.
就這一點來講 TypeScript 爲前端帶來了不小的改變.
action-recorder 我在前面有留文章詳細講了, 是我改出來的名字.
原型的話, 是 Elm 的 hot code swapping 吧, 還有 Redux 的調試工具.
當時具體時間我記不清了, Elm 那些 Demo 你們應該都看過, Redux 也有些風聲,
加上當時社區有很多人在追這方面的工具鏈, 我也就本身作了一些嘗試.
actions-recorder 最初由於實現的問題, 在內部試用性能還挺差, 不過還好立刻修復了.
actions-recorder 是爲了簡聊定製的類庫, 基於 Immutable.js , 算是耦合了.
後來 Redux DevTools 大概作了更完整的版本, 我不清楚用的人有多少.
actions-recorder 的核心原理是全部的 actions 都存儲下來,
後續能夠經過切換 actions 和 updater 從新計算, 回放整個應用的狀態.
這同時也推導出一個要求, 就是 didMount 等等操做時, 不能夠發出 actions.
這個要求當時對我形成了不小的影響, 畢竟在 didMount 時 dispatch actions 挺常見的.
爲了迎合這一套方案, 我花了不小的心思對相關邏輯進行了不小的改造.
特別大的一塊就是切換也沒請求數據的行爲, 都跟 didMount 脫離了.
actions-recorder 這套重構當時也是也沒有太多的難點, 逐漸就完成了.
從效果來講, 我認爲仍是不錯的, 請求從 didMount 分離, 就是從 render 分離,
數據在路由切換前就開始請求, 時間上提前了一些, 界面上也避免了多個分離的 loading.
可是從壞的一面來講, 這種約束對於後續的開發維護來講是不友好的.
我印象當中我同過後面補代碼的時候比較容易會破壞這個規則,
並且有了這些限制, 設計邏輯也累了許多. 誰不喜歡在 didMount 直接請求數據啊.
站在 actions-recorder 的角度, 回放 actions 好處比較明顯,
當時有幾個很差排查的 bug, 在 actions-recorder 的工具當中很容易定位,
由於每一個 action 先後的數據狀態都在, diff 能夠直接看, 很明確.
可是一旦 didMount 時會發送 actions, 就致使回溯時總會有重複的 actions 發出,
這些 actions 也很差追蹤, 那麼調試的方便就被破壞掉了.
這些好處對我而言有用, 可是對團隊來講卻未必真的好, 如今看看確實問題太多.
後面我用 ClojureScript 寫的代碼當中, 對於 Respo 的小應用, 時間起來比較輕鬆.
因爲 Respo 我在設計實現時基本上杜絕了 didMount 發 actions 的可能性,
因此自然就的是知足前面的約束, 而我用 respo-reel 類庫很容易作到這一點.
另外 respo-reel 跟 cljs 純函數的特性配合, 更新 updater 的效果也比較好.
某種程度說這個就是另外一套獨立的技術路線了.
就目前公司中而言, 我徹底放棄了 actions-recorder 的方案(也沒有用 Redux 那套).
當存在大量的伴隨 didMount 進行局部的 actions dispatching 的時候,
actions-recorder 的方案就顯得沒有多少意義, 特別是還跟 Hooks 相關聯.
actions-recorder 也要求數據儘可能存儲在全局, 這一點在當前的場景也不合適.
特別是我的開發大量的子頁面, 各自有各自的數據, 跟簡聊的場景就很不同了.
雖然我最初有考慮複用部分 actions-recorder 的思路過來, 但仔細想總沒有多少好處,
只能說是場景不一樣, 加上那些前車可鑑吧.
脫離 Elm 的全局狀態的方案的話, actions 對我來講就沒有太大的意義了.
若是是 ReasonML 那樣經過類型能定義 action 方便作 switch 的話倒還好,
在 TypeScript 中用 Action 效果並很少, 並且還多一層抽象.
反而不如直接用方法去操做那少有的全局狀態來得好維護了.
簡聊的場景比較特殊一些, group 和 messages 是全局的數據, user 也是.
一方面 WebSockets 會推送新數據過來, 這些默認就是全局處理的, 而不是局部,
另外一方面聊天應用就是圍繞消息和用戶展開的, 消息全局存儲也有意義.
目前公司的場景是各類頁面表單圖表基本上是各自從服務器請求數據,
雖然形態上是單頁面應用, 實際上子頁面之間或者全局複用的數據能夠說少得可憐.
這樣大量的狀態也就是在各個組件本身去作維護了, 而且狀態也很是多.
截然不同的場景. 固然狀態不少對於調試來講也不大友好. 可現狀就是這樣.
因爲前面說的 actions-recorder 的限制, 就要去全部全局狀態都要在 store 統一維護,
比較重要的一塊狀態, 就是路由狀態, 當時我也走到了 redux-router 的前面.
我設計了 router-view 模塊, 改變了路由在 React 中的角色, 使用全局數據控制.
這是個定製化的方案, 也不必展開說了.
另外這個 router-view 跟後來 Vue 說的 router-view 還不同, 具體也沒看.
不過有一點是後面影響我比較深的, 就是我認爲 react-router 界面和路由耦合頗有問題,
router-view 是定義 JSON 的路由規則對路徑進行解析, 而後交給頁面渲染,
這根 react-router 經過 Context 層層處理的方式徹底不同.
router-view 應該說更接近於後端解析和處理路由的方式, 先解析, 再當作數據處理.
解析完成之後, 處理數據對於 React 來講是很是清晰也很好排查問題的.
這個在 react-router 那樣的耦合嚴重的方案當中就顯得分散並且瑣碎了.
這個思路致使後面爲了解決公司面對的嵌套層級過深難以維護的問題時, 我直接選擇了改換路由.
ruled-router是從 router-view 延伸的對於嵌套路由作更深層適配的方案.
後面發現配合 TypeScript 的類型還能作不少的定製, 就繼續深刻作了代碼生成.
關於細節, 前面發文章講過, 再也不展開了.
但這邊也還有個問題, 就是定製過深以後, 整個方案徹底是自成體系了,
這個對於招聘進來的新人來講, 首先有一個學習門檻的問題,
再者雖然跟 TypeScript 作了配合, 可是有些地方並不很直觀, 有一些思考的成本.
這個畢竟不是社區大量的投入打磨的方案, 也沒有大量的文檔教程支持, 致使局面有點奇怪.
若是是 Facebook 或者阿里, 搞出一套方案, 是有能力往整個社區推廣的,
而咱們做爲小廠, 本身用就是本身的培訓成本, 徹底須要本身操心了.
請求這個坑也算是針對簡聊的場景專門設計的方案, 來源是 Netflix 的 data graph.
Netflix 具體細節我記不清了, 當時有個演講專門說的.
思路跟 GraphQL 相似, 也是方案自動處理請求的依賴關係, 前端把相關依賴整合起來.
當年 GraphQL 很是不完善, 並且沒有跟對簡聊作數據推送的場景,
通過這麼多年了, 社區不少公司跟進了 GraphQL 的方案, 作了不少探索.
我所在的公司後端是 Go, 並且業務較重, 我這邊也就失去了跟進 GraphQL 的可能.
當前使用的方案較爲原始, 可是也儘可能在用生成 API 代碼的方案配合 Hooks 作一些抽象.
簡聊當時的數據依賴的抽象很稚嫩的, 也是由於場景簡單, 因此走下去了. 可維護性也通常般.
從後面 GraphQL 的路線進行對比, 很明顯, 數據抽象這個事情須要後端幫忙.
後端微服務之間很容易經過多個請求進行數據聚合, 性能優化辦法也不少.
這些等到前端再想辦法作抽象的, 已經算太晚了, 限制也不少, 效果也很弱.
但整體說我認爲數據自動處理依賴在將來也還會是會被常常觸及和深刻挖掘的場景.
GraphQL 不是最終方案, 至少咱們的場景就不夠用. 固然更多仍是靠後端了.
簡聊由於是聊天室, 當時對於 Form 的需求不多, 或者說只有特定的一塊,
當時的需求是後端想要經過 JSON 配置表單, 前端渲染就行了.
因此當時給出的也只是 React 根據 JSON 作簡單的 Form 響應的方案.
後來到了別的公司, 接觸到後臺管理大量使用 Form 的場景, 我發現本身忽視了這整理一大塊.
我這邊後來開始了 meson-form 模塊繼續針對業務場景定製 Form 的方案.
這就跟簡聊當時面對的簡單的場景很不同了, 並且 Form 後面還會有各類重邏輯的變種.
簡聊那段時間讓我造成了一個觀念, 就是架構調整不多會被分配出時間來作,
因此基本上就常常要咱們本身私下調研方案還有見縫插針一點點去作了.
在執行層面上就基本上這樣了, 並且要一點點拆成小的任務, 否則很差推動.
而大的重構並非說沒有, 可是時間上只能儘可能本身去勻本身去控制了.
而當時因爲 React 生態各類東西不成熟, 並且有時候直接破舊立新, 就比較被動,
我爲了能爭取到主動, 花了不少心思, 可是最終反而開始脫離大部隊的傾向.
如今的借我就是我倒向了 ClojureScript, 不少方案脫離社區更加嚴重了.
我在餓了麼的精力, 如今我也不太肯定, 大公司對技術研究和投入能作到怎樣的節奏.
就我在小公司的話, 因爲場景和業務相對單一, 我要花很長時間面對一樣幾個問題,
因此我能夠花不少零碎的精力定製出相對深刻的方案, 考慮的場景也單一一些,
而在具體操做的要儘可能考慮和已有方案短時間共存長期儘可能替換的思路,
在後續的開發當中穿插着逐步把老的方案和實現一點點替換掉, 或者乾脆不替換.
這個在具體實行的時候是很是瑣碎的, 但涉及的代碼也不會少.
後面我看到社區別人給出的方案, 特別是大廠給的方案, 封裝作得較爲完善甚至到多餘.
就技術方案來講, 我不多能給出那麼完善的方案, 由於打磨的時間和需求都相對少,
個人方案服務的人羣也沒有那麼多, 公司也勻不出那麼多時間讓我專心去作.
但換個角度說這樣持續演進並且封裝不完整的方案, 對於他人接手來講顯然並很差.
這個方案須要我持續 review 和提醒保證執行不出錯, 而不是別人比較容易加入維護和擴展的.
這些事情提及來是要我做出轉變, 規範和嚴謹, 但在個人角度條件又不夠充足的.
未來說若是我去了大廠的話, 是否會有足夠精力在一塊東西上打磨呢, 我內心是有問號的.
但如今再換個角度來講因爲我大量基於 ClojureScript 的研究和展開, 能夠說自成一體了,
這些方案和 JavaScript 內在的那些偏離還有糾葛, 也讓我操不少的新,
操心的結果對於 ClojureScript 受衆之外的人羣可能還不大被理解,
就這個事情算下來也是讓我比較有的頭疼的, 也不大樂意到被糾結在這個上邊.
簡聊的代碼多年前我就沒參與了, 並且公司轉向其餘方向, 後面你們都知道了.
我當初離開的時候除了原來幾個緣由, 也由於當時 teambition 仍是 Backbone 爲主,
我心理上比較反對在享受了 React 的方案以後再去體驗一遍 Backbone 開發的痛苦,
並且在 FP 路線走得太遠特別是孤立行走太遠的狀況下,
加上 leader 們也不會給我足夠支持去推進 React 方面的事情, 我也有點以爲無力.
後來 teambition 轉 React 的事情知道的人不少, 中間的坎坷也很多,
當時我在一家用 Vue 的公司本身心思也在 ClojureScript 上邊, 只能遠遠圍觀了下,
雖然過後有打聽過, 可是也沒機會跟前同事深刻去談那些東西, 後面關聯的少了.
平心而論雖然簡聊場景脫胎於 teambition, 可是後者數據規模複雜太多,
原先積累的那些工具鏈和方法論, 是否能在其中應用, 我內心是沒底的,
並且 Lisp 程序員這種跟着場景作定製的心態, 到了不同的規模給出的方案變化也會不小.
雖然參與的有很多當時簡聊的成員, 但我也不認爲個人方法論有留下多少.
從後面的研究看, 那些方法論跟 ClojureScript 的契合度只多很多,
即使我是在快離開的時候才漸漸整我的倒向 ClojureScript 陣營的...
如今不少的思路在 Respo 相關工具鏈中還存留着, 我的項目也還在, 但畢竟變化很多了.
另外那些延續到了後面公司的用法, 隨着配合 TypeScript 的努力, 也改變了很多,並且從後面接觸的大量後臺管理的表單的場景看, 簡聊的單頁面纔是比較特殊的場景.再說後面我也更多接觸到前端場景的複雜度了, 甚至移動端和小程序我都還沒開始...除了小程序, WebAssembly 那邊的事情纔開了個口.. 前端真是好複雜.