目录
学习资源来自 React.js 小书
-
基础小知识
- 组件一般都需要继承 React.js 的 component。一个组件类必须要实现一个
render
方法,这个 render 方法必须要返回一个 JSX 元素。但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。 - 在 JSX 当中(1 标签内部 2 标签的属性)你可以插入 JavaScript 的表达式,用大括号 { } 包起来。里面可以放任何 JavaScript 的代码,包括变量、表达式计算、函数。
- 实际上,JSX 元素其实就是JavaScript 对象。所以 JSX 元素可以像普通 js 对象一样自由的赋值给变量,或者作为参数传递、函数的返回值等。
- 自定义组件必须要用大写字母开头,普通的 HTML 标签都用小写字母开头。
- React.js 中的事件监听 :只需要给需要监听事件的元素加上属性类似于 onClick、onKeyDown的属性
class Title extends Component { handleClickOnTitle () { console.log('Click on title.') } render () { return ( <h1 onClick={this.handleClickOnTitle}>React 小书</h1> ) } }
onClick 紧跟着一个表达式插入,这个表达式返回一个 Title 自己的一个实例方法。
这些是 React 封装的不同的类型事件 SyntheticEvent - React
note :没有经过特殊处理的话,这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。
-
event 对象
和普通浏览器一样,事件监听函数会被自动的传入一个 event 对象。我们来尝试一下,当用户点击 h1 的时候,把 h1 的innerHTML 打印出来:
class Title extends Component {
handleClickOnTitle (e) {
console.log(e.target.innerHTML)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
这里的 e.target.innerHTML 是什么意思?
在触发DOM上的事件都会产生一个对象,即事件对象(也称event对象),这里用e接收事件对象。事件对象有很多属性和方法,此处的target属性是获取事件目标,即p元素DOM对象,然后获取其相应的属性
-
事件中的 this
一般在某个类的实例方法里面的 this
指的是这个实例本身。但是你在上面的 handleClickOnTitle
中把 this
打印出来,你会看到 this
是 null
或者 undefined
。
...
handleClickOnTitle (e) {
console.log(this) // => null or undefined
}
...
这是因为 React.js 调用你所传给他的方法的时候,并不是通过对象方法的方式调用(this.handleClickOnTitle),而是直接通过函数调用(handleClickOnTitle),所以事件监听函数内并不能通过 this 获取到实例。
如果想在事件函数当中使用当前的实例,你需要手动的将实例方法 bind 到当前实例上再传入给 React
class Title extends Component {
handleClickOnTitle (e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle.bind(this)}>React 小书</h1>
)
}
}
bind 方法如何使用?
ECMAScript 5 引入了Function.prototype.bind。调用 f.bind(someObject)会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久性(不能被再次绑定,也就是不能被再次赋值!)的被绑定到了 bind 的第一个参数,无论这个函数时如何被调用的。
-
call 和 apply
如果要想把 this 的值从一个上下文传到另一个,就要用到 call 或者 apply 方法。
// 将一个对象作为call和apply的第一个参数,this会被绑定到这个对象。
var obj = {a: 'Custom'};
// 这个属性是在global对象定义的。
var a = 'Global';
function whatsThis(arg) {
return this.a; // this的值取决于函数的调用方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
在函数中的应用
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
// 第一个参数是作为‘this’使用的对象
// 后续参数作为参数传递给函数调用
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// 第一个参数也是作为‘this’使用的对象
// 第二个参数是一个数组,数组里的元素用作函数调用中的参数
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
-
箭头函数
-
props
每个组件都可以接受一个props参数,它是一个对象,包含了所有你对这个组件的配置。
如何把 props 穿进去呢?在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值
组件内部可以通过 this.props 来访问这些配置参数
已经讲过,JSX 的表达式插入可以在标签属性上使用。所以其实可以把任何类型的数据作为组件的参数,报空字符串、数字、对象、数组、甚至是函数。下面举一个函数的例子
class Index extends Component {
render () {
return (
<div>
<LikeButton
wordings={{likedText: '已赞', unlikedText: '赞'}}
onClick={() => console.log('Click on like button!')}/>
</div>
)
}
}
note:当看到 {{ }} 不要慌张,其实就是在 { } 内部用对象字面量返回一个对象而已。
-
组件的 state 和 setState
先来看一个例子
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class LikeButton extends Component {
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked ? '取消' : '点赞'}
-
关于 state 和 props 的总结
state
的主要作用是用于组件保存、控制、修改自己的可变状态。state
在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态可以通过 this.setState
方法进行更新,setState
会导致组件的重新渲染。
props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props
,否则组件的 props
永远保持不变。
state
和 props
有着千丝万缕的关系。它们都可以决定组件的行为和显示形态。一个组件的 state
中的数据可以通过 props
传给子组件,一个组件可以使用外部传入的 props
来初始化自己的 state
。但是它们的职责其实非常明晰分明:state
是让组件控制自己的状态,props
是让外部对组件自己进行配置
-
渲染列表数据
之前说过 JSX 的表达式插入 { } 里面可以放任何数据,如果我们往 { } 里面放一个存放 JSX 元素的数组会如何呢?
React.js 会帮你把数组里面一个个元素罗列出来并且渲染出来。我们知道这一点之后,就能想到遇到数组,我们应该使用 map 方法。
但是为了提高效率,现在需要记住一个简单的规则:对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key 属性,这个 key 必须是每个元素唯一的标识。
-
如何向同级组件传递信息呢?
父组件只需要通过 props 给子组件传入一个回调函数。当用户点击按钮的时候,子组件调用 props 中的回调函数并且将 state 传入该函数。然后父组件向子组件中传递 props。
处理的原则就是:将这种组件之间的共享的状态交给组件最近的公共父节点保管,然后通过 props 把状态传递给子组件,这样就可以在组件之间共享数据了。React 将这种行为叫做 ‘状态提升’。
React 并没有提供好的解决方案来管理这种组件之间的共享状态。在实际项目当中状态提升并不是一个好的解决方案,所以我们后续会引入 Redux 这样的状态管理工具来帮助我们来管理这种共享状态。
-
组件周期
我们把 React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载。
React 为了让我们更好的掌控组件的挂载过程,往上面添加了三个方法:
- componentWillMount :组件挂载开始之前,也就是在组件调用 render 方法之前调用
- componentDidMount :组件挂载完成之后也就是 DOM 元素已经插入页面后调用。
- componentWIllUnmount :组件对应的 DOM 元素从页面中删除之前调用。
-> constructor()
-> componentWillMount()
-> render()
// 然后构造 DOM 元素插入页面
-> componentDidMount()
// ...
// 即将从页面中删除
-> componentWillUnmount()
// 从页面中删除
-
ref 属性
React 当中提供了 ref 属性来帮助我们获取已经挂载的元素的 DOM 节点,你可以给某个 JSX 元素加上 ref 属性。
class AutoFocusInput extends Component {
componentDidMount () {
this.input.focus()
}
render () {
return (
<input ref={(input) => this.input = input} />
)
}
}
ReactDOM.render(
<AutoFocusInput />,
document.getElementById('root')
)
我们给input 元素加了一个 ref 属性,属性值为一个函数。当 input 元素在页面上挂载完成以后,React 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。在函数中,我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。
-
style
React.js 中的元素的 style
属性的用法和 DOM 里面的 style
不大一样,普通的 HTML 中的:
<h1 style='font-size: 12px; color: red;'>React.js 小书</h1>
在 React.js 中你需要把 CSS 属性变成一个对象再传给元素:
<h1 style={{fontSize: '12px', color: 'red'}}>React.js 小书</h1>
style 接受一个对象,这个对象里面是这个元素的 CSS 属性键值对,原来 CSS 属性中带 - 的元素都必须去掉 - 换成驼峰命名,如 font-size 换成 fontSize。
-
PropTypes 和组件参数验证
都说 JavaScript 是一门灵活的语言 —— 这就是像是说“你是个好人”一样,凡事都有背后没有说出来的话。
它的灵活性体现在弱类型上。在构建大型应用程序上其实更适合强类型的语言来构建,比如C/C++/JAVA。为了弥补 JavaScript 在这方面的缺陷,近年来出现了类似 TypeScript 和 Flow等技术。
React 的组件其实是为了构建大型应用程序而生。在你编写了一个组件以后,根本不知道别人会如何使用你的组件,往里面传什么乱七八糟的参数。
于是 React 就提供了这么一种机制,让你可以给组件的配置参数加上类型验证。他可以帮助我们验证 props 的参数类型。例如:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Comment extends Component {
static propTypes = {
comment: PropTypes.object
}
render () {
const { comment } = this.props
return (
<div className='comment'>
<div className='comment-user'>
<span>{comment.username} </span>:
</div>
<p>{comment.content}</p>
</div>
)
}
}
React 提供的 propTypes 提供了一系列的数据类型可以用来配置组件的参数:
PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
PropTypes.node
PropTypes.element
...
更多类型及其用法可以参看官方文档: Typechecking With PropTypes - React
通过 PropTypes 给组件的参数做类型限制,可以在帮助我们迅速定位错误,这在构建大型应用程序的时候特别有用。
- 组件的内容编写顺序如下:
- static 开头的类属性,如defaultProps、propTypes。
- 构造函数,constructor。
- getter / setter。
- 组件生命周期
- _ 开头的私有方法。
- 事件监听方法 ,handle*。
- render* 开头的方法
- render( )方法。
组件的私有方法都用 _ 开头,所有事件监听的方法都用 handle 开头。把事件监听方法传给组件的时候,属性名用 on 开头。例如:
<CommentInput
onSubmit={this.handleSubmitComment.bind(this)} />
这样统一规范处理事件命名会给我们带来语义化组件的好处,监听(on
)CommentInput
的 Submit
事件,并且交给 this
去处理(handle
)。这种规范在多人协作的时候也会非常方便。
-
高阶组件
高阶组件是一个函数,传给它一个组件,它返回一个新的组件。