ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
>[info]参考:《React设计模式与最佳实践》 &ensp; React官方文档 &emsp; [React/JSX 编码规范-简书](https://www.jianshu.com/p/2d1c0db6dddb) [TOC] 以下也可以说是 JSX 语法的规范 # 1. 基本规范 * 每个文件只写一个模块 * 但是多个无状态模块可以放在单个文件中. eslint: react/no-multi-comp * 推荐使用 JSX 语法 * 不要使用`React.createElement`,除非从一个非 JSX 的文件中初始化你的 app # 2. 创建模块 * 如果你的模块有内部状态或者是 refs, 推荐使用 class extends React.Component 而不是 React.createClass,也就是说推荐使用 ES6 语法创建 component ```js // 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> } } // bad (relying on function name inference is discouraged) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div> } ``` # 3. 命名 * 扩展名: 使用 .jsx 作为 React 组件的扩展名 * 文件名: 使用帕斯卡命名法命名文件,譬如 ReservationCard.jsx * 引用命名: 使用帕斯卡命名法命名组件和 camelCase 命名实例 ```js // bad const reservationCard = require('./ReservationCard') // good const ReservationCard = require('./ReservationCard') ``` * 模块命名: 模块使用当前文件名一样的名称. 比如 ReservationCard.jsx 应该包含名为 ReservationCard 的模块. 但是,如果整个文件夹是一个模块,使用 index.js 作为入口文件,然后直接使用 index.js 或者文件夹名作为模块的名称 ```js // bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer'; ``` * 高阶模块命名: 对于生成一个新的模块,其中的模块名 displayName 应该为高阶模块名和传入模块名的组合. 例如, 高阶模块 withFoo(), 当传入一个 Bar 模块的时候, 生成的模块名 displayName 应该为 withFoo(Bar) > 为什么?一个模块的 displayName 可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好地 Debug ```js // 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; } ``` # 4. 常见模式 ## 1.多行书写 - 需要嵌套元素的任何情况下都应该多行书写 ```html // bad <div><Header /><div><Main content={...} /></div></div> // good <div> <Header /> <div> <Main content={...} /> </div> </div> ``` - 如果出现子节点不是元素,而是文本或变量这样的例外情况,那么应该和父节点的标签写在同一行,并避免产生混淆 ```html // good <div> <Alert>{message}</Alert> <Button>Close</Button> </div> ``` - 多行书写时,一定要记得用括号封装它们。JSX 本质上会替换成函数,由于自动分号插入机制的存在,另起一行的函数可能会导致意外结果。例如,在渲染方法内返回 JSX 代码,这也是 React 创建 UI 的方式 以下示例可以正常运行,因为 div 元素和返回在同一行 ```js return <div> / ``` 但以下代码会失效 ```js return <div /> ``` 因为它会转换为以下代码 ```js return; React.createElement("div", null) ``` 因此你需要将代码语句包裹在括号内: ```js return ( <div /> ) ``` ## 2.多个属性的书写 如果元素有多个属性,一行书写一个属性,同时缩进一个层级,并保持结尾括号和开始标签对齐 ```html <button foo="bar" veryLongPropertyName="baz" onSomething={this.handleSomething} /> ``` ## 3.条件语句 使用三元条件运算代替`if else`语句,代码更简洁 ```html <div> {isLoggedIn ? <LogoutButton /> : <LoginButton />} </div> ``` ## 4.循环 如果在 JSX 模板中编写一个函数并返回数组,那么数组的每一项都会编译为一个元素 ```html <ul> {users.map(user => <li>{user.name}</li>)} </ul> ``` ## 5.次级渲染 查看以下示例: ```js renderUserMenu() { // JSX 用于用户菜单 } renderAdminMenu() { // JSX 用于管理员菜单 } render() { return ( <div> <h1>Welcome back!</h1> {this.userExists && this.renderUserMenu() } {this.userIsAdmin && this.renderAdminMenu() } </div> ) } ``` 这种方法并不总是可以当作最佳实践,显然拆分组件的做法更好,有时这样做只是为了保持渲染方法的简洁。 # 5. 单引号与双引号 对于JSX 属性值总是使用双引号("), 其他均使用单引号(') ```html // bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} /> ``` # 6. 空格 JSX 处理文本和元素间的空格的方式与 HTML 不同,如以下代码片段 ```html <div> <span>foo</span> bar <span>baz</span> </div> ``` 浏览器解析 HTML 时,以上代码会显示 foo bar baz 而 JSX 会将同一份代码渲染为 foobarbaz,这是因为嵌套的三行代码转译成了 div 元素的独立子元素,没有将空格计算在内。为了得到与 HTML 一致的输出结果,普通的解决方案是在元素间显式插入空格 ```html <div> <span>foo</span> {' '} bar {' '} <span>baz</span> </div> ``` 这里用 JavaScriot 表达式封装了空字符串来强制编译器在元素间插入空格 另外注意以下两个书写规范 * 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行 * 不要在JSX {} 引用括号里两边加空格 ```html // bad <Foo bar={ baz } /> // good <Foo bar={baz} /> ``` # 7. 属性 JSX 不是一门标准语言,需要转译成 JavaScript,因此有些属性无法使用。 如我们需要用`className`取代`class`,用`htmlfor`取代`for` ```jsx <label className="awesome-label" htmlFor="name" /> ``` 这是因为 class 和 for 都是 JavaScript 的保留字 # 8. 函数/方法 * 当在 render() 里使用事件处理方法时,提前在构造函数里把 this 绑定上去 > 为什么? 在每次 render 过程中, 再调用 bind 都会新建一个新的函数,浪费资源. ```jsx // bad class App extends React.Component { onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv.bind(this)} />; } } // good class App extends React.Component { constructor(props) { super(props); this.onClickDiv = this.onClickDiv.bind(this); } onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv} />; } } ``` # 9. 根元素 因为 JSX 元素会转换为 JavaScript 函数,但 JavaScript 不允许返回两个函数,因此如果有多个同级元素,需要强制将它们封装在一个父元素中 ```html <div /> <div /> error: Adjacent JSX elements must be wrapped in an enclosing tag ``` 以下写法是有效的 ```html <div> <div /> <div /> </div> ``` 当然这么做有一个明显的缺点就是最外层多了一个不必要的DOM元素,`React.Fragment`组件能够在不额外创建 DOM 元素的情况下,让`render()`方法中返回多个元素。 ```jsx render() { return ( <React.Fragment> Some text. <h2>A heading</h2> </React.Fragment> ); } ``` 你也可以使用其简写语法`<></>` ```jsx render() { return ( <> Some text. <h2>A heading</h2> More text. <h2>Another heading</h2> Even more text. </> ); } ``` # 10. 布尔属性值 如果设置某个属性却没有赋值,那么 JSX 会默认其值为 true,这种行为类似 HTML 的 disabled 属性 # 11. 展开属性 向子元素传递数据时,不要按引用方式传递整个 JavaScript 对象,而要使用对象的基本类型值以方便校验。这种做法很常见,并且引发的 bug 更少。 该特性的用法如下 ```js const foo = { id: 'bar' } return <div {...foo} /> ``` 以上代码的转译结果如下 ```js var foo = { id: 'bar' }; return React.createElement('div', foo); ```