React 的一些书写规范

本文详细介绍了React的编码规范,包括基本规范、创建模块、命名规则、多行与多属性书写、条件语句、循环、次级渲染等,强调了JSX的使用细节,如单引号与双引号的选择、空格处理、属性的特殊性,以及函数、事件处理、根元素选择、布尔属性值和展开属性的处理方式。遵循这些规范有助于提高代码质量和可读性。

github

更多前端资源和知识整理:https://github.com/ChenMingK/WebKnowledges-Notes

1. 基本规范

  • 每个文件只写一个模块
    • 但是多个无状态模块可以放在单个文件中. eslint: react/no-multi-comp
  • 推荐使用 JSX 语法
  • 不要使用React.createElement,除非从一个非 JSX 的文件中初始化你的 app

2. 创建模块

  • 如果你的模块有内部状态或者是 refs, 推荐使用 class extends React.Component 而不是 React.createClass,也就是说推荐使用 ES6 语法创建 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>
  }
}

// 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 命名实例
// bad
const reservationCard = require('./ReservationCard')

// good
const ReservationCard = require('./ReservationCard')
  • 模块命名: 模块使用当前文件名一样的名称. 比如 ReservationCard.jsx 应该包含名为 ReservationCard 的模块. 但是,如果整个文件夹是一个模块,使用 index.js 作为入口文件,然后直接使用 index.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

// 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.多行书写

  • 需要嵌套元素的任何情况下都应该多行书写
// bad
<div><Header /><div><Main content={...} /></div></div>

// good
<div>
    <Header />
        <div>
            <Main content={...} />
        </div>
</div>
  • 如果出现子节点不是元素,而是文本或变量这样的例外情况,那么应该和父节点的标签写在同一行,并避免产生混淆
// good
<div>
    <Alert>{message}</Alert>
    <Button>Close</Button>
</div>
  • 多行书写时,一定要记得用括号封装它们。JSX 本质上会替换成函数,由于自动分号插入机制的存在,另起一行的函数可能会导致意外结果。例如,在渲染方法内返回 JSX 代码,这也是 React 创建 UI 的方式
    以下示例可以正常运行,因为 div 元素和返回在同一行
return <div> /

但以下代码会失效

return 
    <div />

因为它会转换为以下代码

return;
React.createElement("div", null)

因此你需要将代码语句包裹在括号内:

return (
    <div />
)

2.多个属性的书写

如果元素有多个属性,一行书写一个属性,同时缩进一个层级,并保持结尾括号和开始标签对齐

<button
    foo="bar"
    veryLongPropertyName="baz"
    onSomething={this.handleSomething}
/>

3.条件语句

使用三元条件运算代替if else语句,代码更简洁

<div>
    {isLoggedIn ? <LogoutButton /> : <LoginButton />}
</div>

4.循环

如果在 JSX 模板中编写一个函数并返回数组,那么数组的每一项都会编译为一个元素

<ul>
    {users.map(user => <li>{user.name}</li>)}
</ul>

5.次级渲染

查看以下示例:

renderUserMenu() {
    // JSX 用于用户菜单
}

renderAdminMenu() {
    // JSX 用于管理员菜单
}

render() {
    return (
        <div>
            <h1>Welcome back!</h1>
            {this.userExists && this.renderUserMenu() }
            {this.userIsAdmin && this.renderAdminMenu() }
        </div>
    )
}

这种方法并不总是可以当作最佳实践,显然拆分组件的做法更好,有时这样做只是为了保持渲染方法的简洁。

5. 单引号与双引号

对于JSX 属性值总是使用双引号("), 其他均使用单引号(’)

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

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

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

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

6. 空格

JSX 处理文本和元素间的空格的方式与 HTML 不同,如以下代码片段

<div>
    <span>foo</span>
    bar
    <span>baz</span>
</div>

浏览器解析 HTML 时,以上代码会显示 foo bar baz
而 JSX 会将同一份代码渲染为 foobarbaz,这是因为嵌套的三行代码转译成了 div 元素的独立子元素,没有将空格计算在内。为了得到与 HTML 一致的输出结果,普通的解决方案是在元素间显式插入空格

<div>
    <span>foo</span>
    {' '}
    bar
    {' '}
    <span>baz</span>
</div>

这里用 JavaScriot 表达式封装了空字符串来强制编译器在元素间插入空格
另外注意以下两个书写规范

  • 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行
  • 不要在JSX {} 引用括号里两边加空格
// bad
<Foo bar={ baz } />

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

7. 属性

JSX 不是一门标准语言,需要转译成 JavaScript,因此有些属性无法使用。
如我们需要用className取代class,用htmlfor取代for

<label className="awesome-label" htmlFor="name" />

这是因为 class 和 for 都是 JavaScript 的保留字

8. 函数/方法

  • 当在 render() 里使用事件处理方法时,提前在构造函数里把 this 绑定上去

为什么? 在每次 render 过程中, 再调用 bind 都会新建一个新的函数,浪费资源.

// 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 不允许返回两个函数,因此如果有多个同级元素,需要强制将它们封装在一个父元素中

<div />
<div />
error: Adjacent JSX elements must be wrapped in an enclosing tag

以下写法是有效的

<div>
    <div />
    <div />
</div>

当然这么做有一个明显的缺点就是最外层多了一个不必要的DOM元素,React.Fragment组件能够在不额外创建 DOM 元素的情况下,让render()方法中返回多个元素。

render() {
  return (
    <React.Fragment>
      Some text.
      <h2>A heading</h2>
    </React.Fragment>
  );
}

你也可以使用其简写语法<></>

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 更少。
该特性的用法如下

const foo = { id: 'bar' }
return <div {...foo} />

以上代码的转译结果如下

var foo = { id: 'bar' };
return React.createElement('div', foo);

参考资料

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值