優秀開源框架就一定靠譜么?五招助你駕馭源代碼

      網友投稿 870 2025-03-31

      前言

      The most incomprehensible thing about the world is that it is comprehensible.

      世界上最不可理解的地方就是它竟然是可以理解的。-- 阿爾伯特·愛因斯坦

      筆者也不例外。我最近在用 Vue 3 重構 Crawlab 前端的時候,用到了 Element 團隊開發的升級版的 ElementUI,也就是 Vue 3 重構的新 UI 框架 Element Plus。Element 團隊在 Element Plus 中將該項目用 Vue 3 完全重構,全面擁抱了 TypeScript;而且相比于之前的 Vue 2 版本豐富了部分組件;而整體風格和使用方式跟之前的版本一致;一些 API 在使用上還變得更精簡了。因此,筆者在重構 Crawlab 前端初期過程中沒有遇到太大的障礙,再加上之前的編寫經驗,開發過程中顯得駕輕就熟。然而,好景不長,隨著項目的不斷開發,筆者遭遇到一些技術上的困難。更準確的說,在實現一些復雜功能時遇到了來自于 Element Plus 框架本身的限制。雖然最終想方設法將問題解決了,但是我也深刻體會到了硬啃(Hacking)開源項目源代碼的困難。因此,也希望借此機會將自己駕馭開源代碼的經驗分享給讀者。

      本篇文章將以解決 Element Plus 問題的經歷開始,循序漸進討論開源項目或開源框架的問題,進一步討論駕馭開源項目源代碼的方法和技巧,分享自己閱讀、理解和更改源代碼的思路。本篇文章主要是方法論的討論,不涉及太多技術細節,任何專業背景的讀者都可閱讀。

      硬啃 Element Plus

      Element Plus 的使用方法跟之前的 Vue 2 版本一樣簡單易用,文檔風格也跟老版本一致,非常全面。按照官方文檔鼓勵的,筆者也嘗試用組件化的方式來優化之前的 Crawlab 老組件,例如表格(Table)。除了將列表(Column)、數據(Data)、分頁(Pagination)等進行簡單的封裝以外,筆者還希望加入一些新的實用功能,例如自定義表格的篩選(Filter)和排序(Sort),以及表格能夠允許用戶自定義(Customize)要展示的列和調整列的順序,等等。雖然 Element Plus 框架有排序和篩選功能,但筆者個人覺得太基礎了,通常用戶需要既簡單又好用的 UI 組件。

      下圖是篩選表格的前端截圖,比較類似 Excel 的展示和操作方式。

      實現這個功能的問題不是非常大,按照官方的文檔都能解決。其中主要利用了列組件中的 el-table-column 的 header 插槽(Slot),在其中加入彈出框(Popover)。很好,So far so good!接下來就是加入列的自定義功能了,看上去很快就要大功告成了嘛。

      要實現列的自定義,最完美的實現方法就是讓用戶點擊操作按鈕,彈出對話框(Dialog),里面可以選擇要展現的列,用拖拽的方式將其排序,然后點擊確認。然后我在 Element Plus 官網上快速找到了一個新組件,穿梭框(Transfer),效果如下圖。理論上,只要我在對話框中引用該彈出框來控制展現列的數組就可以了,例如將要展示的列放到 “列表2” 中。鏘鏘!目前看起來似乎一切非常順利。

      不過,在仔細閱讀文檔之后我發現該組件并不支持拖拽,因此暫時無法實現列排序的功能。雖然有點沮喪,但這個影響不大,我們主要想實現的是列選擇功能。排序雖然也重要,但暫時先擱置一下,先實現組件選擇再說!

      然而,現實總是出乎意料。筆者在進一步試驗中發現,這個組件似乎存在一個重大 Bug:當穿梭框中全選候選元素時,竟然無法選擇或取消選擇了(向左移動或向右移動)!趕緊仔細調試查找原因,反復確認是不是我自己寫的代碼有問題。然后發現是 Vue 在更新組件時在 runtime-core.esm-builder.js 中的 patchBlockChildren 方法下出現了 oldChildren 為 null 的情況。進一步閱讀 Element Plus 中穿梭框組件的相關源代碼之后,懷疑是 transfer-panel.vue 這個子組件出了問題,它更新渲染的時候 el-checkbox-group 不存在,因此導致更新時出錯。隨后我在 Element Plus 的 Github 倉庫中提交了 Bug Issue。不過維護者并不認為我反映的問題是 Bug,因為在 CodePen 上似乎沒有問題。但這個 Bug 確實在我的項目中客觀存在,可以在 crawlab-frontend 的 歷史提交 中復現。

      無奈之下,自行琢磨半天無果之后,果斷決定自己造輪子。很快從頭寫了一個類似穿梭框的組件出來,組件效果如下圖。新寫的組件在外觀上與之前的非常類似,不過自由度很高,因此也順便支持了拖拽功能??瓷先栴}似乎快要解決了。

      不過,當我滿懷期望準備測試拖拽排序功能時,意想不到的事情發生了:列順序數據改變之后,表格列竟然沒有任何改變!改變列組件 el-table-column 的順序對界面展示的竟然沒有作用!而在官方文檔的定義中,el-table-column 的順序決定著列的實際展示順序。經過重復多次試驗之后,筆者只好承認原先的實現方式有問題。簡單的改變模版中的列組件順序并不會影響界面上的展示。

      筆者當時想到一個 “聰(yu)明(chun)” 的辦法。我試圖用強制刷新表格組件的方式讓其數據重新渲染,達到改變列順序的目的。但經過測試,發現這樣的暴力方式存在很大的性能問題,每次點擊 “Apply” 之后會卡頓近一秒。

      于是,筆者又陷入了絕望的境地。

      在進退兩難的情況下,筆者停下來仔細思考。是不是我對 Element Plus 的框架還不夠了解?表格組件本身是如何實現的?它有什么限制和缺點?這些問題都推動我進一步來拆解 Element Plus 框架本身,也就是去閱讀它的源代碼,理解組件本身的代碼邏輯和工作原理。于是,筆者克隆了 Element Plus 代碼倉庫 到本地。

      非常幸運,Element Plus 項目的代碼質量相當高,代碼組織結構和命名方式都非常清晰。雖然注釋相對來說偏少,但它清晰的邏輯結構和良好的命名規范讓可讀性變得很強。在驚嘆于大廠工程師職業素養的同時,我也快速定位到了表格組件的源碼位置 packages/table。整個 Element Plus 項目是用 MonoRepo 的方式管理的,簡單來說是一個 Git 倉庫里有很多個 NPM 項目。管理工具使用了 Lerna。下面是 el-table 組件 NPM 項目的代碼組織結構。

      . ├── __tests__ │ └── table.spec.ts ├── index.ts ├── package.json └── src ├── config.ts ├── filter-panel.vue ├── h-helper.ts ├── layout-observer.ts ├── store │ ├── current.ts │ ├── expand.ts │ ├── helper.ts │ ├── index.ts │ ├── tree.ts │ └── watcher.ts ├── table │ ├── style-helper.ts │ └── utils-helper.ts ├── table-body │ ├── events-helper.ts │ ├── index.ts │ ├── render-helper.ts │ ├── styles-helper.ts │ └── table-body.d.ts ├── table-column │ ├── index.ts │ ├── render-helper.ts │ └── watcher-helper.ts ├── table-footer │ ├── index.ts │ ├── mapState-helper.ts │ └── style-helper.ts ├── table-header │ ├── event-helper.ts │ ├── index.ts │ ├── style.helper.ts │ ├── table-header.d.ts │ └── utils-helper.ts ├── table-layout.ts ├── table.type.ts ├── table.vue ├── tableColumn.ts └── util.ts

      從這個結構可以看到,整個 el-table 表格組件由幾個子組件組成,例如 table-body、table-column、table-footer 等。而整個項目只有 table.vue?這個一個組件。根據 Vue 開發的經驗,筆者很快意識到這就是整個組件的入口。咱們先去瞅瞅看吧!

      整個 table.vue 文件有469 行代碼,算是比較大的文件,限于篇幅原因就不在這里詳細解釋了。其中,最重要的發現是一個叫 store 的變量。仔細研究后發現這是由 Vuex 創建的狀態管理器。好家伙!原來表格組件是用 Vuex 來管理數據的啊。這下清楚了,只要能搞定 Vuex 的部分,剩下的問題應該就可以迎刃而解了。

      話不多說,盤它!

      優秀開源框架就一定靠譜么?五招助你駕馭源代碼

      我開始不斷嘗試查找跟列相關的代碼,開始全局搜索 “columns” 等類似的關鍵字。在發揮了名偵探柯南的洞察能力之后,我逐漸注意到 src/store/index.ts 這個文件中的 useStore 方法,這就是整個問題的關鍵!好了,整個問題原因找到了,關鍵在于store.states._columns 這個內部狀態變量,它是渲染列數據的關鍵。但它只有初始化或添加刪除列的時候才會被賦值,調整 el-table-column 的順序根本不會改變這個變量!

      找到原因,解決辦法就簡單了。筆者添加了 setColumns 這個 Mutation 方法,以更方便的設置列數組。具體實現過程限于篇幅原因就不詳述了。感興趣的朋友可以看 Crawlab Frontend 的 源碼。

      下面是完整的表格自定義列的效果圖。大功告成!

      開源框架的利與弊

      世界上沒有完美的東西,優秀開源框架也不例外。而這一次硬啃(Hacking)源碼的經歷讓筆者深刻體會到這個道理。筆者認為非常有必要討論關于使用和開發開源框架或開源項目的相關問題。首先我們來看看它的優勢。

      很多知名優秀的開源項目,例如 Nginx 和 Redis,都成為了軟件開發的核心技術。那么,我們為什么要使用優秀的開源框架?筆者認為開源框架主要有以下幾個優勢:

      免費。誰能拒絕不要錢的東西呢,況且很多免費的開源框架已經足夠優秀了;

      透明。開源框架的所有源代碼都是公開的,任何人都可以看到;

      可更改。大部分開源項目都是自由度很高的 MIT 或 BSD 開源版權,可以按需定制開發;

      可協作。Github 是最大的開源項目平臺,全球的開發者都可以參與迭代開源項目;

      雖然開源項目有不少突出優勢,但在使用開源項目的時候卻經常會遇到各種各樣的問題。筆者認為使用開源框架存在一定的風險。筆者認為開源框架主要有以下幾個劣勢:

      安全隱患。雖然很多優秀開源項目都由企業或資深專家開發維護,但由于不完全是自己使用,導致貢獻者容易對安全性造成疏忽,知名開源項目爆出安全漏洞的例子多不勝數,例如 OpenSSL Heartbleed、Fastjson 遠程代碼漏洞、Antd 圣誕彩蛋等;

      良莠不齊。開源項目開發者、貢獻者和維護者可以是任何人,他們各自的經歷和專業背景不同,所以必然導致代碼或開源項目的質量存在一定的差異;雖然代碼規范(Coding Standard)可以規避一些問題,但優秀的項目畢竟是少數,看看托管了幾百萬項目的 NPM 或 Maven 公共倉庫吧;

      學習成本。筆者承認有一部分優秀開源框架有很成熟和完善的文檔體系,但大部分還是缺乏有效的文檔教程支持;即使有了詳盡的文檔,開發者要閱讀學習也會投入很多時間成本;而大部分付費產品則包含專業技術支持,可以有效幫助開發者節省時間;

      未知風險。再優秀的框架都會存在風險,由于開源框架初期并沒有經歷太多的實際業務測試,很多問題無法得到及時修復,因此在使用開源框架的過程中或多或少都會遇到一些出乎意料的問題,解決它們會花大量時間,甚至有些問題還無法解決。

      就筆者前面提到的解決開源框架問題的經歷來說,未知風險和學習成本尤為突出。筆者在采用 Element Plus 的穿梭框(Transfer)組件時,想當然的認為它可以有效滿足我的需求。然而,沒想到的是它既不能支持我計劃的列排序需求,又存在重大 Bug 導致無法使用。這些問題都讓我放棄使用該組件,從而尋求重新造輪子的高成本解決方案。

      在之后硬啃 Element Plus 表格組件的過程中,我沒有放棄使用該組件,而是通過閱讀該組件的源代碼,根據邏輯和經驗判斷出了核心代碼的位置以及其可能產生問題的原因,并針對性的做了調整,最終順利的解決了問題。這次閱讀源代碼而進行調整的決定,給我節省了大量的時間和精力,因為要重復造表格組件這樣的復雜輪子要做大量的工作,這是非常不劃算的。

      上圖是官方文檔其中一個組件的截圖。Element Plus 的文檔其實已經非常完善了,關于組件的使用方法、例子、API 等都寫得很詳細,每當開發時遇到不確定的直接去官方文檔查看即可。不過,ElementUI 最早是誕生于餓了么,因此其中的組件主要是為了支持餓了么的外賣業務,因此它有一定的局限性。作為 ElementUI 的 升級版,Element Plus 雖然是同一個團隊的產品,但它的功能以及 API 相比于老版本并沒有太大的變化。作為對比,誕生于螞蟻金服的 UI 框架 Ant Design 相對來說就要靈活自由很多,因為它支持的業務線很廣,而阿里推行的中臺系統也要求 UI 框架需要設計得很通用。因此,Element Plus 的局限性很可能來自于它的開發團隊背景。Crawlab 前端框架選擇 Element Plus 主要是因為老版本是 ElementUI,遷移成本相對來說比較低,但因此也帶來了一些因框架不成熟導致的問題。甚至,我們也必須要知道即使是優秀的開源項目也會存在這樣那樣的問題。從這方面來看,了解開源框架本身的優點和缺點,對于如何正確使用來說非常重要。為了讓開源框架能為我所用,我們需要知道如何正確駕馭開源項目的源代碼。

      接下來,筆者將結合自己的項目經驗,為讀者介紹如何有效駕馭開源項目中的源代碼。

      源代碼駕馭技巧

      如果一個古代的人穿越到現代,當他看到各式各樣的科技產品時,一定會驚呼它們為魔法。然而,他們不知道的是,這些所謂的 “魔法” 都是利用科學技術創造出來的,它們背后的原理是通過科學實驗總結出來的,并不是什么神秘的巫術。這個道理同樣適用于軟件行業。

      程序員用代碼創造了魔法般的互聯網社會,這對于非技術人員來說非常神秘。他們知道那不是真正的魔法,但他們完全不知道這些是如何實現的,背后的原理是什么。對于程序員來說,利用一些優秀的開源項目,可以快速讓他們開發出產品原型、驗證產品可行性、甚至開發出生產可用的系統。不過,如果程序員只是把開源產品當作工具,完全不理解里面的原理是什么,那就等于是在使用魔法,而你也不知道它為何會生效。這樣的做法會給你帶來麻煩。要正確使用一個開源框架,你必須掌握它;要掌握它,你必須理解其工作原理;要理解其工作原理,你必須閱讀其源代碼;要讓源代碼聽命于你,你必須學會如何駕馭它,也就是如何修改和優化源代碼。

      接下來筆者將介紹幾個有效方式來幫助你駕馭源代碼。

      讀者可能會驚訝于這個看上去沒什么用的建議。但是,我看到很多朋友想學習一個開源項目時,經常因為項目過于復雜而中途放棄了,包括很多年前的自己。其中就是挫敗感在作祟。我承認,要短期理解一個大型開源項目是不太現實的。這導致了很多朋友被玲瑯滿目的技術難點給嚇跑了,留下一句 “只要會用就行”,然后繼續搬磚擰螺絲。其實,他們都太低估自己的潛力了。筆者并不反對程序員的實用主義,但要注意過于實用主義會導致功利主義,最終原地踏步的將是自己。

      開源框架最棒的一點就是它對來說完全是透明的,任何人都可以看到其中的代碼,包括底層實現邏輯、代碼組織結構、項目部署方式等等。這不是提升自己駕馭源代碼能力的絕佳機會么?通過閱讀開源框架源代碼,你不僅可以了解其中的工作原理,還可以學習更好的編程方式,從而提升自己的職業素養。我真的不提倡 “從入門到放棄系列” 這句戲言,你至少應該這樣暗示自己,“我目前看不懂是因為我的基礎知識不足,等我提升基礎實力之后一定能駕馭它”。筆者在硬啃完一些復雜的開源框架源代碼之后,發現閱讀源碼其實也沒那么難。就像鍛煉身體一樣,撐過這一個山坡、這一個沖刺,之后就非常輕松了,而且身體素質也提高了。

      因此,克服挫敗情緒對于駕馭開源項目源代碼來說非常重要,而且你要暗示自己閱讀源碼并不難。

      當然,要開始寫開源項目并不是一件容易的事情。你首先可能就會陷入寫什么的困難中。這里筆者推薦之前寫的關于開發開源項目的兩篇文章《如何打造一個上千Star的Github項目》以及《收獲人生第一個 5k Star 開源項目,經驗教訓分享給大家》,里面詳細講解了筆者開發、維護、推廣開源爬蟲管理平臺 Crawlab 的心得體會,會介紹如何定位痛點、調研用戶、推廣產品、項目管理等等。有興趣的讀者可以深入閱讀。

      終于,這一條是一個稍微有點干貨的建議了。在閱讀源代碼的時候,你必須首先找到該項目的入口文件(Entry File)。所謂的入口文件,就是這個項目模塊或系統暴露給外部系統的一個公共通道,整個項目的運行、調用、執行都是從這個文件開始的。 而其他實現具體邏輯的代碼就在入口文件中被引用,或者在其他文件中被引用。對于前端項目,一般就是 main.ts;Python 項目一般是 app.py;Golang 項目一般就是 main.go,等等。如果把看似不可駕馭的大型開源項目比做戰無不克的戰神阿喀琉斯,那么入口文件就是他的致命之踵。

      其實,不光是閱讀開源項目,就是我們閱讀工作中其他同事寫的代碼時,也是先從入口文件開始研究。掌握了入口文件的內部邏輯,你可以看到它引用的其他子模塊,然后就可以順藤摸瓜找到其他核心模塊,接著繼續下去。當整個項目通過入口文件遍歷一遍之后,你會更容易理解代碼的邏輯結構,從而幫助你深入理解具體的工作原理。

      在之前解決 Element Plus 表格問題的例子中,筆者定位到了 table.vue 這個入口文件,然后進一步找到了列數據的關鍵位置,最后輕松的解決了問題。看!駕馭源碼其實并不是那么難,對么?

      這又是一個技術性建議。由于大型項目通常來說要拆分成很多文件,因此對于這種情況來看,你如果窮舉式的遍歷所有文件會花大量的時間。因此,你可以嘗試全局搜索可能的關鍵字,從而快速定位到核心代碼部分。全局搜索在主流 IDE 中功能非常強大,通常支持正則表達式、模糊匹配、精確匹配、大小寫等等。下圖是 JetBrains 旗下 WebStorm IDE 中的全局搜索結果截圖。

      對于像 TS 這樣的靜態類型語言來說,還可以像 Java、C#、Golang 那樣查找方法或變量的調用位置,在 WebStorms 里是 “Find Usages”,在其他 IDE 中可能名稱不一樣,但它們都是一個意思。這些技巧都能有效幫助你閱讀理解源碼。

      好的文檔包括項目的架構、原理、概念等,可以幫助開發者快速理解框架的代碼結構。不過,并不是所有開源框架都有詳細的文檔,這種方式可遇不可求。不過,如果開源項目中一旦包含了技術文檔,請一定花時間瀏覽一下。

      總結

      本篇文章通過介紹筆者折騰開源 Vue 3 UI 框架 Element Plus 的開發經歷,討論了開源項目的優勢和劣勢,得出優秀開源框架也存在問題的結論。然后,筆者結合自己的項目經驗提出了 5 個駕馭開源項目的技巧和方法,包括克服挫敗情緒、自己寫開源項目、定位入口文件、使用全局搜索以及閱讀技術文檔?,F在的軟件開發離不開優秀的開源項目,但是我們也必須意識到開源項目并不是專門為你自己服務的,因此它存在一定的局限性。為了能更好的使用開源框架,讓它能兼容你的復雜需求,很多情況下你必須仔細閱讀它的源代碼,這就少不了會產生一定的學習成本。

      我們通常說程序員要提升自己的技術實力,要會 “寫” 出好代碼;但是,在團隊合作越來越重要的趨勢下,筆者認為程序員更重要的能力是要會 “讀” 別人的代碼,不管好與壞。在編寫代碼的過程中保證正確合理的命名規范,盡量添加上注釋以方便他人理解,這是寫代碼的能力;跟他人合作時,仔細閱讀別人寫的代碼,盡可能理解他們的思路,思考實現方式是否正確,等等,這是讀代碼的能力。當然,寫和讀只是非?;A的部分,駕馭代碼的能力還不止 “寫” 和 “讀”,應該還包括思維、邏輯、規劃等能力,例如系統架構、算法原理、可擴展性設計等等。

      因此,當我們在抱怨同事代碼寫得差的時候,千萬別懊惱或生氣,因為那可能是因為你閱讀代碼的能力不夠,沒有理解清楚同事的思路;同樣,可能在同事看來,我們的代碼也糟糕透了;或者,這是傳說中的五十步笑百步?

      社區

      如果您對筆者的文章感興趣,可以加筆者微信 tikazyq1 并注明 “碼之道”,筆者會將你拉入 “碼之道” 交流群。

      GitHub Vue web前端

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel2003折線圖的制作過程
      下一篇:Word中項目編號怎么設置(word2010項目編號怎么設置)
      相關文章
      亚洲精品456在线播放| 亚洲国产精品成人久久蜜臀| 亚洲国产精品尤物yw在线 | 亚洲日本va在线观看| 亚洲人午夜射精精品日韩| 亚洲xxxx18| 亚洲免费人成视频观看| 亚洲第一精品在线视频| 国产综合亚洲专区在线| 亚洲资源最新版在线观看| 亚洲伊人精品综合在合线| 亚洲一级毛片免费观看| 亚洲国产日韩综合久久精品| 亚洲视频在线观看2018| 亚洲人成网站在线在线观看| 亚洲精品乱码久久久久蜜桃| 亚洲经典千人经典日产| 日本亚洲欧美色视频在线播放| 亚洲第一成年免费网站| 国产精品亚洲精品日韩动图| 一级毛片直播亚洲| 亚洲一区二区视频在线观看| 在线亚洲精品自拍| 亚洲国产成人一区二区三区| 亚洲一区中文字幕久久| 亚洲国产精品久久网午夜| 亚洲天堂2016| 亚洲AV无码AV吞精久久| 五月婷婷亚洲综合| 久久久久亚洲爆乳少妇无| 狠狠色伊人亚洲综合成人| 亚洲欧洲日韩不卡| 亚洲伊人久久大香线蕉影院| 亚洲乱码中文字幕在线| 亚洲国产精品人人做人人爱| 亚洲人成影院在线无码按摩店| 婷婷亚洲久悠悠色悠在线播放 | 最新亚洲卡一卡二卡三新区| 亚洲精品天堂成人片AV在线播放| 苍井空亚洲精品AA片在线播放| 亚洲无码黄色网址|