<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從零到一建立前端規(guī)范

          共 19931字,需瀏覽 40分鐘

           ·

          2023-11-09 16:46

          大廠技術  高級前端  Node進階

          點擊上方 程序員成長指北,關注公眾號

          回復1,加入高級Node交流群

          本文適合打算建立前端規(guī)范的小伙伴閱讀

          一、為什么需要規(guī)范

          規(guī)范能給我們帶來什么好處,如果沒有規(guī)范會造成什么后果?這里主要拿代碼規(guī)范來說。

          統(tǒng)一代碼規(guī)范的好處:

          1. 提高代碼整體的可讀性、可維護性、可復用性、可移植性和可靠性,這會從根本上降低開發(fā)成本,也是最重要的一點。

          2. 保證代碼的一致性:軟件系統(tǒng)中最重要的因素之一就是編碼的一致性。如果編碼風格一致,也更加易于維護,因為團隊內任何人都可以快速理解并修改。

          3. 提升團隊整體效率:開發(fā)人員通常需要花費大量的時間來解決代碼質量問題,如果都按照規(guī)范編寫,也有助于團隊盡早發(fā)現問題,甚至完全預防問題,這將提高整個交付過程的效率。

          4. 減少code review期間一系列的爭議,因為缺乏標準,在爭議過程中雙方很難妥協(沒少因為這事爭論過??)。

          若不統(tǒng)一代碼規(guī)范,可能會造成的后果:

          1. 由于缺乏規(guī)范,導致代碼風格不一,增加團隊成員間的心理負擔,極端情況下,某段代碼只有某個人能修改(俗稱屎山??)。

          2. 團隊間協作更加困難:因為開發(fā)人員得適應不同的風格,會導致效率低下(閱讀代碼是我們花費時間最多的地方)。

          3. 在code review期間可能經常為類似的事情做過多的討論。

          4. 影響團隊的生產力和質量,嚴重的甚至會影響團隊和諧。

          二、為什么依然有很多團隊缺乏規(guī)范


          在這件事上,很難達成一致是我認為最重要的原因。并且,僅僅只是擁有規(guī)范也是不夠的:
          • 當開發(fā)人員被要求在短時間內完成任務時,通常會回避質量標準。

          • 團隊中總是有一些有個性的人不會為了團隊去改變自己的習慣。

          • 有些人在會議上就約定達成了一致,在會下依舊我行我素。

          • ...

          三、如何保持規(guī)范

          我曾想通過會議討論的方式來制定規(guī)范,但效果卻差強人意。將失敗的原因總結為大致幾點:

          1. 在會議中,思維很容易發(fā)散,經常出現的情況是討論了很多,卻很難有實際性的成效,在開發(fā)中依然有不少人選擇無視規(guī)則

          2. 正式的會議往往很難組織,大家很難一起有空閑的時間來討論,一次/兩周 都很困難。

          3. 會議中對實際案例分析,提出若干點優(yōu)化建議后,沒有對問題的優(yōu)先級和側重點進行劃分,導致實際效果并不好。

          4. 還有一點也是我自己的原因,組織會議的能力有待提升...??

          經歷了上述的挫敗之后,經過反復復盤總結,決定換一種方式來執(zhí)行:

          1. 對規(guī)范問題進行歸納分析并通過文檔記錄(wiki等),尋找業(yè)內最佳解決方案,在團隊內進行統(tǒng)一。

          2. 采用小步快跑的方式,有問題就解決問題,按照優(yōu)先級和重要性進行排序劃分,依次將問題納入迭代每個迭代重點解決其中幾個即好。

          3. 本迭代的規(guī)范問題絕不留到下個迭代,防止堆積(當然,有時候還是得向項目經理妥協?????)。

          4. 在code review過程中嚴格把關,拒絕睜一只眼??閉一只眼??。

          5. 當團隊成員對具體某個規(guī)范有爭議時,及時討論并定出結論。

          6. 沒有規(guī)則只是為了規(guī)則,制定規(guī)范的目的并不是一定要按照某某標準來執(zhí)行,更多的是團隊成員達成一致即可。

          7. 鼓勵大家大膽的質疑規(guī)則,若不能提高代碼的可讀性,可維護性,可復用性,可移植性和可靠性的規(guī)則都應該被受到質疑。

          8. 以身作則,船頭的方向不能偏航。

          經過兩個月的迭代后,發(fā)現效果出奇的好??,大家的規(guī)范意識普遍增強,當遇到規(guī)范問題時也不再畏畏縮縮,而是大膽的拋出在群里討論。

          四、開發(fā)者需要建立和遵守的規(guī)范

          大致可以劃分成這幾個方向:

          • 開發(fā)流程規(guī)范

          • 代碼規(guī)范

          • git commit規(guī)范

          • 項目文件結構規(guī)范

          • UI設計規(guī)范

          開發(fā)流程規(guī)范

          這里可能有小伙伴有疑問了,開發(fā)流程規(guī)范不是項目經理定的嗎???,跟我有什么關系?

          這里想告訴大家的是,開發(fā)流程在一定程度上應該是由我們自己來掌控。不管是傳統(tǒng)開發(fā)的模式還是敏捷開發(fā)的模式,對于開發(fā)者來說核心依舊是高質高效的完成用戶提出的需求

          筆者曾見過不少開發(fā)者在拿到產品經理的需求后就開始急匆匆的寫代碼,以此來體現他們的高效,但往往卻因為需求理解不到位和前期代碼欠缺設計導致bug率高和返工。

          如何找到適合自己的開發(fā)流程是需要依靠經驗來支撐的,需要反復總結和思考,最終達到高質高效完成的目的。

          說一說筆者自己比較喜歡的開發(fā)流程:

          在接收到需求后應第一時間去了解這個需求的背景是什么?這么做到底有沒有解決用戶的痛點?或者說用戶更深層次的需求是什么?如果團隊的產品經理經驗不豐富,往往可以在這個階段砍掉很多不合理的需求(這一點真的很重要)

          對于復雜大功能往往還需要進行技術方案調研技術方案設計,并輸出詳細的設計文檔。涉及到細節(jié)上,則需要將數據流走向、組件設計等通過腦圖的形式呈現出來。

          代碼規(guī)范之格式化規(guī)范

          由于每個開發(fā)者的IDE不同,即使IDE相同也會因為每個人的配置不一樣導致格式化的結果不一樣。如何確保團隊內開發(fā)人員采用統(tǒng)一的格式化配置呢?

          這里給推薦大家使用 prettier,它內置了一套格式化的規(guī)則,具體配置:

          1). 安裝依賴:

          npm install --save-dev --save-exact prettier 
          // or
          yarn add --dev --exact prettie

          2). 創(chuàng)建一個空配置文件,讓編輯器和其他工具知道你正在使用 Prettier:

          echo {}> .prettierrc.jso

          3). 創(chuàng)建一個.prettierignore文件,讓 Prettier CLI 和編輯器知道哪些文件不能格式化,example:

          # Ignore artifacts:
          dist
          build
          coverag

          4). 配置編輯器(VScode為例)

          IDE中安裝 Prettier-Code Formater 插件:

          找到IDE中設置模塊,搜索 format On Save,勾上這個就可以了。

          現在當我們 Ctrl + S 保存代碼時,插件就會幫助我們自動格式化了。

          這里有小伙伴要問了,要是有人將沒有格式化的代碼提交上去怎么辦?

          這時候就需要在 git commit 的階段自動將提交的代碼進行格式化,這里我們借助工具 husky,它主要可以幫助我們在 git 階段檢查提交消息、運行測試、檢查代碼等。沒接觸過的小伙伴可以去官網了解一下,配置如下:

          • 安裝 husky 和 lint-staged:

          npm install --save-dev husky lint-staged
          npx husky install
          npm set-script prepare "husky install"
          npx husky add .husky/pre-commit "npx lint-staged"
          // or
          yarn add --dev husky lint-staged
          npx husky install
          npm set-script prepare "husky install"
          npx husky add .husky/pre-commit "npx lint-staged
          • 然后將以下內容添加到package.json中:

          {
            "lint-staged": {
              "**/*""prettier --write --ignore-unknown"
            }
          }

          這段配置的意思是:當執(zhí)行 git commit 階段前,先執(zhí)行lint-stagedlint-staged中的內容就是對暫存區(qū)的文件執(zhí)行格式化的命令。
          其他:若使用的是腳手架工具搭建的項目,會自帶eslint配置(eslintConfig)。prettier 和 eslint 會有一些配置上的沖突這個時候需要安裝eslint-config-prettier 以使 ESLint 和 Prettier 相互配合,安裝完后在.eslintrc中配置(以Create-React-App為例):
            "eslintConfig": {
              "extends": [
                "react-app",
                "react-app/jest",
                "prettier"
              ]
            }

          這樣就可以用"prettier"的部分規(guī)則覆蓋前面的規(guī)則,讓它們都能正常工作。

          代碼規(guī)范之JS/TS規(guī)范

          JS/TS主流的大致有這幾種:

          • Airbnb JavaScript Style Guide

          • Google JavaScript Style Guide

          • Idiomatic JavaScript Style Guide

          • JavaScript Standard Style Guide

          • jQuery JavaScript Style Guide

          比較推薦使用 Airbnb JavaScript Style Guide,它在 Github 上足有 12萬 star,幾乎覆蓋了 JavaScript 的每一項特性。

          具體配置:

          1). 安裝依賴

          npm install eslint --save-dev
          // or
          yarn add eslint --de
          2). 生成配置文件
          npm init @eslint/config
          // or
          yarn create @eslint/confi

          跟著終端中的提示,按照自身需求一步步選擇即可。

          有了具體的規(guī)范后,我們同樣需要使用工具去約束:還是通過在git commit階段校驗,若不通過則取消提交。

          配置(還是在 package.json 中的 lint-staged ):
            "lint-staged": {
              "**/*""prettier --write --ignore-unknown"//格式化
              "src/*""eslint --ext .js,.ts,.tsx"  //進行eslint校驗
            }

          注意:這里如果選用的Typescript,則會默認使用@typescript-eslint/parser解析器,官方為了追求更快的解析速度,并不會對.ts文件中的類型進行檢查,只會做語法檢測。

          如果需要對類型也進行檢測,需要在extends中加上plugin:@typescript-eslint/recommended-requiring-type-checking

          索性這里換了另一種方式:在pre commit 中執(zhí)行yarn run tsc,這里的意思是對項目中ts文件進行類型檢測,默認會讀取根目錄中的tsconfig.json配置。

          這種方式并不完美,它的弊端就在于全量檢測,如果項目不大還好,若是項目代碼量夠多,檢測10-20s也是常有的事。

          代碼規(guī)范之CSS規(guī)范

          CSS檢查代碼規(guī)范使用 stylelint 插件,規(guī)范則推薦使用 stylelint-config-standard:

          1). 安裝

          npm install --save-dev stylelint stylelint-config-standar
          2). 在項目的根目錄中創(chuàng)建一個配置文件.stylelintrc.json,內容如下:
          {
            "extends""stylelint-config-standard"

          3). 解決與prettier配置的沖突:
          npm install --save-dev stylelint-config-prettier

          4). 將下面配置復制到.stylelintrc.json中:

          {
            "extends": ["stylelint-config-standard""stylelint-config-prettier"]
          }
          5). 在 git commitv階段進行檢測:
           "lint-staged": {
              "**/*""prettier --write --ignore-unknown"//格式化
              "src/**.{js,jsx,ts,tsx}""eslint --ext .js,.jsx,.ts,.tsx"//對js文件檢測
              "**/*.{less,css}""stylelint --fix" //對css文件進行檢測
            },

          代碼規(guī)范之自定義其他規(guī)范

          下面列一些團隊內定的其他規(guī)范:

          (1)命名規(guī)范

          變量的命名中應盡量減少縮寫的情況發(fā)生,做到見名知意。

          // ?? 自我感覺良好的縮寫:
          let rContent = 'willen'

          // ?? 無需對每個變量都寫注釋,從名字上就看懂
          let firstName = 'jackie'

          // ?? 從命名無法知道返回值類型
          function showFriendsList() {....} // // 無法辨別函數意圖,返回的是一個數組,還是一個對象,還是true or false?

          // ?? 明確函數意圖,對于返回true or false的函數,最好以should/is/can/has開頭
          function shouldShowFriendsList() {...}
          function isEmpty() {...}
          function canCreateDocuments() {...}
          function hasLicense() {...}
          function sendEmailToUser(user{.... } //動詞開頭,函數意圖就很明

          (2)寫注釋

          在每個文件的頂部明確說明該組件做什么,有沒有業(yè)務理解難點等等,對業(yè)務特殊函數/變量也需要寫注釋

          /**
           * 導航頁面-右邊區(qū)域
           */

           
          const Content=>()=>xxx
           
          const MAX_INPUT_LENGTH = 8//用于限制密碼輸入框

          function Component(props{
            return (
              <>
                {/* 如果用戶沒有訂閱則不展示廣告 */}
                {user.subscribed ? null : <SubscriptionPlans />}
              </>

            )
          }

          (3)變量兜底

          // ?? 對于求值獲取的變量,沒有兜底
          const { data } = getApiRequest();
          data.map((s) => s.id); //沒有考慮data異常的情況,代碼一跑就爆炸

          // ?? 對于求值變量,做好兜底
          const { data = [] } = getApiRequest();
          data.map((s) => s?.id); //沒有考慮data異常的情況,代碼一跑就爆炸

          (4)輔助函數必須是純函數

          // ?? 不要讓功能函數的輸出變化無常
          function plusAbc(a, b, c{  // 這個函數的輸出將變化無常,因為api返回的值一旦改變,同樣輸入函數的a,b,c的值,但函數返回的結果卻不一定相同。
            var c = fetch('../api');
            return a+b+c;
          }

          // ?? 功能函數使用純函數,輸入一致,輸出結果永遠唯一
          function plusAbc(a, b, c{  // 同樣輸入函數的a,b,c的值,但函數返回的結果永遠相同。
            return a+b+c;

          (5)優(yōu)先使用函數式編程

          // ?? 使用for循環(huán)編程
          for(i = 1; i <= 10; i++) {
             a[i] = a[i] +1;
          }

          // ?? 使用函數式編程
          let b = a.map(item => ++item

          (6)優(yōu)先使用函數式組件

          除非需要用到錯誤邊界,否則函數式組件應該是首選方法。

          (7)組件復雜度

          如果一個組件做的事情太多,應適當提取一些邏輯,將其拆分為更小的組件。

          如果提取的組件很復雜,則需要依照一定的規(guī)則和條件一一提取它。

          代碼行數并不是一個客觀的衡量標準,更多是需要考慮責任劃分和抽象。

          (8)用錯誤邊界

          當需要對大量數據進行渲染處理時,需要通過錯誤邊界組件對其進行降級處理。

          function Component() {
            return (
              <Layout>
                <ErrorBoundary>
                  <CardWidget />
                </ErrorBoundary>

                <ErrorBoundary>
                  <FiltersWidget />
                </ErrorBoundary>

                <div>
                  <ErrorBoundary>
                    <ProductList />
                  </ErrorBoundary>
                </div>
              </Layout>

            )

          (9)props參數傳遞

          props一層層傳遞一直是我們很頭疼的一個問題,最核心的問題是不清楚props是從哪個初始組件傳來的,以及props中到底有哪些東西,上下文是什么?

          因此對于傳遞較深的場景我推薦直接使用 context,對于 props 中的內容和上下文通過 TS 來解決。
          // A.tsx
          interface AProps {
            param: string;
          }
          const A = ({ param }: AProps) => {
            return <B param = {param} />;
          };

          // ?? 上下文清晰
          // B.tsx
          const B = ({ param }: { param: AProps['param'] }) => {
            return <div>hello world</div>;
          };

          (10)props傳參數量

          如果超過 5 個props,就該考慮是否拆分該組件。在某些情況下,這是需要對組件進行重構的標志。

          注意:組件使用的props越多,重新渲染的理由就越多。

          (11)避免嵌套三元運算符

          三元運算符在第一級之后變得難以閱讀,雖然看起來節(jié)省了代碼空間,但最好在代碼中明確意圖,保持良好的閱讀性。

          // ?? 不夠清晰,要是再嵌套一層兩層呢
          isSubscribed ? (
            <ArticleRecommendations />
          ) : isRegistered ? (
            <SubscribeCallToAction />
          ) : (
            <RegisterCallToAction />
          )

          // ?? 將判斷邏輯進行拆分
          function CallToActionWidget({ subscribed, registered }{
            if (subscribed) {
              return <ArticleRecommendations />
            }

            if (registered) {
              return <SubscribeCallToAction />
            }

            return <RegisterCallToAction />
          }

          function Component() {
            return (
              <CallToActionWidget
                subscribed={subscribed}
                registered={registered}
              />

            )

          (12)將列表組件封裝成獨立組件

          // ?? 列表渲染和其他邏輯雜糅在一起
          function Component({ topic, page, articles, onNextPage }{
            return (
              <div>
                <h1>{topic}</h1>
                {articles.map(article => (
                  <div>
                    <h3>{article.title}</h3>
                    <p>{article.teaser}</p>
                    <img src={article.image} />
                  </div>
                ))}
                <div>You are on page {page}</div>
                <button onClick={onNextPage}>Next</button>
              </div>

            )
          }

          // ?? 將列表組件提取出來,一目了然
          function Component({ topic, page, articles, onNextPage }{
            return (
              <div>
                <h1>{topic}</h1>
                <ArticlesList articles={articles} />
                <div>You are on page {page}</div>
                <button onClick={onNextPage}>Next</button>
              </div>

            )
          }

          (13)避免嵌套渲染函數

          // ?? 不要將其定義在渲染函數組件中
          function Component() {
            function renderHeader() {
              return <header>...</header>
            }
            return <div>{renderHeader()}</div>
          }

          // ?? 將其抽離到獨立的組件中去
          import Header from '@modules/common/components/Header'

          function Component() {
            return (
              <div>
                <Header />
              </div>

            )
          }

          (14)組件/函數導入導出

          // ?? 在文件頭部導入,順序依次為: 第三方庫 > 公共組件/方法 > 非公共部分組件/方法
          import React from 'react'
          import _ from 'loadsh'
          import Header from '@components/header'
          import Content from './Content'

          // ?? 在底部導出
          export { Content, Header }
          export default Componen

          項目文件結構規(guī)范

          在項目初期若不重視,到了后期就是到處天馬行空,你很難在期望的目錄下找到你想要的文件。

          文件夾名稱全部采用小寫 + "-" 來隔開,index.ts更多是用來做導出作用,要不然最后編輯器中滿屏的index.tsx,很難區(qū)分。
             - src 開發(fā)目錄
                - pages 視圖
                    - module-a 模塊A
                      - components 私有組件
                        - ComA.tsx
                        - ComB.tsx
                      - index.module.less
                      - index.tsx
                      - Content.tsx
                    - module-b 模塊B
                - components 公共組件
                  - index.ts 導出所有組件
                  - header
                    - index.tsx
                    - index.module.less
                    - User.tsx
                    - useGetBaseInfo.hooks.ts
                - routers 路由文件
                - store redux中的數據
                - utils 這里是以utils為后綴
                  - index.ts
                  - a.utils.ts
                  - b.utils.ts
                - hooks 這里是以hooks為后綴
                  - index.ts
                  - a.hooks.ts
                  - b.hooks.ts
                - styles 靜態(tài)資源文件
                - service api請求,這里是以api為后綴
                  - a.api.ts 按照后端微服務進行劃分
                  - b.api.ts
                - constans 常量

          通過對工具函數、hooks、api 等加上后綴,更加容易區(qū)分引入的文件。

          Git commit規(guī)范

          git commit 規(guī)范主要可以幫助開發(fā)人員在 code review 期間更容易理解提交的內容,現在大部分主流 commit 規(guī)范都是基于Angular 團隊的規(guī)范而衍生出來的,它的 message 格式如下:
            <type>(<scope>): <subject>
            <BLANK LINE>
            <body>
            <BLANK LINE>
            <footer
          每個提交消息都包含一個subject、一個body和一個footer (中間使用空行分割),提交信息的任何一行不能超過 100 個字符。

          ?? type主要有以下幾種類型:

          • feat: 一個新特性

          • fix: 修復bug

          • docs: 文檔修改

          • style: 不影響代碼含義的更改(空格、格式、缺少分號等)

          • refactor: 代碼重構

          • perf: 優(yōu)化性能

          • test: 測試用例修改

          • chore: 對構建過程或輔助工具和庫的更改,例如文檔生成

          ?? scope:可以是影響范圍的任何內容。

          ?? subject:包含對更改的簡潔描述,規(guī)則:

          • 使用陳述語句

          • 第一個字母不要大寫

          • 末尾沒有點 (.)

          ?? body:commit 具體修改內容, 可以分為多行,應該包括改變的動機,并與以前的行為進行對比。

          ?? footer: 一些備注, 通常是修復的 bug 的鏈接。

          截取一張開源庫的 commit,example:

          有了規(guī)范后,我們需要通過工具去約束:commitlint。它要做的就是在我們每次提交 git commit 的時候,都會幫我們檢查 commit message 是否符合一定的規(guī)范,如果不符合,就讓這次提交失敗。

          具體配置:
          # 安裝 commitlint cli 和 conventional config
          npm install --save-dev @commitlint/{config-conventional,cli}
          # Windows:
          npm install --save-dev @commitlint/config-conventional @commitlint/cli

          配置要使用的 commitlint 規(guī)則
          echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

          加入到husky中:
          npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
          or
          yarn husky add .husky/commit-msg 'yarn commitlint --edit $1

           UI設計規(guī)范

          優(yōu)秀的開發(fā)者應該反向推動UI的規(guī)范,并且能夠支撐UI規(guī)范的落地。

          UI 規(guī)范的最大好處就是能夠提質提效:

          • 在開發(fā)者的角度,與設計規(guī)范同步形成研發(fā)資產,避免重復造輪子;

          • 在測試的角度,能夠避免重復的無意義走查;

          • 在UI設計師的角度,減少設計成本,提高設計效率,可以快速承接新需求;

          • 站在產品角度,提高產品迭代與優(yōu)化效率,降低試錯成本;

          • 站在用戶角度,解決用戶體驗一致性。

          那到底應該怎么去推動UI規(guī)范?我的做法是先讓設計師去給出一系列的規(guī)范,沒有相關規(guī)范就拉上產品經理一起制定規(guī)范。然后前端建立一套自己的組件庫,再將組件庫提供給UI設計師,以此來相互監(jiān)督是否達成了規(guī)范協議。


          本文轉載自:https://juejin.cn/post/7085257325165936648

          五、總結

          統(tǒng)一規(guī)范的最根本目的是為了保證團隊成員的一致性,從而減少溝通成本,提高開發(fā)效率。
          學會熱愛標準,但要確保它們不是一成不變的。如果制定的規(guī)范不適合您的團隊,請確保可以適應和重寫所需要的任何規(guī)則。它并不是要強制執(zhí)行一種工作方式,而是為了幫助促進團隊之間的互動 ??????。

          Node 社群

              
              


          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

             “分享、點贊在看” 支持一下

          瀏覽 2507
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩一级 | 日曰日天天日 | 色屁屁影院| 国产精品久久久久久无码牛牛章艳 | 日批视频大全在线 |