没有合适的资源?快使用搜索试试~ 我知道了~
React Notes React笔记
需积分: 10 30 下载量 158 浏览量
2017-11-12
13:17:08
上传
评论
收藏 9.01MB PDF 举报
温馨提示
试读
182页
React 笔记,涉及react router, mobx, create react app等
资源推荐
资源详情
资源评论
通过实例,学习编写 React 组件的“最佳实践”
Lucas HC
2 个月前
现在前端程序员都知道,React 是组件化的。当我开始学习 React 的时候,我记得当时已经存在了很多不同编写组件的方式了。如
今,React 社区已经愈发成熟,但是对于组件正确编写姿势却没有一个相对完备的指导。
这篇文章仅从作者的观点出发,来谈一谈我们究竟应该如何来写高质量的 React 组件。来谈一谈我们究竟应该如何来写高质量的 React 组件。
在开始前,需要说明以下几个问题:
这篇文章以及代码实例,都采用了 ES6 或者 ES7 的写法;
对于一些基本概念不再进行科普;
适合有 React 初级经验的读者阅读,内容较为简单;
如果有任何问题,欢迎留言交流。
基于 Class 的组件是 状态化 的,包含有自身方法、生命周期函数、组件内状态等。最佳实践包括但不限于以下一些内容:
我很喜欢 CSS in JavaScript 这一理念。在 React 中,我们可以为每一个 React 组件引入相应的 CSS 文件,这一“梦想”成为了
现实。在下面的代码示例,我把 CSS 文件的引入与其他依赖隔行分开,以示区别:
1. import React, {Component} from 'react'
2. import {observer} from 'mobx-react'
3. import ExpandableForm from './ExpandableForm'
4. import './styles/ProfileContainer.css'
当然,这并不是真正意义上的 CSS in JS,具体实现其实社区上有很多方案。我的 Github 上 fork 了一份各种 CSS in JS 方案的
多维度对比,感兴趣的读者可以参考这里:HOUCe/css-in-js。
在编写组件过程中,一定要注意初始状态的设定。同时,利用 ES6 模块化的知识,我们确保该组件暴露都是 “export default”
形式,方便其他模块(组件)的调用和团队协作。
1. import React, {Component} from 'react'
2. import {observer} from 'mobx-react'
3. import ExpandableForm from './ExpandableForm'
4. import './styles/ProfileContainer.css'
5.
6. export default class ProfileContainer extends Component {
7. state = { expanded: false }
8. ......
通过实例,学习编写 React 组件的“最佳实践”通过实例,学习编写 React 组件的“最佳实践”
基于 Class 的组件最佳实践(Class Based Components)基于 Class 的组件最佳实践(Class Based Components)
1)引入 CSS 依赖 (Importing CSS)1)引入 CSS 依赖 (Importing CSS)
2)设定初始状态(Initializing State)2)设定初始状态(Initializing State)
3)设定 propTypes 和 defaultProps3)设定 propTypes 和 defaultProps
propTypes 和 defaultProps 都是组件的静态属性。在组件的代码中,这两个属性的设定位置越高越好。这两个属性的设定位置越高越好。 因为这样方便其他阅读代
码者或者开发者自己 review,一眼就能看到这些信息。这些信息就如同组件文档一样,对于理解或熟悉当前组件非常重要。
同样,原则上,你编写的组件都需要有 propTypes 属性。如同以下代码:
1. export default class ProfileContainer extends Component {
2. state = { expanded: false }
3.
4. static propTypes = {
5. model: React.PropTypes.object.isRequired,
6. title: React.PropTypes.string
7. }
8.
9. static defaultProps = {
10. model: {
11. id: 0
12. },
13. title: 'Your Name'
14. }
Functional Components 是指没有状态、没有方法,纯组件。Functional Components 是指没有状态、没有方法,纯组件。 应该最大限度地编写和使用这一类组件。这类组件作为函数,其参数
就是 props, 我们可以合理设定初始状态和赋值。
1. function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
2. const formStyle = expanded ? {height: 'auto'} : {height: 0}
3. return (
4. <form style={formStyle} onSubmit={onSubmit}>
5. {children}
6. <button onClick={onExpand}>Expand</button>
7. </form>
8. )}
在编写组件方法时,尤其是你将一个方法作为 props 传递给子组件时,需要确保 this 的正确指向。我们通常使用 bind 或者 ES6
箭头函数来达到此目的。
1. export default class ProfileContainer extends Component {
2. state = { expanded: false }
3.
4. handleSubmit = (e) => {
5. e.preventDefault()
6. this.props.model.save()
7. }
8.
9. handleNameChange = (e) => {
10. this.props.model.changeName(e.target.value)
11. }
12.
13. handleExpand = (e) => {
14. e.preventDefault()
15. this.setState({ expanded: !this.state.expanded })
16. }
当然,这并不是唯一做法。实现方式多种多样,我专门有一片文章来对比 React 中对于组件 this 的绑定,可以参考:从 React 绑
定 this,看 JS 语言发展和框架设计。
在上面的代码示例中,我们使用了:
4)组件方法(Methods)4)组件方法(Methods)
5)setState 接受一个函数作为参数(Passing setState a Function)5)setState 接受一个函数作为参数(Passing setState a Function)
1. this.setState({ expanded: !this.state.expanded })
这里,关于 setState hook 函数,其实有一个非常“有意思”的问题。React 在设计时,为了性能上的优化,采用了 Batch 思想,
会收集“一波” state 的变化,统一进行处理。就像浏览器绘制文档的实现一样。所以 setState 之后,state 也许不会马上就发state 也许不会马上就发
生变化,这是一个异步的过程。生变化,这是一个异步的过程。
这说明,我们要谨慎地在 setState 中使用当前的 state,因为当前的state 也许并不可靠。 为了规避这个问题,我们可以这样
做:
1. this.setState(prevState => ({ expanded: !prevState.expanded }))
我们给 setState 方法传递一个函数,函数参数为上一刻 state,便保证setState 能够立刻执行。
关于 React setState 的设计, Eric Elliott 也曾经在一篇文章中这么喷过:https://medium.com/javascript-scene/setstate-
gate-abc10a9b2d82#.ftefj7nn2 , 并由此展开了多方“撕逼”。作为围观群众,我们在吃瓜的同时,一定会在大神论道当中收获很
多思想,建议阅读。
如果你对 setState 方法的异步性还有困惑,可以同我讨论,这里不再展开。
这个其实没有太多可说的,仔细观察代码吧:我们使用了解构赋值。除此之外,如果一个组件有很多的 props 的话,每个 props 应每个 props 应
该都另起一行该都另起一行,这样书写上和阅读性上都有更好的体验。
1. export default class ProfileContainer extends Component {
2. state = { expanded: false }
3.
4. handleSubmit = (e) => {
5. e.preventDefault()
6. this.props.model.save()
7. }
8.
9. handleNameChange = (e) => {
10. this.props.model.changeName(e.target.value)
11. }
12.
13. handleExpand = (e) => {
14. e.preventDefault()
15. this.setState(prevState => ({ expanded: !prevState.expanded }))
16. }
17.
18. render() {
19. const {model, title} = this.props
20.
21. return (
22. <ExpandableForm
23. onSubmit={this.handleSubmit}
24. expanded={this.state.expanded}
25. onExpand={this.handleExpand}>
26. <div>
27. <h1>{title}</h1>
28. <input
29. type="text"
30. value={model.name}
31. onChange={this.handleNameChange}
32. placeholder="Your Name"/>
33. </div>
34. </ExpandableForm>
35. )
36. }}
6)合理利用解构(Destructuring Props)6)合理利用解构(Destructuring Props)
这一条是对使用 mobx 的开发者来说的。如果你不懂 mobx,可以大体扫一眼。 我们强调使用 ES next decorate 来修饰组件使用 ES next decorate 来修饰组件,如
同:
1. @observerexport default class ProfileContainer extends Component {
使用修饰器更加灵活且可读性更高。即便你不使用修饰器,也需要如此暴露你的组件:
1. class ProfileContainer extends Component {
2. // Component code}export default observer(ProfileContainer)
一定要尽量避免以下用法:
1. <input
2. type="text"
3. value={model.name}
4. // onChange={(e) => { model.name = e.target.value }}
5. // ^ Not this. Use the below:
6. onChange={this.handleChange}
7. placeholder="Your Name"/>
不要:
1. onChange = {(e) => { model.name = e.target.value }}
而是:
1. onChange = {this.handleChange}handleChange = (e) => {
2. this.props.model.name = e.target.value}
原因其实很简单,每次父组件 render 的时候,都会新建一个新的函数并传递给 input。 如果 input 是一个 React 组件,这会粗
暴地直接导致这个组件的 re-render,需要知道,Reconciliation 可是 React 成本最高的部分。
另外,我们推荐的方法,会使得阅读、调试和更改更加方便。
真正写过 React 项目的同学一定会明白,JSX 中可能会存在大量的条件判别,以达到根据不同的情况渲染不同组件形态的效果。 就
像下图这样:
这样的结果是不理想的。这样的结果是不理想的。我们丢失了代码的可读性,也使得代码组织显得混乱异常。多层次的嵌套也是应该避免的。
针对于此,有很对类库来解决 JSX-Control Statements 此类问题,但是与其引入第三方类库的依赖,还不如我们先自己尝试探索解
决问题。
此时,是不是有点怀念if…else?是不是有点怀念if…else? 我们可以使用大括号内包含立即执行函数IIFE,来达到使用 if…else 的目的:
当然,大量使用立即执行函数会造成性能上的损失。所以,同代码可读性上的权衡,还是有必要好好斟酌的。
7)使用修饰器(Decorators)7)使用修饰器(Decorators)
8)闭包(Closures)8)闭包(Closures)
9)JSX中的条件判别(Conditionals in JSX)9)JSX中的条件判别(Conditionals in JSX)
我更加建议的做法是分解此组件,因为这个组件的逻辑已经过于复杂而臃肿了。如何分解?请看我这篇文章:React 组件设计和分解
思考 - 知乎专栏 jianshu^
其实所谓 React “最佳实践”,想必每个团队都有自己的一套“心得”,怎么可能只有一个统一套路?怎么可能只有一个统一套路?
本文指出的几种方法未必对任何读者、团队都适用。本文指出的几种方法未必对任何读者、团队都适用。针对不同的代码风格,开发习惯,拥有自己团队一套“最佳实践”是很有必要
的。从另一方面,也说明了 React 技术栈本身的灵活于强大。
另外,这篇文章并不是我原创,而是翻译了Our Best Practices for Writing React Components一文,并在此基础上进行了较大幅并在此基础上进行了较大幅
度扩展。度扩展。
如果您对React生态有兴趣,同样推荐我的其他几篇文章:
React 组件设计和分解思考
做出Uber移动网页版还不够 极致性能打造才见真章
解析Twitter前端架构 学习复杂场景数据设计
React Conf 2017 干货总结1: React + ES next = ♥
React+Redux打造“NEWS EARLY”单页应用 一个项目理解最前沿技术栈真谛
一个react+redux工程实例
……
Happy Coding!
PS: 作者Github仓库 和 知乎问答链接 欢迎各种形式交流。
总结总结
剩余181页未读,继续阅读
资源评论
qq_40167106
- 粉丝: 2
- 资源: 12
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于Python实现的自动化办公项目.zip
- 基于python实现的基于PyQt5和爬虫的小说阅读系统.zip
- 机械设计整经机上纱自动化sw20非常好的设计图纸100%好用.zip
- Screenshot_20240427_031602.jpg
- 网页PDF_2024年04月26日 23-46-14_QQ浏览器网页保存_QQ浏览器转格式(6).docx
- 直接插入排序,冒泡排序,直接选择排序.zip
- 在排序2的基础上,再次对快排进行优化,其次增加快排非递归,归并排序,归并排序非递归版.zip
- 实现了7种排序算法.三种复杂度排序.三种nlogn复杂度排序(堆排序,归并排序,快速排序)一种线性复杂度的排序.zip
- 冒泡排序 直接选择排序 直接插入排序 随机快速排序 归并排序 堆排序.zip
- 课设-内部排序算法比较 包括冒泡排序、直接插入排序、简单选择排序、快速排序、希尔排序、归并排序和堆排序.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功