React/JSX 編碼規(guī)范

我們在企業(yè)的項(xiàng)目開發(fā)中,制定一套完善有效的編碼規(guī)范是極為有必要的,也是必須的,因?yàn)楹玫木幋a規(guī)范能極大的降低組員開發(fā)時的溝通成本,也更加便于codeReview,而且還能約束自己,養(yǎng)成正確的編程習(xí)慣??傊辛司幋a規(guī)范,你會發(fā)現(xiàn)你慢慢的喜歡上自己寫的代碼了。

目錄

  1. 基本規(guī)范
  2. 創(chuàng)建模塊
  3. 命名
  4. 聲明模塊
  5. 代碼對齊
  6. 單引號還是雙引號
  7. 空格
  8. 屬性
  9. 引用
  10. 括號
  11. 標(biāo)簽
  12. 函數(shù)/方法
  13. 模塊生命周期

基本規(guī)范

  • 每個文件只寫一個模塊
    • 但是多個無狀態(tài)模塊可以放在單個文件中. eslint: react/no-multi-comp
  • 推薦使用JSX語法
  • 不要使用 React.createElement,除非從一個非JSX的文件中初始化你的app

創(chuàng)建模塊

  • 如果你的模塊有內(nèi)部狀態(tài)或者是refs, 推薦使用 class extends React.Component 而不是 React.createClass,也就是說推薦使用ES6語法創(chuàng)建component
// bad
const Listing = React.createClass({
  // ...
  render() {
    return <div>{this.state.hello}</div>;
  }
});

// good
class Listing extends React.Component {
  // ...
  render() {
    return <div>{this.state.hello}</div>;
  }
}
  • 如果你的模塊沒有狀態(tài)或是沒有引用refs, 推薦使用普通函數(shù)(非箭頭函數(shù))而不是類
// bad
class Listing extends React.Component {
  render() {
    return <div>{this.props.hello}</div>;
  }
}

// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
  <div>{hello}</div>
);

// good
function Listing({ hello }) {
  return <div>{hello}</div>;
}

命名

  • 擴(kuò)展名: 使用.jsx作為React組件的擴(kuò)展名
  • 文件名: 使用帕斯卡命名法命名文件,譬如ReservationCard.jsx
  • 引用命名: 使用帕斯卡命名法命名組件和camelCase命名實(shí)例
// bad
const reservationCard = require('./ReservationCard');

// good
const ReservationCard = require('./ReservationCard');

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;
  • 模塊命名: 模塊使用當(dāng)前文件名一樣的名稱. 比如 ReservationCard.jsx 應(yīng)該包含名為 ReservationCard的模塊. 但是,如果整個文件夾是一個模塊,使用 index.js作為入口文件,然后直接使用 index.js 或者文件夾名作為模塊的名稱
// bad
import Footer from './Footer/Footer';

// bad
import Footer from './Footer/index';

// good
import Footer from './Footer';
  • 高階模塊命名: 對于生成一個新的模塊,其中的模塊名 displayName 應(yīng)該為高階模塊名和傳入模塊名的組合. 例如, 高階模塊 withFoo(), 當(dāng)傳入一個 Bar 模塊的時候, 生成的模塊名 displayName 應(yīng)該為 withFoo(Bar)

為什么?一個模塊的 displayName 可能會在開發(fā)者工具或者錯誤信息中使用到,因此有一個能清楚的表達(dá)這層關(guān)系的值能幫助我們更好的理解模塊發(fā)生了什么,更好的Debug

// bad
export default function withFoo(WrappedComponent) {
  return function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }
}

// good
export default function withFoo(WrappedComponent) {
  function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }

  const wrappedComponentName = WrappedComponent.displayName
    || WrappedComponent.name
    || 'Component';

  WithFoo.displayName = `withFoo(${wrappedComponentName})`;
  return WithFoo;
}
  • 屬性命名: 避免使用DOM相關(guān)的屬性來用作其他的用途

為什么?對于style 和 className這樣的屬性名,我們都會默認(rèn)它們代表一些特殊的含義,如元素的樣式,CSS class的名稱。在你的應(yīng)用中使用這些屬性來表示其他的含義會使你的代碼更難閱讀,更難維護(hù),并且可能會引起bug

jsx

// bad
<MyComponent style="fancy" />

// good
<MyComponent variant="fancy" />

聲明模塊

  • 不要使用 displayName 來命名React模塊,而是使用引用來命名模塊, 如 class 名稱
// bad
export default React.createClass({
  displayName: 'ReservationCard',
  // stuff goes here
});

// good
export default class ReservationCard extends React.Component {
}

代碼對齊

  • 遵循以下的JSX語法縮進(jìn)/格式
// bad
<Foo superLongParam="bar"
     anotherSuperLongParam="baz" />

// good, 有多行屬性的話, 新建一行關(guān)閉標(biāo)簽
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
/>

