本人寫了五年的後臺管理。每次面試前就會頭疼,因為寫的頁面除了表單就是表格。抱怨過苦惱過也後悔過,但是站在現在的時間點回想以前,發現有很多事情可以做的更好,於是有了這篇文章。
本文首發在 https://juejin.cn/post/7360528073631318027
寫優雅的程式碼
一道面試題
大概兩年以前,面試美團的時候,面試官讓我寫一道程式碼題,時間單位轉換。具體的題目我忘記了。
原題目我沒做過,但是我寫的業務程式碼程式碼裡有類似的單位轉換,後端返回一個數字,單位是kb
,而我要展示成 KB
,MB
等形式。大概寫一個工具函式(具體怎麼寫的忘記了,不過功能比這個複雜點):
function formatSizeUnits(kb) {
let units = ['KB', 'MB', 'GB', 'TB', 'PB'];
let unitIndex = 0;
while (kb >= 1024 && unitIndex < units.length - 1) {
kb /= 1024;
unitIndex++;
}
return `${kb.toFixed(2)} ${units[unitIndex]}`;
}
而在此之前,是一個後端同學寫的程式碼(說起來,後臺管理系統前端人力不夠後端幫忙寫好像是常規操作,後端總覺得寫前端簡單,而我只能說你們那程式碼寫的,一言難盡……),如下:
function formatSizeUnits(kb) {
var result = '';
if (kb < 1024) {
result = kb + ' KB';
} else if (kb < 1024 * 1024) {
result = (kb / 1024).toFixed(2) + ' MB';
} else if (kb < 1024 * 1024 * 1024) {
result = (kb / 1024 / 1024).toFixed(2) + ' GB';
} else {
result = (kb / 1024 / 1024 / 1024).toFixed(2) + ' TB';
}
return result;
}
雖然功能都一樣,但是我寫在面試的時候寫了上面的程式碼得到了面試官的讚揚,也因此非常順利的透過了面試。而如果我寫成了下面的樣子,估計不會有這種效果。
如何提升程式碼質量
想提升程式碼質量,最理想的是靠 code review ,但實際上這玩意在大多數公司根本就不存在。其次就只能多看看大佬們寫的程式碼,但是一來後臺管理本來就沒太大難度很少有大佬,二來很多時候一個後臺管理系統就一兩個人負責。
還好現在有了各種 AI 工具,可以非常方便的最佳化自己的程式碼了。
還是上面的問題,看看 GPT 給的答案
// 提問:寫一個工具函式,對檔案大小的單位進行轉換,加註釋
// 回答:下面是一個JavaScript工具函式,它接受檔案大小、初始單位、目標單位和小數位數,然後返回目標單位下的檔案大小。
/**
* 將檔案大小從一個單位轉換為另一個單位。
*
* @param {number} size 檔案大小。
* @param {string} fromUnit 初始單位('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')。
* @param {string} toUnit 目標單位('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')。
* @param {number} [decimalPoint=2] 結果保留的小數位數,預設為2。
* @return {string} 轉換後的檔案大小,帶單位。
*/
function convertFileSize(size, fromUnit, toUnit, decimalPoint = 2) {
// 定義單位與位元組之間的轉換關係
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
// 獲取初始單位和目標單位的索引
const fromIndex = units.indexOf(fromUnit);
const toIndex = units.indexOf(toUnit);
// 如果單位不在列表中,丟擲錯誤
if (fromIndex === -1 || toIndex === -1) {
throw new Error('Invalid units');
}
// 計算初始單位與目標單位之間的轉換系數
const exponent = toIndex - fromIndex;
// 計算結果大小
const resultSize = size / Math.pow(1024, exponent);
// 返回格式化後的結果
return parseFloat(resultSize.toFixed(decimalPoint)) + ' ' + toUnit;
}
// 示例使用
console.log(convertFileSize(1, 'GB', 'MB')); // 輸出: 1024.00 MB
console.log(convertFileSize(1, 'MB', 'KB')); // 輸出: 1024.00 KB
console.log(convertFileSize(1, 'KB', 'B')); // 輸出: 1024.00 B
console.log(convertFileSize(1, 'MB', 'GB', 5)); // 輸出: 0.00098 GB
還能說什麼,我只能說 NB!(經評論區提示,此段程式碼可能有問題,建議大家在使用AI工具時,重點是借鑑思路,直接使用程式碼需要謹慎~)
我現在每次寫大段邏輯之後都習慣拿給 AI 看看,有什麼更好的實現方式,或者用什麼設計模式。AI 是非常低成本且高效提升程式碼質量的工具。
學會封裝
一個功能用到了好多次,為什麼不封裝成元件?一個元件用到了好幾個專案,為什麼不單獨寫個npm包?差不多的專案建立了好幾個,為什麼不封裝成腳手架?
你說,沒時間,沒必要,複製貼上反而更快。
那你就完全沒理解,這麼做不一定是為了讓工作更快完成,而是可以讓你在年年終述職時更有話說(你就算寫了一百個表單表格沒有寫一個腳手架更值得炫耀),如果不會寫可以問問 AI。
而當你真正開始封裝元件,開始寫工具庫了,你會發現你需要思考的確實比之前多了。
關注業務
對於前端業務重要嗎?
相比於後端來說,前端一般不會太關注業務。就算出了問題大部分也是後端的問題。
但是就我找工作的經驗,業務非常重要!
如果你做的工作很有技術含量,比如你在做低程式碼,你可以面試時講一個小時的技術難點。但是你只是一個破寫後臺管理,你什麼都沒有的說。這個時候,瞭解業務就成為了你的亮點。
一場面試
還是拿真實的面試場景舉例,當時前同事推我位元組,也是我面試過N次的夢中情廠了,剛好那個組做的業務和我之前呆的組做的一模一樣。
- 同事:“做的東西和咱們之前都是一樣的,你隨便走個過場就能過,我在前端組長面前都誇過你了!”
- 我:“好嘞!”
等到面試的時候:
- 前端ld:“你知道xxx嗎?(業務名詞)”
- 我:“我……”
- 前端ld:“那xxxx呢?(業務名詞)”
- 我:“不……”
- 前端ld:“那xxxxx呢??(業務名詞)”
- 我:“造……”
然後我就掛了………………
如何瞭解業務
-
每次接需求的時候,都要了解需求背景,並主動去理解
我們寫一個表格簡簡單單,把資料展示出來就好,但是表格中的資料是什麼意思呢?比如我之前寫一個 kafka 管理平臺,裡面有表格表單,涉及什麼
cluster
controller
topic
broker
partition
…… 我真的完全不瞭解,很後悔我幾年時間也沒有耐下心來去了解。 -
每次做完一個需求,都需要了解結果
有些時候,後臺管理的團隊可能根本沒有PM,那你也要和業務方瞭解,這個功能做了之後,多少人使用,效率提高了嗎?資料是怎樣的?
-
理解需求,並主動去最佳化
產品要展示一千條資料,你要考慮要不要分頁,不分頁會不會卡,要不要上虛擬表格?
產品要做一個可拖拽表單,你要考慮是否需要拖動,是否需要配置。
其實很多時候,產品的思維可能會被侷限在競品的實現方式,而前端可以給TA更多選項。在和產品溝通的時候,你不僅是溝通頁面的實現,也更能理解業務。
關注原始碼
說到原始碼, Vue,React 這些基本是每次必問,也有太多大佬們的總結我就不多說了。
除此之外,我想大家每次面試應該都會被問,你寫了什麼亮點的專案,困難的專案,你會說什麼?哦,都是表單表格,都用 Element UI 實現的,沒什麼難度。那估計你面試也就到這裡了。
那說什麼,後臺管理就這些啊?!
如果你用了 Element UI 你可以說寫了什麼自定義 Element UI 表單元件,Element UI 表單原始碼是什麼原理,怎麼通訊的?用了 Axios 可以說 Axios 的原理,它的攔截器怎麼實現的?用了編輯器 diff 那你知道 diff 演算法是什麼嗎?用了 ECharts 那你知道他底層是用什麼繪製的嗎?用了 husky ?husky的原理是什麼?用了Vite,它為什麼快?是怎麼打包的?寫過外掛嗎?
可能有人覺得我離譜,事實上上面每一個問題都是我在面試中真實被問到的。當然,如果全部去了解,肯定不現實,每個人的精力有限,不可能每個用到的每個工具每個庫都去了解原理,不過只要有一兩個你去了解過,就可以在面試中引導面試官來問,要學會表現出你擅長的點。
至於怎麼讀原始碼?以前我不知道,現在嘛…… 為什麼不問問神奇的 ChatGPT 呢?
講一下 Axios 原始碼中,攔截器是怎麼實現的?
Axios 的攔截器實現基於 Axios 的核心原理,即 Axios 例項是一個包含請求和響應攔截器堆疊的物件。當發出請求或接收響應時,Axios 會遍歷這些攔截器,並按照新增的順序執行請求攔截器,以及按照相反的順序執行響應攔截器。
在 Axios 的原始碼中,攔截器是透過一個 AxiosInterceptorManager 例項來管理的,它維護了一個攔截器陣列。每個攔截器都是一個包含
fulfilled
和rejected
函式的物件。這兩個函式分別對應於攔截器成功處理和攔截器處理出錯的情況。以下是 Axios 攔截器管理器的一個簡化版本,展示了其核心實現思路:
class InterceptorManager { constructor() { this.handlers = []; // 儲存攔截器的陣列 } use(fulfilled, rejected) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected }); return this.handlers.length - 1; // 返回攔截器的ID } eject(id) { if (this.handlers[id]) { this.handlers[id] = null; // 移除攔截器 } } forEach(fn) { this.handlers.forEach((h) => { if (h !== null) { fn(h); } }); } }
在傳送請求或接收響應時,Axios 會建立一個 promise 鏈,並透過
forEach
方法將攔截器中的fulfilled
和rejected
函式新增到這個鏈中。這樣,每個攔截器都可以對請求或響應進行處理,然後將結果傳遞到鏈的下一個攔截器,或者在出錯時結束鏈的執行。axios.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); axios.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); });
在 Axios 的完整實現中,這個攔截器機制被整合到了 Axios 的請求傳送和響應處理流程中。透過這種方式,Axios 可以在傳送請求之前和接收響應之後,但在使用者定義的
.then
或.catch
執行之前,插入自定義的邏輯。請注意,這裡提供的程式碼只是為了說明 Axios 攔截器的實現原理,並不是 Axios 原始碼的完整複製。如果你對 Axios 的攔截器實現細節感興趣,建議檢視 Axios 的官方 GitHub 倉庫中的原始碼。
前端基建
當我們工作時間久了面試難免會遇到這些問題,前端工程化,前端監控,工作流,部署,效能等等。其實我們在工作中絕大部分時間都在寫程式碼,對於這些不是所有人都有機會接觸到,不過這些和所做的業務無關,是我們提升自己很好的一個思路。
技術選型
技術棧選 Vue 還是 React?Vue 選 Vue2 還是 Vue3?元件庫選 ElementUI 還是 Ant Design?微前端有沒有使用過?打包工具用 Vite 還是 Webpack?有那麼多表單怎麼實現的,有沒有什麼表達配置化方案,比如Formily?
對於我這種菜雞,我這種只寫簡單的表單表格的人,這些都……無所謂……
不過為了應對面試我們還是需要了解下未選擇技術棧的缺點,和已選擇技術棧的優點(有點本末倒置…但是常規操作啦)
Vue 你可以說簡單高效輕量級,面試必會問你為什麼,你就開始說 Vue 的響應式系統,依賴收集等。
React 你可以說 JSX、Hooks 很靈活,那你必然要考慮 JSX 怎麼編譯, Hooks 實現方式等。
總體而言,對於技術選型,依賴於我們對所有可選項的理解,做選擇可能很容易,給出合理的理由還是需要花費一些精力的。
開發規範
這個方面,在面試的時候我被問到的不多,我們可以在建立專案的時候,配置下 ESlint
,stylelint
, prettier
, commitlint
等。
前端監控
幹了這麼多年前端,前端監控我是……一點沒做過。
前端監控,簡單來說就是我們在前端程式中記錄一些資訊並上報,一般是錯誤資訊,來方便我們及時發現問題並解決問題。除此之外也會有效能監控,使用者行為的監控(埋點)等。之前也聽過有些團隊分享前端監控,為了出現問題明確責任(方便甩鍋)。
對於實現方案,無論使用第三方庫還是自己實現,重要的都是理解實現原理。
對於錯誤監控,可以瞭解一下 Sentry,原理簡單來說就是透過 window.onerror
和 window.addEventListener('unhandledrejection', ...)
去分別捕獲同步和非同步錯誤,然後透過錯誤資訊和 sourceMap
來定位到原始碼。
對於效能監控,我們可以透過 window.performance
、PerformanceObserver
等 API 收集頁面效能相關的指標,除此之外,還需要關注介面的響應時間。
最後,收集到資訊之後,還要考慮資料上報的方案,比如使用 navigator.sendBeacon
還是 Fetch、AJAX?是批次上報,實時上報,還是延遲上報?上報的資料格式等等。
CI/CD
持續整合(Continuous Integration, CI)和 持續部署(Continuous Deployment, CD),主要包括版本控制,程式碼合併,構建,單測,部署等一系列前端工作流。
場景的工作流有 Jenkins、 Gitlab CI 等。我們可以配置在合併程式碼時自動打包部署,在提交程式碼時自動構建併發布包等。
這塊我瞭解不多,但感覺這些工具層面的東西,不太會涉及到原理,基本上就是使用的問題。還是需要自己親自動手試一下,才能知道細節。比如在 Gitlab CI 中, Pipeline
、 Stage
和 Job
分別是什麼,怎麼配置,如何在不同環境配置不同工作流等。
瞭解技術動態
這個可能還是比較依賴資訊收集能力,雖然我個人覺得很煩,但好像很多領導級別的面試很願意問。
比如近幾年很火的低程式碼,很多面試官都會問,你用過就問你細節,你沒用過也會問你有什麼設計思路。
還有最近的兩年爆火的 AI,又或者 Vue React的最新功能,WebAssembly,還有一些新的打包工具 Vite Bun 什麼的,還有鴻蒙開發……
雖然不可能學完每一項新技術,但是可以多去了解下。
總結
寫了這麼多,可能有人會問,如果能回到過去,你會怎麼做。
啊,我只能說,說是一回事,做又是另一回事,事實上我並不希望回到過去去卷一遍,菜點沒關係,快樂就好,一切都是最好的安排。