本文记录React框架入门知识
安装及启动
npm i -g create-react-app //全局安装react的cli
create-react-app hello-react //hello-react是项目名
cd hello-react & npm start //启动项目
基本用法
初始完后,src/index.js内容如下:
import React, { Component } from 'react' //必须引入
import ReactDOM from 'react-dom' //做浏览器应用时必须引入
import './index.css' //引入样式文件
class Header extends Component {
render () { // 必须有render函数,返回的是JSX形式模板
return (
<div>
<h1>Hello React</h1>
</div>
)
}
}
ReactDOM.render( //ReactDOM帮忙渲染到指定节点
<Header />,
document.getElementById('root')
)
所以基本有三个必要步骤:1、引库;2、写组件的render函数;3、ReactDOM渲染到页面。
重要语法点:
-
JSX实际是把html转成JS对象保存,再借助ReactDOM构建真正DOM渲染到页面。原因有两点:1、方便渲染到其他平台,如RN,就是利用这个JS对象,只是不用ReactDOM来渲染而已;2、组件变动时方便做Diff,提升效率
页面渲染过程
- render函数必须返回jsx,并且必须被一个最外层jsx容器包裹,不能平行返回多个(和vue的template一样),return一般还用小括号包裹
...
render () { //ES6函数简写形式
return (
<div>
<div>第一个</div>
<div>第二个</div>
</div>
)
}
...
- jsx中可以用'{}'来插值,内部可以放任何 JavaScript 的代码,包括变量、表达式计算、函数执行等等
render () {
const word = 'is good'
return (
<div>
<h1>Hello World {word}</h1> // vue中是双大括号'{{}}'
</div>
)
}
- 表达式插入不仅仅可以用在标签内部,也可以用在标签的属性上
render () {
const className = 'header'
return (
<div className={className}>
<h1>Hello World</h1>
</div>
)
}
- JSX元素本身可转为JS对象,因此可将JSX元素看成变量
render () {
const isGoodWord = true
const goodWord = <strong> is good</strong> // 引号都不能加,加了就当字符串了,最重要的是这里可以嵌套插值!!!!
const badWord = <span> is not good</span>
return (
<div>
<h1>
Hello World
{isGoodWord ? goodWord : badWord}
</h1>
</div>
)
}
- 自定义的组件都必须要用大写字母开头,普通的 HTML 标签都用小写字母开头
- 事件监听
1、给需要监听事件的元素加上类似于 onClick、onKeyDown 这样的属性即可,本质上也是用了插值运算符,此外,在render里掉方法时都要用this来调。
class Title extends Component {
handleClickOnTitle () {
console.log('Click on title.')
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>Hello World</h1>
)
}
}
2、事件属性名都必须要用驼峰命名法,且只能用在普通的 HTML 的标签上,不能用在组件标签上,如:<Header onClick={this.handleClick} />是无效的。
3、event对象会被传入处理函数,该对象经过React自己的封装,与原生event对象差异不大
4、一般在某个类的实例方法里面的 this 指的是这个实例本身,但react不是,也就是在实例方法里拿不到当前实例,所以需要借助bind来实现。这个模式在react中非常常见。
还可以利用bind来传参数,注意此时事件处理器参数顺序:先接受参数,最后是event
class Title extends Component {
handleClickOnTitle (param,e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle.bind(this,param)}>Hello World</h1>
)
}
}
- state & setState
一个组件的显示形态是可以由它数据状态和配置参数决定的
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class LikeButton extends Component {
constructor () {
super() //构造函数中调父元素初始化方法
this.state = { // 添加自身状态属性
name: 'Tomy',
isLiked: false
}
}
handleClickOnLikeButton () {
this.setState({ //只能通过setState来改变状态,不能直接this.isLiked=××
isLiked: !this.state.isLiked //只设置需要更新的属性,其他属性保持不变,如name
})
}
render () {
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked ? '取消' : '点赞'} 👍
</button>
)
}
}
setState接受函数参数和对象参数。当是对象参数时,多个setState不是实时更新,而是react帮你存在队列里一起更新(为了效率);当是函数参数时,setState操作是实时更新的。
handleClickOnLikeButton () {
this.setState((prevState) => {
return { count: 0 }
})
this.setState((prevState) => {
return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
})
this.setState((prevState) => {
return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
})
// 最后的结果是 this.state.count 为 3
}
当进行多次 setState,实际上组件只会重新渲染一次,而不是多次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件
- props & defaultProps
class LikeButton extends Component {
static defaultProps = {
likedText: '取消',
unlikedText: '点赞'
}
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked
? this.props.likedText
: this.props.unlikedText} 👍
</button>
)
}
}
1、为了使得组件的可定制性更强,在使用组件的时候,可以在标签上加属性来传入配置参数。
2、组件可以在内部通过 this.props 获取到配置参数,组件可以根据 props 的不同来确定自己的显示形态,达到可配置的效果。
3、可以通过给组件添加类属性 defaultProps 来配置默认参数。
4、props 一旦传入,你就不可以在组件内部对它进行修改。但是你可以通过父组件主动重新渲染的方式来传入新的 props,从而达到更新的效果。
- state Vs props
state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。
无状态组件(函数式组件):只能接收props为参数,只提供render的返回值
const HelloWorld = props=> {
const sayHi = event => alert('Hello World')
return (
<div onClick={sayHi}>Hello World</div>
)
}
- 渲染列表常用做法(map)
class Index extends Component {
render () {
return (
<div>
{users.map((user,index) => { //map不改变原数组
return (
<div key={index}>
<div>姓名:{user.username}</div>
<div>年龄:{user.age}</div>
<div>性别:{user.gender}</div>
<hr />
</div>
)
})}
</div>
)
}
}
- 组件生命周期
React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载。
生命周期函数有:
componentWillMount:组件挂载开始之前,也就是在组件调用 render 方法之前调用。
componentDidMount:组件挂载完成以后,也就是 DOM 元素已经插入页面后调用。
componentWillUnmount:组件对应的 DOM 元素从页面中删除之前调用
组件的 state 的初始化工作放在 constructor 里面去做;
在 componentWillMount 进行组件的启动工作,例如 Ajax 数据拉取、定时器的启动;
组件从页面上销毁的时候,有时候需要一些数据的清理,例如定时器的清理,就会放在 componentWillUnmount 里面去做;
需要进行DOM操作时,在componentDidMount中做 - ref
ref 属性可以帮助我们获取已经挂载的元素的 DOM 节点
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.js 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。在函数中我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。
注意:ref对应值是函数,函数以dom为参数;一般只用于普通html标签,不用于自定义组件上。
- props.children
使用自定义组件的时候,可以在其中嵌套 JSX 结构。嵌套的结构在组件内部都可以通过 props.children 获取到,这种组件编写方式在编写容器类型的组件当中非常有用。
ReactDOM.render(
<Card>
<h2>React.js 小书</h2>
<div>开源、免费、专业、简单</div>
订阅:<input />
</Card>,
document.getElementById('root')
)
// Card组件
class Card extends Component {
render () {
return (
<div className='card'>
<div className='card-content'>
{this.props.children}
</div>
</div>
)
}
}
- dangerouslySetHTML 和 style 属性
dangerouslySetHTML设置的值不会被转义
render () {
return (
<div
className='editor-wrapper' //这里用className,而不是class
dangerouslySetInnerHTML={{__html: this.state.content}} /> //这个content不会转义
)
}
style的特殊性:1、接受一个对象,这个对象里面是这个元素的 CSS 属性键值对;2、原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如 font-size 换成 fontSize
<h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小书</h1>
- 组件参数验证PropTypes
// npm install --save prop-types
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Comment extends Component {
static propTypes = {
comment: PropTypes.object.isRequired
}
render () {
const { comment } = this.props
return (
<div className='comment'>
<div className='comment-user'>
<span>{comment.username} </span>:
</div>
<p>{comment.content}</p>
</div>
)
}
}
//
PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
PropTypes.node
PropTypes.element
- 高阶组件
高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。高阶组件的作用其实就是为了组件之间的代码复用。组件可能有着某些相同的逻辑,把这些逻辑抽离出来,放到高阶组件中进行复用。高阶组件内部的包装组件和被包装组件之间通过 props 传递数据 - context 对象
一个组件可以通过 getChildContext 方法返回一个对象,这个对象就是子树的 context,提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证。
如果一个组件设置了 context,那么它的子组件都可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过 contextTypes 来声明你想要的 context 里面的哪些状态,然后可以通过 this.context 访问到那些状态。
// 以Index里包含Header组件为例
class Index extends Component {
static childContextTypes = { // 1、声明state中属性类型,必须写
themeColor: PropTypes.string
}
constructor () {
super()
this.state = { themeColor: 'red' } //2、初始化state
}
getChildContext () {
return { themeColor: this.state.themeColor } //3、用state值生成后代可以共用的context
}
render () {
return (
<div>
<Header />
</div>
)
}
}
// 子组件是这样获取context的
class Header extends Component {
static contextTypes = { //只要声明contextTypes就可以通过this.context使用全局的context了
themeColor: PropTypes.string
}
render () {
return (
<h1 style={{ color: this.context.themeColor }}>React.js 小书标题</h1>
)
}
}
网友评论