// 若能在一行中顯示, 直接寫成一行
<Foo bar="bar" />

// 子元素按照常規(guī)方式縮進(jìn)
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
>
  <Quux />
</Foo>

單引號還是雙引號

  • 對于JSX屬性值總是使用雙引號("), 其他均使用單引號(')

為什么? HTML屬性也是用雙引號, 因此JSX的屬性也遵循此約定

// bad
<Foo bar='bar' />

// good
<Foo bar="bar" />

// bad
<Foo style={{ left: "20px" }} />

// good
<Foo style={{ left: '20px' }} />

空格

  • 總是在自動關(guān)閉的標(biāo)簽前加一個空格,正常情況下也不需要換行
// bad
<Foo/>

// very bad
<Foo                 />

// bad
<Foo
 />

// good
<Foo />
  • 不要在JSX {} 引用括號里兩邊加空格
// bad
<Foo bar={ baz } />

// good
<Foo bar={baz} />

屬性

  • JSX屬性名使用駱駝式風(fēng)格camelCase
// bad
<Foo
  UserName="hello"
  phone_number={12345678}
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
/>
  • 如果屬性值為 true, 可以直接省略
// bad
<Foo
  hidden={true}
/>

// good
<Foo
  hidden
/>

// good
<Foo hidden />
  • <img> 標(biāo)簽總是添加 alt 屬性. 如果圖片以presentation(感覺是以類似PPT方式顯示?)方式顯示,alt 可為空, 或者<img> 要包含role="presentation"
// bad
<img src="hello.jpg" />

// good
<img src="hello.jpg" alt="Me waving hello" />

// good
<img src="hello.jpg" alt="" />

// good
<img src="hello.jpg" role="presentation" />
  • 不要在 alt 值里使用如 "image", "photo", or "picture"包括圖片含義這樣的詞, 中文也一樣

為什么? 屏幕助讀器已經(jīng)把 img 標(biāo)簽標(biāo)注為圖片了, 所以沒有必要再在 alt 里說明了

// bad
<img src="hello.jpg" alt="Picture of me waving hello" />

// good
<img src="hello.jpg" alt="Me waving hello" />
  • 使用有效正確的 aria role屬性值 ARIA roles
// bad - not an ARIA role
<div role="datepicker" />

// bad - abstract ARIA role
<div role="range" />

// good
<div role="button" />
  • 不要在標(biāo)簽上使用 accessKey 屬性

為什么? 屏幕助讀器在鍵盤快捷鍵與鍵盤命令時造成的不統(tǒng)一性會導(dǎo)致閱讀性更加復(fù)雜

// bad
<div accessKey="h" />

// good
<div />
  • 避免使用數(shù)組的index來作為屬性key的值,推薦使用唯一ID
// bad
{todos.map((todo, index) =>
  <Todo
    {...todo}
    key={index}
  />
)}

// good
{todos.map(todo => (
  <Todo
    {...todo}
    key={todo.id}
  />
))}
  • 對于所有非必須的屬性,總是手動去定義defaultProps屬性

為什么? propTypes 可以作為模塊的文檔說明, 并且聲明 defaultProps 的話意味著閱讀代碼的人不需要去假設(shè)一些默認(rèn)值。更重要的是, 顯示的聲明默認(rèn)屬性可以讓你的模塊跳過屬性類型的檢查

// bad
function SFC({ foo, bar, children }) {
  return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};

// good
function SFC({ foo, bar, children }) {
  return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};
SFC.defaultProps = {
  bar: '',
  children: null,
}
  • 盡可能少地使用擴(kuò)展運(yùn)算符

為什么? 除非你很想傳遞一些不必要的屬性。對于React v15.6.1和更早的版本,你可以給DOM傳遞一些無效的HTML屬性

例外情況:

  • 使用了變量提升的高階組件
function HOC(WrappedComponent) {
  return class Proxy extends React.Component {
    Proxy.propTypes = {
      text: PropTypes.string,
      isLoading: PropTypes.bool
    };

    render() {
      return <WrappedComponent {...this.props} />
    }
  }
}  
  • 只有在清楚明白擴(kuò)展對象時才使用擴(kuò)展運(yùn)算符。這非常有用尤其是在使用Mocha測試組件的時候
export default function Foo {
  const props = {
    text: '',
    isPublished: false
  }

  return (<div {...props} />);
}    
  • 特別提醒:盡可能地篩選出不必要的屬性。同時,使用prop-types-exact來預(yù)防問題出現(xiàn)
 //good
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...relevantProps} />
}

//bad
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...this.props} />
}    

引用

  • 總是在Refs里使用回調(diào)函數(shù)
// bad
<Foo
  ref="myRef"
/>

// good
<Foo
  ref={(ref) => { this.myRef = ref; }}
/>

括號

  • 將多行的JSX標(biāo)簽寫在 ()里
// bad
render() {
  return <MyComponent className="long body" foo="bar">
           <MyChild />
         </MyComponent>;
}

