Airbnb React/JSX 編碼規范
算是最合理的React/JSX編碼規范之一了
內容目錄
- 基本規范
- Class vs React.createClass vs stateless
- 命名
- 聲明模塊
- 代碼對齊
- 單引號還是雙引號
- 空格
- 屬性
- Refs引用
- 括號
- 標簽
- 函數/方法
- 模塊生命周期
- isMounted
Basic Rules 基本規范
- 每個文件只寫一個模塊.
- 但是多個無狀態模塊可以放在單個文件中. eslint:?
react/no-multi-comp
.
- 但是多個無狀態模塊可以放在單個文件中. eslint:?
- 推薦使用JSX語法.
- 不要使用?
React.createElement
,除非從一個非JSX的文件中初始化你的app.
創建模塊
Class vs React.createClass vs stateless
如果你的模塊有內部狀態或者是
refs
, 推薦使用?class extends React.Component
?而不是?React.createClass
?,除非你有充足的理由來使用這些方法.
eslint:?react/prefer-es6-class
?react/prefer-stateless-function
// badconst Listing = React.createClass({ // ...render() { return <div>{this.state.hello}</div>;}}); // goodclass Listing extends React.Component { // ...render() { return <div>{this.state.hello}</div>;}}
如果你的模塊沒有狀態或是沒有引用
refs
, 推薦使用普通函數(非箭頭函數)而不是類:// badclass 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>); // goodfunction Listing({ hello }) { return <div>{hello}</div>;}
Naming 命名
- 擴展名: React模塊使用?
.jsx
?擴展名. - 文件名: 文件名使用駝峰式. 如,?
ReservationCard.jsx
. 引用命名: React模塊名使用駝峰式命名,實例使用駱駝式命名. eslint:?
react/jsx-pascal-case
// badimport reservationCard from './ReservationCard';// goodimport ReservationCard from './ReservationCard';// badconst ReservationItem = <ReservationCard />;// goodconst reservationItem = <ReservationCard />;
模塊命名: 模塊使用當前文件名一樣的名稱. 比如?
ReservationCard.jsx
?應該包含名為?ReservationCard
的模塊. 但是,如果整個文件夾是一個模塊,使用?index.js
作為入口文件,然后直接使用?index.js
?或者文件夾名作為模塊的名稱:// badimport Footer from './Footer/Footer';// badimport Footer from './Footer/index';// goodimport Footer from './Footer';
高階模塊命名: 對于生成一個新的模塊,其中的模塊名?
displayName
?應該為高階模塊名和傳入模塊名的組合. 例如, 高階模塊?withFoo()
, 當傳入一個?Bar
?模塊的時候, 生成的模塊名?displayName
?應該為?withFoo(Bar)
.為什么?一個模塊的?
displayName
?可能會在開發者工具或者錯誤信息中使用到,因此有一個能清楚的表達這層關系的值能幫助我們更好的理解模塊發生了什么,更好的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相關的屬性來用作其他的用途。
為什么?對于
style
?和?className
這樣的屬性名,我們都會默認它們代表一些特殊的含義,如元素的樣式,CSS class的名稱。在你的應用中使用這些屬性來表示其他的含義會使你的代碼更難閱讀,更難維護,并且可能會引起bug。// bad<MyComponent style="fancy" /> // good<MyComponent variant="fancy" />
Declaration 聲明模塊
不要使用?
displayName
?來命名React模塊,而是使用引用來命名模塊, 如 class 名稱.// badexport default React.createClass({ displayName: 'ReservationCard', // stuff goes here });// goodexport default class ReservationCard extends React.Component { }
Alignment 代碼對齊
遵循以下的JSX語法縮進/格式. eslint:?
react/jsx-closing-bracket-location
// bad <Foo superLongParam="bar"anotherSuperLongParam="baz" />// good, 有多行屬性的話, 新建一行關閉標簽 <FoosuperLongParam="bar"anotherSuperLongParam="baz" />// 若能在一行中顯示, 直接寫成一行 <Foo bar="bar" />// 子元素按照常規方式縮進 <FoosuperLongParam="bar"anotherSuperLongParam="baz" ><Quux /> </Foo>
Quotes 單引號還是雙引號
對于JSX屬性值總是使用雙引號(
"
), 其他均使用單引號. eslint:?jsx-quotes
為什么? JSX屬性?不能包括轉譯的引號, 因此在雙引號里包括像?
"don't"
?的屬性值更容易輸入. HTML屬性也是用雙引號,所以JSX屬性也遵循同樣的語法.// bad<Foo bar='bar' /> // good<Foo bar="bar" /> // bad<Foo style={{ left: "20px" }} /> // good<Foo style={{ left: '20px' }} />
Spacing 空格
總是在自動關閉的標簽前加一個空格,正常情況下也不需要換行. eslint:?
no-multi-spaces
,?react/jsx-space-before-closing
// bad <Foo/>// very bad <Foo />// bad <Foo/>// good <Foo />
不要在JSX?
{}
?引用括號里兩邊加空格. eslint:?react/jsx-curly-spacing
// bad <Foo bar={ baz } />// good <Foo bar={baz} />
Props 屬性
JSX屬性名使用駱駝式風格
camelCase
.// bad <FooUserName="hello"phone_number={12345678} />// good <FoouserName="hello"phoneNumber={12345678} />
如果屬性值為?
true
, 可以直接省略. eslint:?react/jsx-boolean-value
// bad <Foohidden={true} />// good <Foohidden />
<img>
?標簽總是添加?alt
?屬性. 如果圖片以presentation(感覺是以類似PPT方式顯示?)方式顯示,alt
?可為空, 或者<img>
?要包含role="presentation"
. eslint:?jsx-a11y/img-has-alt
// 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"包括圖片含義這樣的詞, 中文也一樣. eslint:?jsx-a11y/img-redundant-alt
為什么? 屏幕助讀器已經把?
img
?標簽標注為圖片了, 所以沒有必要再在?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. eslint:?jsx-a11y/aria-role
// bad - not an ARIA role <div role="datepicker" />// bad - abstract ARIA role <div role="range" />// good <div role="button" />
不要在標簽上使用?
accessKey
?屬性. eslint:?jsx-a11y/no-access-key
為什么? 屏幕助讀器在鍵盤快捷鍵與鍵盤命令時造成的不統一性會導致閱讀性更加復雜.
// bad <div accessKey="h" />// good <div />
避免使用數組的index來作為屬性
key
的值,推薦使用唯一ID. (為什么?)// bad {todos.map((todo, index) =><Todo{...todo}key={index}/> )}// good {todos.map(todo => (<Todo{...todo}key={todo.id}/> ))}
Refs
總是在Refs里使用回調函數. eslint:?
react/no-string-refs
// bad <Fooref="myRef" />// good <Fooref={ref => { this.myRef = ref; }} />
Parentheses 括號
將多行的JSX標簽寫在?
()
里. eslint:?react/wrap-multilines
// badrender() { return <MyComponent className="long body" foo="bar"><MyChild /></MyComponent>; }// goodrender() { return (<MyComponent className="long body" foo="bar"><MyChild /></MyComponent>); }// good, 單行可以不需要render() {const body = <div>hello</div>; return <MyComponent>{body}</MyComponent>; }
Tags 標簽
對于沒有子元素的標簽來說總是自己關閉標簽. eslint:?
react/self-closing-comp
// bad <Foo className="stuff"></Foo>// good <Foo className="stuff" />
如果模塊有多行的屬性, 關閉標簽時新建一行. eslint:?
react/jsx-closing-bracket-location
// bad <Foobar="bar"baz="baz" />// good <Foobar="bar"baz="baz" />
Methods 函數
使用箭頭函數來獲取本地變量.
function ItemList(props) { return (<ul>{props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))}</ul>); }
當在?
render()
?里使用事件處理方法時,提前在構造函數里把?this
?綁定上去. eslint:?react/jsx-no-bind
為什么? 在每次?
render
?過程中, 再調用?bind
?都會新建一個新的函數,浪費資源.// badclass extends React.Component {onClickDiv() { // do stuff}render() { return <div onClick={this.onClickDiv.bind(this)} />}} // goodclass extends React.Component {constructor(props) {super(props); this.onClickDiv = this.onClickDiv.bind(this);}onClickDiv() { // do stuff}render() { return <div onClick={this.onClickDiv} />}}
在React模塊中,不要給所謂的私有函數添加?
_
?前綴,本質上它并不是私有的.為什么?
_
?下劃線前綴在某些語言中通常被用來表示私有變量或者函數。但是不像其他的一些語言,在JS中沒有原生支持所謂的私有變量,所有的變量函數都是共有的。盡管你的意圖是使它私有化,在之前加上下劃線并不會使這些變量私有化,并且所有的屬性(包括有下劃線前綴及沒有前綴的)都應該被視為是共有的。了解更多詳情請查看Issue#1024, 和?#490?。// badReact.createClass({_onClickSubmit() { // do stuff}, // other stuff}); // goodclass extends React.Component {onClickSubmit() { // do stuff} // other stuff}
在?
render
?方法中總是確保?return
?返回值. eslint:?react/require-render-return
// badrender() {(<div />); }// goodrender() { return (<div />); }
Ordering React 模塊生命周期
class extends React.Component
?的生命周期函數:
- 可選的?
static
?方法 constructor
?構造函數getChildContext
?獲取子元素內容componentWillMount
?模塊渲染前componentDidMount
?模塊渲染后componentWillReceiveProps
?模塊將接受新的數據shouldComponentUpdate
?判斷模塊需不需要重新渲染componentWillUpdate
?上面的方法返回?true
, 模塊將重新渲染componentDidUpdate
?模塊渲染結束componentWillUnmount
?模塊將從DOM中清除, 做一些清理任務- 點擊回調或者事件處理器?如?
onClickSubmit()
?或?onChangeDescription()
render
?里的 getter 方法?如?getSelectReason()
?或?getFooterContent()
- 可選的 render 方法?如?
renderNavigation()
?或?renderProfilePicture()
render
?render() 方法
如何定義?
propTypes
,?defaultProps
,?contextTypes
, 等等其他屬性...import React, { PropTypes } from 'react';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
?的生命周期函數,與使用class稍有不同: eslint:?react/sort-comp
displayName
?設定模塊名稱propTypes
?設置屬性的類型contextTypes
?設置上下文類型childContextTypes
?設置子元素上下文類型mixins
?添加一些mixinsstatics
defaultProps
?設置默認的屬性值getDefaultProps
?獲取默認屬性值getInitialState
?或者初始狀態getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- clickHandlers or eventHandlers?like?
onClickSubmit()
?or?onChangeDescription()
- getter methods for?
render
?like?getSelectReason()
?or?getFooterContent()
- Optional render methods?like?
renderNavigation()
?or?renderProfilePicture()
render
isMounted
不要再使用?
isMounted
. eslint:?react/no-is-mounted
為什么??
isMounted
?反人類設計模式:(), 在 ES6 classes 中無法使用, 官方將在未來的版本里刪除此方法.
? 回到頂部