1.React环境安装
这里会直接使用 React.js 官网所推荐使用的工具 create-react-app
工具
1 npm install -g create-react-app
2 create-react-app hello-react
3 cd hello-react
4 npm startt
在安装之前可以修改npm 的源,会加速下载:
npm config set registry https://registry.npm.taobao.org
2.使用JSX结构描述UI信息
我们可以用JavaScript对象来描述多有能用HTML表示的UI信息,但是用JavaScript写起来太长了,结构看起来很不清晰,用HTML的方式写起来就方便许多,因此React.js把JavaScript的语法扩展了一下,就有了JSX。
如这样一个DOM元素的结构:
<div>
<h1 className='title'>React 小书</h1>
</div>
用JSX语言表示为:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class Header extends Component {
render () {
return (
<div>
<h1 className='title'>React 小书</h1>
</div>
)
}
}
ReactDOM.render(
<Header />,
document.getElementById('root')
)
经过编译后:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class Header extends Component {
render () {
return (
React.createElement(
"div",
null,
React.createElement(
"h1",
{ className: 'title' },
"React 小书"
)
)
)
}
}
ReactDOM.render(
React.createElement(Header, null),
document.getElementById('root')
);
JSX语言经过Babel编译后,会生成如上图所示的React.createElement
结构,React.createElement
会创建一个JavaScript对象来描述HTML结构的信息,包括标签、属性和子元素等,这样的代码就是合法的JS代码了。所谓的JSX其实就是JS对象。ReactDOM.render
功能就是把组件渲染并且构造DOM树,然后插到页面上某个特定的元素上(这里是id为root
的div
元素)。
总结
-
JSX 是 JavaScript 语言的一种语法扩展,长得像 HTML,但并不是 HTML。
-
React.js 可以用 JSX 来描述你的组件长什么样的。
-
JSX 在编译的时候会变成相应的 JavaScript 对象描述。
-
react-dom
负责把这个用来描述 UI 信息的 JavaScript 对象变成 DOM 元素,并且渲染到页面上。
3.组件中的render方法
一个组件类必须实现一个render
方法,这个render
方法必须要返回一个JSX元素。这里要注意的是必须要用一个外层的JSX元素把素有内容能够包裹起来。返回并列的对个JSX元素是不合法的。
如这种是错误的做法:
render () {
return (
<div>第一个</div>
<div>第二个</div>
)
}
必须要用一个外层元素把里层元素包裹起来:
render () {
return (
<div>
<div>第一个</div>
<div>第二个</div>
</div>
)
}
表达式插入{}
在JSX当中可以插入JavaScript的表达式,返回的结果会相应的炫染道页面上。表达式用{}
包裹。如在JSX中插入变量:
render(){
const word='is good'
return(
<div>
<h1>React {word}</h1>
</div>
)
}
页面上就会显示“React is good”。也可以插入表达式计算{1+2}。也可以把它写成一个函数执行表达式返回:
render () {
return (
<div>
<h1>React {(function () { return 'is good'})()}</h1>
</div>
)
}
{}表达式插入除了可以卸载标签内部,也可以用在标签的属性上,比如插入一个类名:
render () {
const className = 'header'
return (
<div className={className}>
<h1>React</h1>
</div>
)
}
这样可以给div添加一个header
类名。
注意:
在react中用calssName
给元素添加类名,因为class是JS中的关键字,用在类名上不合法。在JSX中用htmlFor
替代for
属性。其他的style
、data-*
等就可以像普通的HTML属性那样直接添加上去。
条件返回
在JSX内部除了可以放置任何表达式内容外,还可以放置JSX,在render函数内可以根据不同条件返回不同的JSX。比如:
render () {
const isGoodWord = true
return (
<div>
<h1>
React
{isGoodWord
? <strong> is good</strong>
: <span> is not good</span>
}
</h1>
</div>
)
}
上面的代码中定义了一个常量isGoodWord
为true
,在JSX中插入例如一个条件表达式{},isGoodWord
为true
就返回<strong> is good</strong>
的JSX内容,否则返回<span> is not good</span>
。
如果在表达式中条件返回null,那么react就什么都不显示。
JSX元素变量
在react总可以直接定义JSX变量,如:
const goodWord = <strong> is good</strong>
const badWord = <span> is not good</span>
代码练习:见练习2
4.组件的组合、嵌套和组件树
组件可以和组件组合在一起、组件内部也可以使用别的组件。这样的组件嵌套构成了组件树。
例如:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Title extends Component {
render () {
return (
<h1>React 小书</h1>
)
}
}
class Header extends Component {
render () {
return (
<div>
<Title />
<h2>This is Header</h2>
</div>
)
}
}
class Main extends Component {
render () {
return (
<div>
<h2>This is main content</h2>
</div>
)
}
}
class Footer extends Component {
render () {
return (
<div>
<h2>This is footer</h2>
</div>
)
}
}
class Index extends Component {
render () {
return (
<div>
<Header />
<Main />
<Footer />
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
在这代码中,Index
组件嵌套了Header、
Main、
Footer组件,而Header
组件又嵌套了Title
组件。用组件树表示为如下图:
代码练习:见练习3
5.事件监听
-
只需要使用
on*
方法就可以为React.js的组件添加事件监听,这些方法兼容所有的浏览器; -
React会给每个事件监听传入一个
event
是对象,这个对象提供的功能和浏览器提供的功能一致,而且它是兼容所有浏览器的; -
Reat.js的事件监听方法需要手动
bind
到当前实例,这种模式在React.js中非常常用;
on*方法
React为我们封装很多类型的事件;
(1)剪贴板事件(Clipboard Events)
onCopy(复制) onCut(剪切) onPaste(粘贴)
(2)文字输入事件(Composition Events)
onCompositionEnd(输入截止) onCompositionStart(输入开始) onCompositionUpdate(输入更新)
(3)键盘事件(KeyboardEvents)
onKeyDown(当按下任何键盘键时) onKeyPress(当用户按下并放开任何字母数字键时发生) onKeyUp(当用户放开人格先前按下的键盘键时发生)
(4)焦点事件(Focus Events)
onFocus(在对象获得焦点时发生) onBlur(在对象失去焦点时发生)
(5)表单事件(Form Events)
onChange(当表单发生改变时发生) onInput(当输入表单时发生) onInvalid(当表单失效时发生) onSubmit(当表单提交时发生)
(6)鼠标事件(Mouse Events)
onClick(点击时) onContextMenu(事件在元素中用户右击鼠标时触发并打开上下文菜单) onDoubleClick(当用户双击某个对象时) onDrag(元素正在拖动时触发) onDragEnd(用户完成元素拖动后触发) onDragEnter(当被鼠标拖动的对象进入其容器范围内时触发此事件) onDragExit(当元素不再是拖动操作的直接选择元素时触发() onDragLeave(当被鼠标拖动的对象离开其容器范围内时触发此事件) onDragOver(当某被拖动的对象在另一对象容器范围内拖动时触发此事件) onDragStart(用户开始拖动元素时触发) onDrop(在一个拖动过程中,释放鼠标键时触发此事件) onMouseDown(鼠标按钮被按下) onMouseEnter(当鼠标指针移动到元素上时触发) onMouseLeave(当鼠标指针移出元素时触发) onMouseMove(鼠标被移动) onMouseOut(鼠标从某元素移开) onMouseOver(鼠标移到某元素之上) onMouseUp(鼠标按键被松开)
(7)选择事件(Selection Events)
onSelect(事件会在文本框中的文本被选中时发生)
(8)触屏事件(Touch Events)
onTouchCancel(当取消触屏操作) onTouchEnd(当移走手指时) onTouchMove(当移动手指时) onTouchStart(当按下手指时)
(9)UI事件(UI Events)
onScroll(元素滚动时执行)
(10)滚轮事件(wheel Events)
onWheel(在鼠标滚轮在元素上下滚动时触发、同样可以在触摸板上滚动或放大缩小区域时触发(如笔记本上的触摸板))
(11)媒体事件(Media Events)
onAbort(在视频/音频(audio/video)终止加载时触发) onCanPlay(在视频(video)准备开始播放时执行 ) onCanPlayThrough(当视频可以正常播放且无需停顿时执行 ) onDurationChange(当视频的时长发生变化时执行) onEmptied(期播放列表为空时触发 ) onEncrypted(当视频加密时发生) onEnded(当音频播放结束后执行) onError(当音频加载出错时执行) onLoadedData(在当前帧的数据可用时执行) onLoadedMetadata(在视频的元数据加载后执行) onLoadStart(在视频开始加载时执行) onPause(在视频暂停时执行) onPlay(当视频播放时执行) onPlaying(在视频暂停后准备开始播放时执行) onProgress(视频正在下载时执行) onRateChange(当视频的播放速度发生改变时执行) onSeeked(在用户重新定位视频的播放位置后执行) onSeeking( 在用户开始重新定位视频的播放位置时执行) onStalled(在浏览器尝试获取媒体数据且数据不可用时执行) onSuspend(在浏览器读取媒体数据中止时执行) onTimeUpdate(在当前播放位置改变时执行) onVolumeChange(当视频的音量改变时执行) onWaiting(在视频由于要播放下一帧而需要缓冲时执行)
(12)图片事件(Image Events)
onLoad(图片加载时触发) onError(图片加载出错时触发)
(13)动画事件(Animation Events)
onAnimationStart(动画开始时执行) onAnimationEnd(动画完成时执行) onAnimationIteration( 动画重复播放时触发)
(14)过渡事件
onTransitionEnd(过渡结束时触发)
(15)其他事件(Other Events)
onToggle(当元素打开或者关闭时执行)
Events
React.js中的Event事件和普通浏览器的Event事件不一样,它将浏览器提供的Event事件进行了封装,可以兼容所有的浏览器。
有关this和bind
错误示例:
class Title extends Component {
handleClickOnTitle (e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
控制台输出为:undefined
正确示例:
class Title extends Component {
handleClickOnTitle (e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle.bind(this)}>React 小书</h1>
)
}
}
输出为:Title
在React.js中调用你传给它的方法时,并不是通过对象方法的方式调用(this.handleCliclkOnTitle
),而是直接通过(handleCliclOnTitle
),所以事件监听函数并不能通过this
获取到实例。
如果你想在事件函数当中使用当前的实例,你需要手动地将实例方法 bind
到当前实例上再传入给 React.js
bind的作用会把实例方法绑定到当前实例上
代码练习:见练习1
6.组件的state和setState
-
Reat中的state是用来存储组件状态变化的;
-
setState方法由父类
Component
所提供,当我们调用这个函数的时候,Reat会更新组件的状态state
,并且重新调用render方法,然后再把render
方法所渲染的最新的内容显示到页面上; -
setState方法接受一个对象或者函数作为参数;
-
当调用setState时并不会马上修改state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到
state
当中,然后再触发组件更新。如下面的例子:
constructor (props) { super(props) this.state = { isLiked: false } } handleClickOnLikeButton () { console.log(this.state.isLiked) this.setState({ isLiked: !this.state.isLiked }) console.log(this.state.isLiked) }
输出:false false
第二次不是true的原因是setState并不会马上修改state的状态,而是React会把你传进来的状态缓存进来,稍后才会帮你更新到state
上。所以我们获取到的还是之前的state。
但是可以采用让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
}
代码练习:见练习4
7.配置组件的props
props可以让我们的组件达到可配置性的效果。每个组件都可以接受一个对象包含了所有我们对这个组件的配置。
如:
class LikeButton extends Component {
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
const likedText = this.props.likedText || '取消'
const unlikedText = this.props.unlikedText || '点赞'
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked ? likedText : unlikedText} 👍
</button>
)
}
}
从render函数中我们可以看到,通过this.props
获取组件的参数,如果``this.props里面有我们需要的参数我们就采用它的属性没有就采用默认的属性。
那么props是怎么传进去的呢
在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值,例如上面的组件LikeButton
在别的组件中使用它的时候,我们可以给把参数加在它的标签属性中:
class Index extends Component {
render () {
return (
<div>
<LikeButton likedText='已赞' unlikedText='赞' />
</div>
)
}
}
我们给LikeButton
组件定义了两个标签属性作为组件的参数,组件内部就可以通过this.props
来访问到这些配置参数了。
补充
可以把任何类型的数据作为组件的参数,包括字符串、数字、对象、数组、甚至是函数等等,因为JSX的表达式可以插入到标签属性上使用。比如:
class Index extends Component {
render () {
return (
<div>
<LikeButton wordings={{likedText: '已赞', unlikedText: '赞'}} />
</div>
)
}
}
我们将likeText
和unlikeText
这两个参数封装到一个叫做wording
的对象参数内,然后传入组件内部。{{}}
其实就是在{}
内部用对象字面了返回一个对象而已。
默认配置defaultProps
默认配置的方式除了设置||
(或)方法外,还有一种方式defaultProps
只用在子组件内定义defaultProps
变量即可。如:
static defaultProps = {
likedText: '取消',
unlikedText: '点赞'
}
props不可变
props一旦传进来就不能改变。要想在组件内改变组件的形态,可以采用setState
的方法主动修改组件的状态
8.state vs props
state
的主要作用是用于组件保存、控制、修改自己的可变状态。state
在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态可以通过 this.setState
方法进行更新,setState
会导致组件的重新渲染。
props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props
,否则组件的 props
永远保持不变。
state
和 props
有着千丝万缕的关系。它们都可以决定组件的行为和显示形态。一个组件的 state
中的数据可以通过 props
传给子组件,一个组件可以使用外部传入的 props
来初始化自己的 state
。但是它们的职责其实非常明晰分明:state
是让组件控制自己的状态,props 是让外部对组件自己进行配置。
记住一个原则:
尽量少地用state
,尽量多地用props
。
因为状态太多,维护和管理起来难度大。
9.渲染列表数据
渲染存放JSX元素的数组
因为JSX的表达式插入{ }
里面可以放任何数据,所以也可以存放数组,如
class Index extends Component {
render () {
return (
<div>
{[
<span>React.js </span>,
<span>is </span>,
<span>good</span>
]}
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
页面上会看到:React.js is good
如果你往 {}
放一个数组,React.js 会帮你把数组里面一个个元素罗列并且渲染出来。
使用map渲染列表数据
class Index extends Component {
render () {
return (
<div>
{users.map((user) => {
return (
<div>
<div>姓名:{user.username}</div>
<div>年龄:{user.age}</div>
<div>性别:{user.gender}</div>
<hr />
</div>
)
})}
</div>
)
}
}
Key!Key!Key!
规则:
对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key
属性,这个 key
必须是每个元素唯一的标识。一般来说,key
的值可以直接后台数据返回的 id
,因为后台的 id
都是唯一的。
如果没有id可以用循环计数器i
作为key
class Index extends Component {
render () {
return (
<div>
{users.map((user, i) => <User key={i} user={user} />)}
</div>
)
}
}
网友评论