// good
render() {
  return (
    <MyComponent className="long body" foo="bar">
      <MyChild />
    </MyComponent>
  );
}

// good, 單行可以不需要
render() {
  const body = <div>hello</div>;
  return <MyComponent>{body}</MyComponent>;
}

標(biāo)簽

  • 對于沒有子元素的標(biāo)簽來說總是自己關(guān)閉標(biāo)簽
// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />
  • 如果模塊有多行的屬性, 關(guān)閉標(biāo)簽時新建一行
// bad
<Foo
  bar="bar"
  baz="baz" />

// good
<Foo
  bar="bar"
  baz="baz"
/>

函數(shù)/方法

  • 使用箭頭函數(shù)來獲取本地變量
function ItemList(props) {
  return (
    <ul>
      {props.items.map((item, index) => (
        <Item
          key={item.key}
          onClick={() => doSomethingWith(item.name, index)}
        />
      ))}
    </ul>
  );
}
  • 當(dāng)在 render() 里使用事件處理方法時,提前在構(gòu)造函數(shù)里把 this 綁定上去

為什么? 在每次 render 過程中, 再調(diào)用 bind 都會新建一個新的函數(shù),浪費(fèi)資源.

// bad
class extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv.bind(this)} />;
  }
}

// good
class extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv} />;
  }
}
  • 在React模塊中,不要給所謂的私有函數(shù)添加 _ 前綴,本質(zhì)上它并不是私有的

為什么?_ 下劃線前綴在某些語言中通常被用來表示私有變量或者函數(shù)。但是不像其他的一些語言,在JS中沒有原生支持所謂的私有變量,所有的變量函數(shù)都是共有的。盡管你的意圖是使它私有化,在之前加上下劃線并不會使這些變量私有化,并且所有的屬性(包括有下劃線前綴及沒有前綴的)都應(yīng)該被視為是共有的

// bad
React.createClass({
  _onClickSubmit() {
    // do stuff
  },

  // other stuff
});

// good
class extends React.Component {
  onClickSubmit() {
    // do stuff
  }

  // other stuff
}
  • 在 render 方法中總是確保 return 返回值
// bad
render() {
  (<div />);
}

// good
render() {
  return (<div />);
}

模塊生命周期

  • class extends React.Component 的生命周期函數(shù)
  1. 可選的 static 方法
  2. constructor 構(gòu)造函數(shù)
  3. getChildContext 獲取子元素內(nèi)容
  4. componentWillMount 模塊渲染前
  5. componentDidMount 模塊渲染后
  6. componentWillReceiveProps 模塊將接受新的數(shù)據(jù)
  7. shouldComponentUpdate 判斷模塊需不需要重新渲染
  8. componentWillUpdate 上面的方法返回 true, 模塊將重新渲染
  9. componentDidUpdate 模塊渲染結(jié)束
  10. componentWillUnmount 模塊將從DOM中清除, 做一些清理任務(wù)
  11. 點(diǎn)擊回調(diào)或者事件處理器 如 onClickSubmit() 或 onChangeDescription()
  12. render 里的 getter 方法 如 getSelectReason() 或 getFooterContent()
  13. 可選的 render 方法 如 renderNavigation() 或 renderProfilePicture()
  14. render render() 方法
  • 如何定義 propTypes, defaultProps, contextTypes, 等等其他屬性
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};

const defaultProps = {
  text: 'Hello World',
};

class Link extends React.Component {
  static methodsAreOk() {
    return true;
  }

  render() {
    return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>;
  }
}

Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link
  • React.createClass 的生命周期函數(shù),與使用class稍有不同
  1. displayName 設(shè)定模塊名稱
  2. propTypes 設(shè)置屬性的類型
  3. contextTypes 設(shè)置上下文類型
  4. childContextTypes 設(shè)置子元素上下文類型
  5. mixins 添加一些mixins
  6. statics
  7. defaultProps 設(shè)置默認(rèn)的屬性值
  8. getDefaultProps 獲取默認(rèn)屬性值
  9. getInitialState 或者初始狀態(tài)
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
  19. getter methods for render like getSelectReason() or getFooterContent()
  20. Optional render methods like renderNavigation() or renderProfilePicture()
  21. render

總結(jié)

有了好的編碼規(guī)范,這只是第一步,最重要的是,團(tuán)隊(duì)要達(dá)成一致,遵守這套編碼,這樣才能發(fā)揮出規(guī)范強(qiáng)大的作用。

更多文章

  • 作者React Native開源項(xiàng)目OneM【500+ star】地址(按照企業(yè)開發(fā)標(biāo)準(zhǔn)搭建框架完成開發(fā)的):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
  • 作者簡書主頁:包含60多篇RN開發(fā)相關(guān)的技術(shù)文章http://www.itdecent.cn/u/023338566ca5 歡迎小伙伴們:多多關(guān)注,多多點(diǎn)贊
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容