react使用
- 1.React概述
- 1.1 什么是React?
- 1.2 React特点
- 2.React的基本使用
- 2.1 React的安装
- 2.2 React的使用
- 3.React脚手架的使用
- 3.1 使用React脚手架初始化项目
- 3.2 在脚手架中使用React
- 3.3 JSX的基本使用
- 3.4 组件
- 3.5 React事件处理
- 3.6 组件中state和setState
- 3.7 表单处理
- 4.组件通讯
- 4.1 组件的props
- 4.2 组件通讯的三种方式
- 4.3 props深入
- 4.4 props校验
- 4.5 组件的生命周期
- 5.Redux
- 5.1 Redux概述
- 5.2 Redux三大核心
- 5.3 Redux的组成
- 5.4 React-Redux
1.React概述
1.1 什么是React?
React是一个用于构建用户界面的JavaScript库。
React主要用来写HTML页面,或构建Web应用。
如果从MVC的角度来看,React仅仅是视图层(V),也就是只负责视图的渲染,而并非提供了完整的M和C的功能。
React起源于Facebook的内部项目,后又用来假设Instagram的网站,并与2013年5月开源。
1.2 React特点
- 声明式
- 基于组件
- 学习一次,随处使用
使用React可以开发Web应用;
使用React可以开发移动端原生应用(react-native);
使用React可以开发VR(虚拟现实)应用(react360)。
2.React的基本使用
2.1 React的安装
安装命令:npm i react react-dom
react包是核心,提供创建元素、组件等功能
react-dom包提供DOM相关功能等
2.2 React的使用
1.引入react和react-dom两个js文件
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
// 或者
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
2.创建React元素
const title = React.createElement('h1',null,'Hello React !!!')
3.渲染React元素到页面中
ReactDOM.render(title, document.getElementById('root'))
3.React脚手架的使用
3.1 使用React脚手架初始化项目
1.安装命令
推荐使用:npx create-react-app my-app
npm init react-app my-app
yarn create react-app my-app
2.npx命令介绍
npm v5.2.0引入的一条命令。
目的:提升包内提供的命令行工具的使用体验。
原来:先安装脚手架包,再使用这个包中提供的命令。
现在:无需安装脚手架包,就可以直接使用这个包提供的命令。
3.yarn介绍
yarn是Facebook发布的包管理器,可以看作是npm的替代品,功能与npm相同。
yarn具有快捷、可靠和安全的特点。
初始化新项目:yarn init
安装包:yarn add 包名称
4.启动
npm run start
3.2 在脚手架中使用React
1.导入react和react-dom两个包。
2.调用React.createElement()方法创建react元素。
3.调用ReactDOM.render()方法渲染react元素到页面中。
import React from 'react'
import ReactDom from 'react-dom'
const title = React.createElement('h1',null,'Hello React !!!')
ReactDOM.render(title, document.getElementById('root'))
4.为什么脚手架可以使用JSX语法?
JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展。
需要使用babel编译处理后,才能在浏览器环境中使用。
create-react-app脚手架中已经默认有该配置,无须手动配置。
编译JSX语法的包为:@babel/preset-react。
3.3 JSX的基本使用
1.注意点
React元素的属性名使用驼峰命名法;
特殊属性名:class -> className、for -> htmlFor、tabindex -> tabIndex。
没有子节点的React元素可以用/>结束。
推荐:使用小括号包裹JSX,从而避免JS中的自动插入分号陷阱。
// 使用小括号包裹JSX
const dv = (
<div>Hello JSX</div>
)
2.JSX中使用JavaScript表达式
嵌入JS表达式:
数据存储在JS中
语法:{ JavaScript 表达式}
const name = 'Jack'
const dv = (
<div>
Hello!{name}
{loadData()}
</div>
)
3.JSX的条件渲染
实现方式:
if/else
三元运算符
逻辑与运算符&&(只有&&前面语句为真才能执行后面的)
4.JSX的列表渲染
语法:数组的map()方法
注意:渲染列表时应添加key属性,key的值要保证唯一,尽量避免使用索引号作为key。
const peopleList = [
{id: 1, name: '张三'},
{id: 2, name: '李四'},
{id: 3, name: '小王'}
]
const list = (
<div className="list">
{peopleList.map(item => <div key={item.id}>{item.name}</div>)}
</div>
)
5.JSX的样式处理
(1)行内样式
<h1 style={{ color: 'red', backgroundColor: 'skyblue'}}>
JSX的样式处理
</h1>
(2)类名——className(推荐)
<h1 className="title">
JSX的样式处理
</h1>
6.JSX总结
JSX是React的核心内容。
JSX在JS代码中写HTML结构,是React声明式的体现。
使用JSX配合嵌入的JS表达式、条件渲染、列表渲染,可以描述任意结构。
推荐使用className的方式给JSX添加样式。
React完全利用JS语言自身的能力来编写UI,而不是造轮子增强HTML功能。
3.4 组件
1.组件的创建方式
(1)使用函数创建,叫做无状态组件,只负责数据的展示(静),无state和生命周期,动态数据都是通过父组件传值子组件通过props接收渲染。
使用JS的函数或箭头函数创建。
函数名必须以大写字母开头。
函数组件必须有返回值,表示该组件的结构,如果返回为null,表示不渲染任何内容。
使用函数名作为组件标签名。
function Hello() {
return(
<div>这是我创建的第一个函数组件!</div>
)
}
// 使用箭头函数创建
const Hello = () => <div>这是我创建的第一个函数组件!</div>
ReactDOM.render(<Hello/>, document.getElementById('root'))
(2)使用类class创建,叫做有状态组件,有生命周期,有this和state。
类组件:使用ES6的class创建的组件。
类名称必须以大写字母开头。
类组件应该继承React.Component父类,从而可以使用父类中提供的方法和属性。
类组件必须使用render()方法,render()方法必须有返回值,表示该组件的结构。
注意:使用定义的变量和方法,必须要加上this,而使用第一种方式定义的不需要使用this,但是第一种定义需要使用const,第二种不需要。
class Hello extends React.Component {
render() {
return <div>这是我创建的第一个函数组件!</div>
}
}
ReactDOM.render(<Hello />, document.getElementById('root'))
注:大部分建议使用无状态组件,因为如果大量用有状态的组件容易触发生命周期和钩子函数,页面会出面加载慢等问题。
(3)抽离为独立的JS文件
组件需要导出,在另一个文件导入即可使用。
export default Hello;
import Hello from "Hello.js";
3.5 React事件处理
1.事件绑定
React事件绑定的语法与DOM事件语法相似。
语法:on+事件名称={事件处理程序},比如:onClick = {() => {}}
注意:React事件采用驼峰命名法,比如:onMouseEnter、onFocus。
在函数组件中绑定事件:
class Hello extends React.Component {
handleClick() {
console.log('单击事件触发了')
}
render() {
return (
<button onClick={this.handleClick}>点击</button>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('root'))
export default Hello;
2.事件对象
可以通过事件处理程序中的参数获取到事件对象。
React中的事件对象叫做:合成事件(对象)。
合成事件:兼容所有浏览器,无须担心跨浏览器兼容性问题。
class Hello extends React.Component {
handleClick(e) {
e.preventDefault()
console.log('单击事件触发了')
}
render() {
return (
<a href="http://www.baidu.com" onClick={this.handleClick}>点我,不会跳转页面</a>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('root'))
export default Hello;
3.6 组件中state和setState
1.state的基本使用
状态state即数据,是组件内部的私有数据,只能在组件内部使用。
state的值是对象,表示一个组件中可与有多个数据。
通过this.state来获取状态。
class Hello extends React.Component {
// 完整写法
constructor(){
super()
// 初始化state
this.state = {
count: 0
}
}
// 简化写法
state = {
count: 0
}
render() {
return (
<div>点击,{this.state.count}</div>
)
}
}
2.setState()修改状态
语法:this.setState({要修改的数据})
作用:修改state,更新UI
思想:数据驱动视图
注意:不能直接修改state的值,必须要用setState修改。
this.setState({
count: this.state.count+1
})
3.事件绑定的this指向
(1)箭头函数
特点:箭头函数自身不绑定this,this指向外部环境。
// 写法1
handleClick() {
// 此处this指向render方法
this.setState({
count: this.state.count+1
})
}
render() {
return (
<div onClick={() => this.handleClick()}>点击{this.state.count}</div>
)
}
// 写法2
handleClick = () => {
this.setState({
count: this.state.count+1
})
}
render() {
return (
<div onClick={this.handleClick}>点击{this.state.count}</div>
)
}
(2)Function.prototype.bind()
直接使用JS函数,函数中this的值会是undefined。
解决方法:利用ES5中的bind()方法,将事件处理程序中的this与组件实例绑定到一起。
class Hello extends React.Component {
constructor(){
super()
// 初始化state
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({
count: this.state.count+1
})
}
render() {
return (
<div onClick={this.handleClick}>点击{this.state.count}</div>
)
}
}
3.7 表单处理
1.受控组件(推荐使用)
HTML中的表单元素是可输入的,也就是有自己的可变状态。
而,React中的可变状态通常保存在state中,并且只能通过setState()方法来修改。
React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值。
受控组件:其值受到React控制的表单元素。
总结:文本框、富文本框、下拉框,操作value属性。
复选框:操作checked属性。
{/* 文本框 */}
<input type="text" value={this.state.txt} onChange={this.handleInput}/>
{/* 富文本框 */}
<textarea value={this.state.content} onChange={this.handleContent}></textarea>
{/* 下拉框 */}
<select value={this.state.city} onChange={this.handleCity}>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
{/* 复选框 */}
<input type="checkbox" value={this.state.isChecked} onChange={this.handleChecked}/>
state = {
txt:'',
content: '',
city: '',
isChecked: false
}
handleInput = (e) => {
this.setState({
txt: e.target.value
})
}
handleContent = (e) => {
this.setState({
content: e.target.value
})
}
handleCity = (e) => {
this.setState({
city: e.target.value
})
}
handleChecked = (e) => {
this.setState({
isChecked: e.target.checked
})
// console.log(this)
}
优化:
给表单元素添加name属性,名称与state定义的相同。
根据表单元素类型获取对应的值。
在change事件处理程序中通过[name]来修改对应的state。
<div>
{/* 文本框 */}
<input type="text" name="txt" value={this.state.txt} onChange={this.handleChange}/>
{/* 富文本框 */}
<textarea name="content" value={this.state.content} onChange={this.handleChange}></textarea>
{/* 下拉框 */}
<select name="city" value={this.state.city} onChange={this.handleChange}>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
{/* 复选框 */}
<input type="checkbox" name="isChecked" value={this.state.isChecked} onChange={this.handleChange}/>
</div>
handleChange = (e) => {
// 获取当前DOM对象
const target = e.target
// 根据类型获取值
const value = target.type === 'checkbox' ? target.checked : target.value
// 获取nama
const name = target.name
this.setState({
[name]: value
})
}
2.非受控组件
(1)调用React.createRef()方法创建一个ref对象
constructor(){
super()
this.txtRef = React.createRef()
}
(2)将创建好的ref对象添加到文本框中
<input type="text" ref={this.txtRef} onChange={this.handleChange}/>
(3)通过ref对象获取到文本框的值
handleChange = () => {
console.log(this.txtRef.current.value)
}
4.组件通讯
4.1 组件的props
组件是封闭的,要接收外部数据应该通过props来实现;
props作用:接收传递组件的数据;
传递数据:给组件标签添加属性;
接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据。
例如:
(1)函数组件
<Hello name="Jack" age={18} />
function Hello(props) {
console.log(props)
return (
<div>接收到的数据:{props.name}</div>
)
}
(2)类组件
<Hello name="Jack" age={18} />
class Hello extends React.Component {
render() {
return (
<div>接收到的数据:{this.props.name}</div>
)
}
}
props特点:
(1)可以给组件传递任意类型的数据;
(2)props是只读的对象,只能读取属性的值,无法修改对象;
(3)注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到props,值为undefined。
constructor(props) {
super(props)
console.log(props)
}
<Hello
name="Jack"
age={18}
colors={['red', 'green', 'blue']}
fn={() => console.log('这是一个函数')}
tag={<p>这是一个p标签</p>}
/>
4.2 组件通讯的三种方式
1.父组件传递数据给子组件
给子组件标签添加属性,子组件通过props接收父组件传递的数据,具体操作见4.1。
2.子组件传递数据给父组件
父组件提供一个回调函数,用于接收数据,将该函数作为属性的值,传递给子组件,子组件再通过props调用此函数将参数传过去。
// 父组件
getChildMsg = (msg) => {
console.log('接收子组件数据', msg)
}
<Child getMsg={this.getChildMsg}/>
// 子组件
this.props.getMsg('参数a')
3.兄弟组件
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态。
思路:状态提升。
公共父组件职责:提供共享状态,提供操作共享状态的方法。
要通讯的子组件只要通过props接收状态或操作状态的方法。
4.Context跨组件传递数据
(1)创建Provider(提供数据)和Consumer(消费数据)两个组件。
(2)使用Provider作为父节点,值放在value传递过去
(3)子组件调用Consumer组件使用data接收数据
实例:
// 兄弟组件通信
import React, { Component } from 'react';
const {Provider, Consumer} = React.createContext()
class Login extends Component {
render() {
return(
<Provider value="pink11">
<div className="App">
<Node />
</div>
</Provider>
)
}
}
const Node = props => {
return (
<div className="node">
<Consumer>
{data => <span>数据--{data}</span>}
</Consumer>
</div>
)
}
4.3 props深入
children属性
表示组件标签的子节点,当组件标签有子节点时,props就会有该属性。
children属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)
function Hello(props) {
return(
<div>
组件的子节点:{props.children}
</div>
)
}
<Hello>
<p>我是子节点</p>
</Hello>
4.4 props校验
1.安装prop-types
yarn add prop-types
npm i prop-types
2.导入prop-types
3.使用组件名propTypes={}来给组件添加校验规则
4.校验规则通过PropTypes对象来指定
import PropTypes from 'prop-types'
function App(props){
return (
<h1>Hi, {props.colors}</h1>
)
}
App.propTypes = {
a: PropTypes.number,
fn: PropTypes.func.isRequired,
tag: PropTypes.element,
filter: PropTypes.shape({
area: PropTypes.string,
price: PropTypes.number
})
}
5.约束规则
常见类型:array、bool、func、number、object、string、symbol
React元素类型:element
必填项:isRequired
特定结构的对象:shape({})
optionalFunc: PropTypes.func,
requiredFunc: PropTypes.func.isRequired,
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
})
4.5 组件的生命周期
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程。
只有类组件才有生命周期。
1.创建时(挂载阶段)
constructor() --> render() --> componentDidMount()
constructor:创建组件时最先执行,作用:初始化state,为事件处理程序绑定this
render:每次组件渲染都会触发,作用:渲染UI,注意不能调用setState()
componentDidMount:组件挂载(完成dom渲染)后,作用:发送网络请求,DOM操作
2.更新时(更新阶段)
执行时机:setState()、forceUpdate()、组件接收到新的props。
说明:以上三者任意一种变化,组件就会重新渲染。
render() --> componentDidUpdate()
componentDidUpdate:组件更新(完成dom渲染)后,作用:发送网络请求,DOM操作
3.卸载
componentWillUnmount:清除定时器
5.Redux
5.1 Redux概述
Redux是一个用于JavaScript状态容器,提供可与预测化的状态管理。
Redux可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。
React除了和React一起使用外,还支持其他界面库,而且它体积小(2kb)。
Redux与React之间是没有关系的,Redux支持React、Angular、jQuery甚至是JavaScript。
5.2 Redux三大核心
1.单一数据源头store
2.State是只读的,只能通过触发action去改变:
store.dispatch({
type: 'add',
index: 1
})
3.使用纯函数来执行修改
为了描述action如何改变state,需要用到reducers。
reducers只是一些纯函数,它接收先前的state和action,并且返回新的state,可以复用、可以控制顺序、传入附加参数。
5.3 Redux的组成
组件 -> store.dispatch()发送action -> reducer -> store
1.State-状态(可以理解为数据)
state分类:
DomainDate:服务端的数据,比如:获取用户信息、商品的列表等等;
UI State:关于当前组件的state,比如:弹框的显示隐藏,受控组件等等;
App State:全局的state,比如:当前是否请求loading、当前路由信息等可能被多个和组件去使用的到的状态。
2.Action-事件
Action是把数据从应用传到store的载体,它是store数据的唯一来源,通过store.dispatch()将action传给store。
Action特点:
Action本质是一个js对象,对象内部必须有一个type属性来表示要执行的动作。
Action只是描述了有事情要发生,并没有描述如何去更新state。
3.Reducer
Reducer本质就是一个函数,用来响应发送过来的action,然后经过处理,把state发送给store。
注意:在Reducer函数中,必须要有return返回值,这样store才能接收到数据。
函数会接收两个参数,第一个是初始化state,第二个是action。
const initState = {...}
rootReducer = (state = initState, action) => {
... return {...}
}
4.Store
Store是把action和reducer联系到一起的对象。
主要职责:
维持应用的state;
提供getState()方法获取state;
提供dispatch()方法发送action;
提供subscribe()来注册监听;
提供subscribe()返回值来注销监听;
import { createStore } from 'redux'
const store = createStore(传递reducer)
5.Redux的使用
(1)创建store文件夹,存放index.js、reducer.js、action.js
index.js
import { createStore } from 'redux'
import { reducer } from './reducer'
const store = createStore(reducer)
export default store
reducer.js
const initState = {
name:'默认的initState'
}
const reducer = (state = initState, action) => {
switch(action.type){
case 'send':
return Object.assign({}, state, action);
default:
return state;
}
}
module.exports = {
reducer
}
action.js
const sendAction = () => {
console.log('调用了sendAction--')
return {
type: 'send',
value: 'send发送了'
}
}
module.exports = {
sendAction
}
(2)创建组件A
import React, { Component } from 'react';
import store from '../store'
import {sendAction} from '../store/action'
class ComA extends Component {
handle = () => {
store.dispatch(sendAction())
}
componentDidMount() {
store.subscribe(()=>{
// 接收数据
console.log('subscribe---', store.getState())
})
}
render() {
return (
<div>
<button onClick={this.handle}>点击</button>
</div>
)
}
}
export default ComA;
5.4 React-Redux
1.React-Redux中两个最重要的成员
Provider:
Provider包裹在根组件最外层,使所有子组件都可以拿到state。
Provider接收store作为props,然后通过context往下传递,这样react中任何组件都可以通过context获取到store。
connect:
connect使组件跟store来进行关联。
2.安装
npm install redux
npm install react-redux
或
yarn add redux
yarn add react-redux
3.React-Redux的使用
(1)创建store文件夹,存放index.js、reducer.js、action.js
index.js
import { createStore } from 'redux'
import { reducer } from './reducer'
const store = createStore(reducer)
export default store
reducer.js
const initState = {
count:0
}
const reducer = (state = initState, action) => {
switch(action.type){
case 'add':
return {
count: state.count + 1
};
case 'plus':
return {
count: state.count - 1
};
default:
return state;
}
}
module.exports = {
reducer
}
action.js
const mapDispatchToProps = dispatch => {
return {
addAction:()=>{
dispatch({
type: 'add'
})
},
plusAction:()=>{
dispatch({
type: 'plus'
})
}
}
}
const mapStateToProps = state => {
return state
}
module.exports = {
mapDispatchToProps,
mapStateToProps
}
(2)在根组件使用Provider
import React, { Component } from 'react';
import ComA from './ComA'
import ComB from './ComB'
import store from '../store1'
import {Provider} from 'react-redux'
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="App">
<ComA></ComA>
<ComB></ComB>
</div>
</Provider>
)
}
}
export default App;
(3)创建组件A,用来传递数据
import React, { Component } from 'react';
import {connect} from 'react-redux'
import {mapDispatchToProps} from '../store1/action'
class ComA extends Component {
add = () => {
this.props.addAction()
}
plus = () => {
this.props.plusAction()
}
render() {
console.log(this.props)
return (
<div>
<button onClick={this.add}>+</button>
<button onClick={this.plus}>-</button>
</div>
)
}
}
export default connect(null, mapDispatchToProps)(ComA);
(4)创建组件B,用来接收数据
import React, { Component } from 'react';
import {connect} from 'react-redux'
import {mapStateToProps} from '../store1/action'
class ComB extends Component {
render() {
return (
<div>{this.props.count}</div>
)
}
}
export default connect(mapStateToProps)(ComB);