1.引言
1.1 為什么要寫高質量的代碼
在業務開發中,我們經常出現的一種情形,在項目初期高效地實現業務需求,但隨著時間推移,添加新功能的速度逐漸減慢。我們需要花費更多的事件去思考如何將新功能塞進現有的代碼庫,不斷蹦出來的bug修復起來也變得越來越難。代碼庫看起來就像在補丁上打補丁,最終需要進行繁瑣的考古工作才能理解系統的運行方式。
高質量的代碼通常更易于理解和修改,這可以減少在維護和更新代碼時所需的時間和精力。高質量的代碼通常更易于重用和擴展,這可以幫助開發者更快地開發新的功能和應用。高質量的前端代碼通常意味著更少的錯誤,更快的加載速度,更好的響應性,這些都可以直接影響用戶的體驗。
好文章的標準有三條:思想、邏輯和修辭。思想是根本,是內容。邏輯與修辭則是形式,是工具。邏輯是理性的形式與工具,修辭是感性的形式與工具。寫代碼和寫文章一樣,首先保證的根本是功能可用,準確表達出業務含義;其次也要追求形式上的優雅,編碼規范就是對代碼的組織形式與風格的一種約束。好的代碼不僅邏輯清晰,風格一致,而且無論多少人參與,都如同是一個人在編碼。
1.2 什么是高質量的代碼
有良好的可讀性,代碼應易于理解。如使用清晰的命名、保持函數和方法簡短、使用注釋來解釋復雜的代碼段等。應該盡可能地簡潔,避免不必要的復雜性和冗余。在一個項目中,代碼應該有一致的風格和模式。這使得代碼更易于閱讀和理解。
可為維護性高,應該易于修改和擴展。這通常意味著代碼應該遵循某種設計模式,避免過度復雜的依賴關系,并且有良好的模塊化。好的代碼應該是直觀的,當有人需要進行修改時,他們應能迅速找到需要修改的部分,能夠快速進行更改,而且不容易引入新的錯誤。
健壯性有考量,對于邊界場景有覆蓋和處理。應該能夠處理各種預期和未預期的輸入情況,并在出現問題時優雅地失敗。遵循最佳的安全實踐,避免可能的安全漏洞。
高效運行,提供優異的用戶體驗。較少的資源文件數量和大小;地理位置更近的CDN資源請求,資源懶加載;合理的數據結構和算法,減少CPU時間、內存使用和磁盤I/O。
1.3 如何編寫高質量的代碼
工程師的編碼素養
編程不僅僅是關于如何編寫出能夠運行的代碼,它更是關于如何編寫出優雅高效、易于維護的代碼。這種藝術涉及到的不僅僅是技術層面的問題,更是關于思維方式、解決問題的策略、以及對于質量和細節的追求。優秀的編程藝術,就像一座建筑的設計,既要考慮到功能和效率,也要考慮到美感和人性化。
前端開發涉及到許多不同的技術和工具,如果開發者對這些技術和工具的理解不夠深入,或者缺乏必要的編程技能,可能會難以編寫高質量的代碼。在工作協同上,我們也常常需要在項目里程碑節點和有限的資源中尋找平衡。因此,是對我們軟性素質和專業技能的雙重考驗。
電商前端研發規范
前端開發已經成為了軟件工程的重要組成部分。它是用戶與應用程序之間的橋梁,直接影響著用戶體驗和滿意度。然而,前端開發的復雜性和挑戰性也在不斷增加,涉及到多種技術、工具、框架,以及不斷變化的業務需求和用戶期望。因此,建立一套有效的前端團隊研發規范,對于提高開發效率、保證代碼質量、促進團隊協作,以及滿足業務和用戶需求,具有至關重要的意義。
前端團隊研發規范不僅包括編碼規范,也包括研發流程規范、代碼審查規范、安全與性能規范等等。這些規范應該反映出對質量、效率、協作和持續改進的追求。大家應對規范活學活用,使我們能夠更好地應對挑戰,提供優秀的產品和服務,以滿足業務和用戶的期望。
2.工程師的編碼素養
先從形成良好的編碼習慣開始,注重編程的基本素養和要求。先寫出可讀性、可維護性高的代碼。再逐步提升專業技能,寫出健壯、高效、交互優異的代碼,對業務工程的全聲明周期進行把控,負責其功能迭代、架構設計、甚至項目重構。
2.1 有意義的命名
命名是開發過程中至關重要的技能,有一個易于理解的名字可以承載很信息,某種程度上是一種更好的注釋,一個糟糕的命名,可能會引起別人的誤解,對開發效率和項目質量影響很大;相反,遵循一套嚴格的命名規范,無論是對自己還是接手項目的人,都會大大降低代碼的維護成本。命名規范涵蓋的面比較廣,一般包括變量或常量名、函數或類名、文件或工程目錄名、工程名以及空間名等。
把信息裝到名字里,從字面含義可以關聯其代碼中的用途。名字應該盡量精確、專業、不要有多余。不會誤解的名字,閱讀你代碼的人應該理解你的本意,并且不會有其他的理解。
2.1.1 基本要求
選擇專業的單詞。比如分發事件時 send 可以用 dispatch 替代。
避免空泛的名字。如在定義變量時使用temp、arr、obj。
用具體的名字代替抽象的名字。比如我們定義一個訂單狀態,應使用orderState,而不是寫成thisState。
使用前綴或后綴來給名字附帶更多信息。比如用setPageSize來描述設置列表的分頁條目數。
合理的名字長度。為作用域大的名字采用更長的名字。
利用名字的格式來表達含義。有目的的使用命名方式、大小寫、下劃線等。比如用全大寫下劃線命(MY_CONSTANT_NAME)方式表達常亮。
使用行業/團隊范式命名。加上像is、has、can 或should這樣的詞,讓布爾值變得更明確。類或者構造函數首字母大寫。
以下列舉的不規范的命名方式,在任何情況下,你都不應該考慮使用它們:
單詞拼寫錯誤
提交表單中,把 Form 寫成了 From,如submitFrom
中英文混用
let chanpinList;這個變量名混用中英文,很不容易理解。除非是一些被創造出來但已經被廣泛接受了的名詞,如淘寶-taobao,微博-weibo,其他的情況都建議用英文;
中文詞匯縮寫
比如表達服務市場時,直接使用fwsc,對于第一次接觸的人完全不理解含義
以1-9或a-z命名
比如頁面上有幾個按鈕,直接命名成 btn1,btn2,btn3,...或者 btnA,btnB,btnC,...,這樣看似簡單,實際上從這些命名里面讀取不到任何信息,時間久了就加無法與業務對應
混用命名格式
比如表示評論列表,有地方叫 comments,另一個地方叫 comment-list,還有的地方叫 commentList,幾種規范混在一起,就感覺很不規范
單復數不分
比如有兩個操作,一個是下載全部訂單數據,一個是下載當前訂單數據,結果分別命名為 downloadOrderData 與 downloadOrder,如果沒有單復數,是不能很好地表達出業務含義的
正反義詞錯用
比如有兩個操作,一個是顯示彈窗,一個是關閉彈窗,結果分別命名為 showEditDialog 與 closeEditDialog。show 和 close ,一個是顯示,一個是關閉,顯然不是一組正反義詞
容易被過濾的單詞
ad、banner、gg、guanggao 等有機會和廣告掛勾的字眠不建議直接用來做ClassName,因為有些瀏覽器插件(Chrome的廣告攔截插件等)會直接過濾這些類名
2.1.3 團隊規范
變量命名規范
變量名【應該】使用小駝峰式命名法,且前綴應當是名詞,盡量在名字中體現類型,如 length、count 表示數字,而name、title表示字符串;
var tableTitle = 'LoginTable';
var getTitle = 'LoginTable';
函數命名規范
函數名【應該】使用小駝峰式命名法,且前綴應當是動詞,常用的動詞前綴如下表所示;
動詞 | 含義 | 返回值 |
can | 判斷是否可執行某個動作 | 函數返回一個布爾值。true:可執行;false:不可執行 |
has | 判斷是否含有某個值 | 函數返回一個布爾值。true:含有此值;false:不含有此值 |
get | 獲取某個值 | 函數返回一個非布爾值 |
set | 設置某個值 | 無返回值、返回是否設置成功或者返回鏈式對象 |
load/query | 加載某些數據 | 無返回值或者返回是否加載完成的結果 |
save/update | 保存或修改某些數據 | 無返回值或者返回是否操作成功的結果 |
function queryProductList() {
常量命名規范
常量名【應該】使用全部使用大寫字母和下劃線來組合來命名,下劃線用以分割單詞;
const MAX_IMAGE_SIZE = 10 * 1024 * 1024;
const MaxImageSize = 10 * 1024 * 1024;
const maximagesize = 10 * 1024 * 1024;
const maxImageSize = 10 * 1024 * 1024;
類或構造函數命名規范
類名或構造函數【應該】使用大駝峰式命名法,即首字母大寫。類的成員屬性和方法的命名跟變量和函數保持一致,只是私有屬性和方法名應該以下劃線開頭;
this.getName = function () {
this.setName = function (value) {
2.2 恰如其分的注釋
注釋是對于代碼中巧妙的、 晦澀的或重要的地方加以解釋。
2.2.1 基本要求
優先考慮命名而不是注釋。注釋固然很重要,但最好的文檔其實就是代碼本身。優先考慮使用有意義的類型名和變量名,不要為了注釋而注釋,某種程度上,因為需要注釋常常因為它不是很好讀,這個時候應該先考慮你的函數名和變量名是不是應該改改。
不要給不好的名字加注釋
應該把名字改好
聲明高層次的意圖而非細節。不要描述顯而易見的現象,永遠不要用自然語言翻譯代碼,而應當解釋代碼為什么要這么做,或者是為了讓代碼文檔化。比如 為接口提供功能說明,為復雜的實現提供邏輯說明,以闡述為什么是這樣而不是那樣,標注代碼中的缺陷,解釋讀者意料之外的行為等。
對代碼的翻譯,是沒有價值的注釋
說明背后為什么是它,而不是其他寫法
公布可能得陷阱,提供總結性注釋。難免在實現中引入hack代碼或考慮但未處理的邊界場景,此時應為后來者顯示標注,以便后續回溯和修復。在大塊長函數前,總結其用途和用法。
2.2.2 團隊規范
JS支持兩種不同類型的注釋:單行注釋和多行注釋。
使用 // 作為單行注釋,【應該】在注釋前插入一個空行且使 // 與注釋文字之間保留一個空格。
console.log('fetching type...');
var type = this.type || 'no type';
console.log('fetching type...');
var type = this.type || 'no type';
使用 /** ... */ 作為多行注釋,包含描述、指定所有參數和返回值的類型和值。若開始 /* 和結束 */ 都在一行,【應該】采用單行注釋。若至少三行注釋時,【應該】第一行為 /*,最后行為 */,其他行以 * 開始,并且注釋文字與 * 保留一個空格。
函數(方法)注釋也是多行注釋的一種,但是包含了特殊的注釋要求,常見的注釋關鍵字有@param、@return、@author、@version、@example,更多用法參照JSDoc
function mergeCells(grid: Ext.Grid.Panel, cols: Number[]) {
使用 // @TODO 標注問題及問題的解決方式;
2.3 合理地組織代碼
把流程控制變得易讀
條件語句中變化的值放左邊,穩定的值放右邊
優先處理條件為true的邏輯、簡單的情況、有趣和可疑的情況
fs.readFile('/file-does-not-exist', (err, data) => {
通過提早返回來減少嵌套
if (user_result == 'SUCCESS') {
if (permission_result != 'SUCCESS') {
if (user_result != 'SUCCESS') {
if (permission_result != 'SUCCESS') {
拆分過長的表達式
三目運算符只在最簡單的情況下使用,優先用if/else;不要濫用短路邏輯,部分判斷邏輯可以交由后端處理。
mode === 'multi' ? hasSelectedAll ? '已選中所有項' : '未選中所有項' : mode === 'single' ? '僅可單選' : null
state === 'INIT' && sign_state === ''
|| state === 'CHECKING' && sign_state === 'NOT_SIGNABLE'
|| state === 'AUDITING' && sign_state === 'SIGNED'
使用易懂的臨時變量,或封裝成函數
if (request.user.id == document.owner_id){
const user_owns_document = (request.user.id = document.owner_id)
別引入無謂的變量,減小變量的作用域
變量當然是越少越好,太多則難以跟蹤它們的動向,要去掉那些臨時變量、中間結果、控制流變量。
const isCurrent = timestamp === now
縮小變量的作用域,讓你的變量對盡量少的代碼可見,防止命名空間污染。
function getUserName () {
只寫一次的變量更好,不斷變化的值讓人難以理解,跟蹤這種變量的值很有難度,善用typescript與const。
通用邏輯提取與封裝
對于多次重復使用的值,可以提取為定義為變量/常量。
if (response.data.user.status === 1) {
} else if (response.data.user.status === 2) {
} else if (response.data.user.status === 3) {
const status = response.data.user.status;
case STATUS_MAP.PROCESSING:
case STATUS_MAP.ACTICATED:
case STATUS_MAP.DISABLED:
提取重復且通用的函數,以提供更好的可讀性、可維護性、及復用的可能。一段代碼一次只做一件事,可以通過拆分為段落/函數/類,讓其更清晰。
function errorHandler(error) {
log('axios response err', error)
async function getUser() {
const response = await axios.get('/user?ID=12345')
組件的封裝,遵循物料規范封裝。
2.4 前端技能提升
對于前端基礎與框架,經常通讀其整個API,主動了解其原理,保持對他們的熟悉程度。熟知其能力邊界,并在編碼過程中加以應用和實踐。
善用已有的類庫/物料庫,比如用浮點數運算的decimal.js、時間和日期計算的day.js,以及團隊的工程模板、函數庫、物料庫。
一方面已封裝的類庫有較完備的建設,如使用文檔、測試用例、符合團隊規范等。另一方面,也通過廣泛應用完成踩坑,有更好的穩定性。一個好的類庫,也是我們學習的對象,可以多問幾個為什么,它為什么產生?它能做什么?它的代碼組織形式有什么優點?它存在什么潛在風險?等等。
2.5 前端安全與用戶體驗
安全無小事,前端是Web安全的第一道屏障。
用戶輸入驗證和清理:需要驗證和清理用戶輸入的數據,以防止SQL注入和其他形式的攻擊。不要信任用戶輸入的任何數據。服務端返回的HTML不要直接渲染在頁面中。
跨站腳本攻擊(XSS):這是最常見的前端安全問題之一。攻擊者通過在網站上注入惡意腳本,當其他用戶訪問該網站時,這些腳本會在他們的瀏覽器上運行。為了防止XSS攻擊,需要確保你的應用不會接受或執行用戶提供的未經驗證和清理的HTML代碼。
跨站請求偽造(CSRF):在這種攻擊中,攻擊者會誘導用戶去點擊一個鏈接或者加載一個頁面,這個鏈接或頁面會包含一個請求,這個請求會在用戶的瀏覽器中向另一個網站發送。為了防止CSRF攻擊,你可以使用一些防護措施,例如使用同步的防偽令牌(anti-forgery token)。
HTTPS和HTTP嚴格傳輸安全(HSTS):HTTPS通過加密你的網站的流量來提供安全,而HSTS則確保瀏覽器只通過HTTPS與你的網站通信,即使用戶嘗試通過HTTP訪問。
內容安全策略(CSP):CSP是一個額外的安全層,它幫助檢測和緩解某些類型的攻擊,包括XSS和數據注入攻擊。CSP允許網頁開發者聲明頁面的內容來源,瀏覽器只會執行或渲染從這些來源加載的代碼。
前端用戶體驗是指用戶在使用網站或應用時的感受。包括了網站的性能、頁面布局、頁面交互、錯誤處理等多個方面。
在處理需求時,需要多站在用戶角度思考,針對不同手機(小屏幕/折疊屏)是怎樣的體驗;對于按鈕點擊/數據請求時,會不會產生頁面無響應、無UI反饋、多次請求等異常CASE;操作流暢性、學習和認知成本會不會過高等等。
3.電商前端研發規范
3.1 語言框架與物料
基于團隊共識,約定Web開發中心所有增量工程均采用 React 技術棧
PC端中后臺項目基礎組件庫默認為 MUI
移動端增量頁面首選一碼多投解決方案 :RTX文檔,組件庫:KproM、Kid-ui-plus
注:MUI、RTX、KproM、Kid-ui-plus等為快手自研技術產品,后續會一一為大家介紹。
3.2 工程編碼規范
代碼風格上的一致性,借助于Lint工具和類型檢查器,既約束了代碼風格,有規避了語法錯誤。
編碼風格
ESLint 負責JavaScript 的語法檢測
StyleLint 負責 CSS 的語法檢查及排版優化
Prettier 負責所有文件的格式化
在團隊的工程體系下,可以使用 Jia 命令來觸發代碼進行檢查和自動修復。其背后使用code-spec-unid,他將ESLint、Stylelint、Prettier 三者結合使用,Prettier 的配置為基礎,去覆蓋 ESLint 與 Stylelint 的配置中關于代碼格式化的部分,讓 ESLint 與 Stylelint 專注于做 JS 與 CSS的語法檢查, 而所有的代碼格式化工作交由 Prettier 完成。
jia check
注:jia命令為快手電商前端自研開發工具,后續會為大家介紹。
提交規范
Husky 結合 Lint-staged 與 CommitLint 規范代碼提交
提交消息時須遵循約定格式 <type>: <subject>,type 是 提交的類別,subject 是對提交的簡短描述。注意冒號后有空格,比如:git commit -m 'feat: 增加國際化功能'。以下是 type 的枚舉:
1. upd:更新某功能(不是 feat, 不是 fix)
4. docs:文檔(documentation)
6. refactor:重構(即不是新增功能,也不是修改bug的代碼變動)
兼容性處理
Browserslist 提供瀏覽器兼容性個性化配置
Babel 轉譯高版本 JavaScript 以向后兼容
Postcss 結合其插件 Autoprefixer 為 CSS進行預處理以向后兼容
在實際開發過程中,絕大部分情況無需感知其配置,通過 Jia 命令生成的項目已實現相關配置。
3.4 代碼靜態檢查
1. kdev 代碼掃描(代碼分)
天穹
源碼檢查:對倉庫源碼進行敏感詞、代碼規范、內外部域名、License等檢查,打造符合規范的倉庫源碼
產物檢查:檢查構建產物類型、大小,是否含有sourcemap文件等, 保障線上產物安全穩定
動態檢查:檢查頁面代碼覆蓋率、LCP、FMP等指標,提供性能優化建議,帶您全面感知頁面性能
注:kdev、天穹為快手自研技術產品,后續為大家介紹詳細功能。
該文章在 2024/11/21 17:28:28 編輯過