安装命令:npx install react react-dom
react 包是核心,提供创建元素,组件等功能
react-dom 包提供DOM相关功能等
Document
相比于使用createElement函数创建元素,jsx更直观,代码结构更清晰
// 它被称为 JSX,是一个 JavaScript 的语法扩展。
const element = Hello, world!
;
// 在下面的例子中,我们声明了一个名为 name 的变量,然后在 JSX 中使用它,并将它包裹在大括号中
const name = 'Josh Perez';
const element = Hello, {name}
;
ReactDOM.render(
element,
document.getElementById('root')
);
// 在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
Hello, {formatName(user)}!
);
ReactDOM.render(
element,
document.getElementById('root')
);
在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。
也就是说,你可以在
if
语句和for
循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX
function getGreeting(user) {
if (user) {
return Hello, {formatName(user)}!
;
}
return Hello, Stranger.
;
}
// 属性值指定为字符串字面量
const element = ;
// 使用大括号,来在属性值中插入一个 JavaScript 表达式
const element = ;
// 注意: 因为 JSX 语法上更接近 JavaScript 而不是 HTML,
// 所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,
// 而不使用 HTML 属性名称的命名约定。
// 例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex。
渲染函数组件: 用函数名作为组件标签名
组件标签可以是单标签也可以是双标签
import React from 'react';
import ReactDOM from 'react-dom';
// 函数组件
// 1. 要以大写字母开头
// 2. 函数必须要有一个返回值
// function Hello() {
// return (
// 这是一个函数组件
// )
// }
// 使用箭头函数
const Hello = () => (这是一个函数组件)
// 3. 渲染react元素
ReactDOM.render( , document.getElementById('root'))
类组件: 使用ES6的class创建的组件
约定1: 类名称也必须以大写字母开头
约定2: 类组件应该继承React.Component父类,从而可以使用父类种提供的方法或属性
约定3: 类组件必须提供render() 方法
约定4: render() 方法必须有返回值,表达该组件的结构
import React from 'react';
import ReactDOM from 'react-dom';
// 类组件
class Hello extends React.Component {
render() {
return (
这是一个类组件
)
}
}
// 3. 渲染react元素
ReactDOM.render( , document.getElementById('root'))
- 创建Hello.js文件
- 在js文件种导入React
- 创建组件(函数或类)
- 在Hello.js种导出该组件
- 在index.js种导入Hello组件
- 渲染组件
// Hello.js
import React from 'react'
class Hello extends React.Component {
render() {
return Hello Class Component!
}
}
export default Hello
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './Hello'
// 3. 渲染react元素
ReactDOM.render( , document.getElementById('root'))
函数组件又叫无状态组件,类组件又叫做有状态组件
状态state 及数据
函数组件没有自己的状态, 只负责数据展示
类组件有自己的状态,负责更新ui,让页面‘动’起来
import React from 'react';
import ReactDOM from 'react-dom';
// 事件绑定
// React事件绑定语法与DOM事件语法相似
// on + 事件名称 = {事件处理程序}, 比如: onClick={ () => {}}
// 注意: React事件采用驼峰命名法,比如: onMouseEnter onFocus
// 函数中事件绑定
function Hello() {
function handleClick() {
console.log('元素发生了点击');
}
return 开始点击吧
}
// 类中事件绑定
class Hello extends React.Component {
handleClick() {
console.log('元素发生了点击');
}
render() {
return 类中的事件绑定
}
}
// 3. 渲染react元素
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
// 事件对象
// 可以通过事件处理程序的参数获取到事件对象
// React中的事件对象叫做: 合成事件(对象)
// 合成事件: 兼容所有浏览器,无需担心跨浏览器兼容性问题
function handleClick(e) {
e.preventDefault()
console.log('事件对象',e);
}
const title = (
点击获取事件对象
)
// 渲染react元素
ReactDOM.render(title, document.getElementById('root'))
state 定义数据
setState 修改数据
语法: this.setState({})
setState() 作用: 1. 修改state 2. 更新UI
import React from 'react';
import ReactDOM from 'react-dom';
// state的基本使用
class App extends React.Component {
// constructor() {
// super()
// // 初始化state
// this.state = {
// count: 0
// }
// }
// 简化写法
state = {
count: 0
}
changeCount = () => {
// setState修改数据
this.setState(
{ count: this.state.count + 1}
)
// 错误的修改数据
// this.state.count += 1
}
render() {
return (
{/* 使用数据 this.state */}
计数器:{this.state.count}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
setState() 是异步更新数据的
使用该语法时,后面的setState()不要依赖前面的setState()
多次调用setState(),只会触发一次重新渲染
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
state = {
count: 1
}
changeCount = () => {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
console.log(this.state.count); // 1
}
render() {
return (
{this.state.count}
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
state = {
count: 1
}
changeCount = () => {
// 写成回调函数形式(推荐写法)
// state: 表示最新的state
// props: 表示最新的props
this.setState((state, props) => {
return {
count: state.count + 1
}
})
this.setState(state => ({ count: state.count + 1 }))
// setState的第二个参数 (传入一个回调函数)
// 在状态更新(页面完成重新渲染)后立即执行
this.setState(state => ({ count: state.count + 1 }),
() => {
console.log('在状态更新(页面完成重新渲染)后立即执行');
}
)
}
render() {
return (
计数器: {this.state.count}
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
state = {
count: 1
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
{this.state.count}
{/* {箭头函数中的this指向外部环境,此处为: render() 方法} */}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor() {
super()
this.state = {
count: 1
}
// 使用bind方法
this.onIncrement = this.onIncrement.bind(this)
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
{this.state.count}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
state = {
count: 1
}
// 使用箭头函数,
onIncrement = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
{this.state.count}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
React将state与表单元素值value绑定到一起,由state的值来控制元素的值
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
state = {
// 1. 在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
txt: '',
content: '',
city: 'bj',
isChecked: false
}
onIncrement = (e) => {
this.setState({
count: e.target.value
})
}
handleContent = e => {
this.setState({
content: e.target.value
})
}
handleCity = e => {
this.setState({
city: e.target.value
})
}
handleIsChecked = e => {
this.setState({
isChecked: e.target.checked
})
}
render() {
return (
{/* 2. 给表单元素绑定change事件,将表单元素的值 设置为state的值(控制表单元素值的变化) */}
{/* 文本框 */}
{/* 富文本框 */}
{/* 下拉框 */}
{/* 复选框 */}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor() {
super()
// 调用React.createRef()函数,创建ref
this.txtRef = React.createRef()
}
handleTxt = () => {
// 通过ref对象获取文本框的值
console.log(this.txtRef.current.value);
}
render() {
return (
{/* 将创建好的ref对象添加到文本框中 */}
)
}
}
// 渲染react元素
ReactDOM.render( , document.getElementById('root'))
组件是封闭的,要接收外部数据,应该通过props来实现
props的作用: 接受传递给组件的数据
传递数据: 给组件标签添加属性
接受数据: 函数组件通过参数props接收数据,类组件通过this.props接收数据
import React from 'react';
import ReactDOM from 'react-dom';
// props
// 2. 通过函数参数接收参数
function App(props) {
// props是一个对象
return (
传递过来的name:{props.name}
传递过来的age:{props.age}
)
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
// props
// 2. 通过函数参数接收参数
class App extends React.Component {
render() {
return (
{/* 通过this.props 接收数据 */}
传递过来的name: {this.props.name}
传递过来的age: {this.props.age}
)
}
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
- 可以给组件传递任意类型的数据
- props是只读的对象,
- 使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props
import React from 'react';
import ReactDOM from 'react-dom';
// props
// 2. 通过函数参数接收参数
class App extends React.Component {
constructor(props) {
// 推荐将props传递给父类构造函数
super(props)
console.log(props.name);
}
render() {
return (
{/* 通过this.props 接收数据 */}
传递过来的name: {this.props.name}
传递过来的age: {this.props.age}
)
}
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
表示组件标签的字节点,当组件标签有子节点时,props就会有该属性
与普通的props一样,值可以是任意值(文本,React元素,组件,函数)
import React from 'react';
import ReactDOM from 'react-dom';
// children属性
class App extends React.Component {
render() {
return (
组件的子节点: {this.props.children}
)
}
}
ReactDOM.render(我是子节点 , document.getElementById('root'))
- 下载 prop-types包 npm i prop-types
- 导入prop-types包
- 使用组件名.propTypes = {} 来给组件的props添加校验规则
- 校验规则通过PropTypes对象来指定
import React from 'react';
import ReactDOM from 'react-dom';
// 导入包
import propTypes from 'prop-types'
// children属性
const App = props => {
const arr = props.colors
const lis = arr.map((item, index) => {item.name} )
return {lis}
}
// 添加props校验
App.propTypes = {
// 约定colors属性为array类型
// 如果类型不对,则报出明确的错误,便于分析错误原因
// 常见类型 array bool func number object string
colors: propTypes.array,
// 必选 isRequired
// colors: propTypes.isRequired
// 特定结构对象
// colors: propTypes.shape({
// color: propTypes.string,
// fontSize: propTypes.number
// })
}
const arr = [{ name: 'coder' }, { name: 'jeka' }]
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
// props的默认值
const App = (props) => {
console.log(props);
return (
props的默认值: {props.pageSize}
)
}
// 设置默认值
App.defaultProps = {
pageSize: 10
}
// 当传递了,就使用传递的值
ReactDOM.render( , document.getElementById('root'))
- 父组件提供传递的state数据
- 给子组件标签添加属性,值为state中的数据
- 子组件中通过props接收父组件中传递的数据
import React from 'react';
import ReactDOM from 'react-dom';
// 父子通讯
// 父组件
class App extends React.Component {
state = {
lastName: '李银河'
}
render() {
return (
传递数据给子组件:
)
}
}
// 子组件
class Child extends React.Component {
render() {
return (
父组件传递过来的name: {this.props.name}
)
}
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
- 利用回调函数,父组件提供回调,
- 子组件调用,将要传递的数据作为回调函数的参数
import React from 'react';
import ReactDOM from 'react-dom';
// 子父通讯
// 父组件
class App extends React.Component {
state = {
name: ''
}
// 1. 父组件提供回调函数
childMsg = (data) => {
// 4. 父组件通过参数,拿到子组件传递过来的参数
this.setState({
name: data
})
}
render() {
return (
{/* 2. 父组件通过props把回调函数传递给子组件 */}
子组件:
子组件传递过来的值: {this.state.name}
)
}
}
// 子组件
class Child extends React.Component {
state = {
lastName: '李银河'
}
render() {
return (
// 3. 通过props调用父组件中的回调函数,传递数据
this.props.getMsg(this.state.lastName)}>
点击给父组件传递数据: {this.state.lastName}
)
}
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
import React from 'react';
import ReactDOM from 'react-dom';
// 兄弟通讯
// 父组件
class App extends React.Component {
// 1. 父组件提供公共管理数据
state = {
count: 1
}
// 2. 父组件提供修改公共数据的方法
changeCount = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
{/* 3. 通过props传递给子组件 */}
)
}
}
// 子组件
class Child1 extends React.Component {
render() {
return (
// 子组件使用传递的状态
计数器: {this.props.count}
)
}
}
class Child2 extends React.Component {
render() {
return (
// 子组件通过调用方法来修改状态
)
}
}
// 1. 传递数据
ReactDOM.render( , document.getElementById('root'))
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
import React from 'react';
import ReactDOM from 'react-dom';
// 1.创建context得到两个组件
const { Provider, Consumer } = React.createContext()
class App extends React.Component {
render() {
return (
{/* 2. 通过Provider组件传递数据 */}
)
}
}
class Node extends React.Component {
render() {
return (
)
}
}
class SubNode extends React.Component {
render() {
return (
)
}
}
class Child extends React.Component {
render() {
return (
{/* 3. 使用Consumer使用数据 */}
{data => 传递过来的数据: {data}}
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
// 生命周期
// 1. 创建时(挂载时执行的函数 constructor() -> render() -> componentDidMount)
// 2. 更新时
// (当接收新的 props 时,调用setState(), forceUpdate() 都会重新执行render() 函数)
// render() -> componentDidUpdate()
// 3. 卸载时 componentWillUnmount()
class App extends React.Component {
constructor() {
super()
// 一:创建组件时,最先执行
// 1. 初始化state
this.state = {
count: 1
}
// 2. 为事件处理程序绑定this
console.warn('创建组件时,最先执行')
}
handleClick = () => {
// 强制更新 forceUpdate()
this.forceUpdate()
}
// 钩子函数
shouldComponentUpdate(nextProps,nextState) {
// 更新阶段的钩子函数,在render() 之前执行
// 返回false,阻止组件重新渲染
// 返回true,正常渲染
// nextProps最新的props
// nextState最新的state
//
}
render() {
// 二:每次组件渲染都会触发
// 渲染UI (不能调用setState())
console.warn('每次组件渲染都会触发')
return (
打豆豆
)
}
componentDidMount() {
// 三:组件挂载(完成DOM渲染)后
// 1. 发送网络请求
// 2. DOM操作
console.warn('组件挂载(完成DOM渲染)后')
}
conponentDidUpdate(prevProps) {
// 四:组件更新(完成DOM渲染)后
// 1. 发送网络请求
// 2 DOM操作
console.warn('组件更新(完成DOM渲染)后')
// 如果要使用setState() 必须放在一个if条件中
// 比较更新前后的props是否相同,来决定是否重新渲染组件
if (prevProps.count !== this.props.count) {
this.setState({})
}
}
componentWillUnmount() {
// 五:组件卸载 (从页面上消失)
// 执行清理工作(比如定时器等)
console.warn('组件卸载');
}
}
// 当传递了,就使用传递的值
ReactDOM.render( , document.getElementById('root'))
将复用的state和操作state的方法封装到一个组件中
在使用组件时,添加一个值为函数的prop,通过函数的参数来获取(需要组件内部实现)
import React from 'react';
import ReactDOM from 'react-dom';
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
render() {
// 2. 调用props中的render函数,把值当参数传递进去
return this.props.render(this.state)
}
}
class App extends React.Component {
render() {
return (
render props 模式
{/* 1. 通过给组件传递一个render属性,值为一个函数 */}
鼠标的位置:{mouse.x}, {mouse.y}
} />
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
render() {
// mouse => 鼠标的位置:{mouse.x}, {mouse.y}
console.log(this.props.children);
// 调用传递进来的函数
return this.props.children(this.state)
}
}
class App extends React.Component {
render() {
return (
render props 模式
{/* 把当前函数当作子节点传递进去 */}
{mouse => 鼠标的位置:{mouse.x}, {mouse.y}
}
)
}
}
ReactDOM.render( , document.getElementById('root'))
高阶组件是一个函数, 接收要包装的组件,返回增强后的组件
高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件
- 创建一个函数,名称约定以with开头
- 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
- 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
import React from 'react';
import ReactDOM from 'react-dom';
// 创建高阶组件
function withMouse(WrappedComponent) {
// 该组件提供复用的状态逻辑
return class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
render() {
// 把state和props传给子props
return
}
}
}
// 鼠标位置组件
const Position = (props) => {
return (
鼠标的位置:(x:{props.x},y:{props.y})
)
}
// 获取增强后的组件
const MounsePosition = withMouse(Position)
class App extends React.Component {
render() {
return (
高阶组件
{/* 渲染增强后的组件 */}
)
}
}
ReactDOM.render( , document.getElementById('root'))
使用高阶组件存在的问题: 得到的两个组件名称相同
原因:默认情况下,React使用组件名称作为displayName
解决方法: 为高阶组件设置displayName
作用: 用于设置调式工具(React Developer Tools信息)
// 创建高阶组件
function withMouse(WrappedComponent) {
// 该组件提供复用的状态逻辑
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
render() {
// 把state传给props
return
}
}
// 设置displayName
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
return Mouse
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
纯组件和组件的区别:
- 自动实现了shouldComponentUpdate钩子,分别对比前后两次props和state的值(浅层对比),来决定是否重新渲染组件
import React from 'react';
import ReactDOM from 'react-dom';
// 通过继承 React.PureComponent 来创建一个纯组件
class App extends React.PureComponent {
render() {
return (
纯组件
)
}
}
ReactDOM.render( , document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// 下载路由
// npm i react-router-dom
// 导入路由
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
// 登录页面
function Login() {
return (
登录页面
)
}
// home页面
function Home() {
return (
Home页面
)
}
function App() {
return (
// 配置所有路由
{/* 指定路由出口 */}
}>
}>
)
}
ReactDOM.render(
// 使用BrowserRouter(Router)包裹整个应用
,
document.getElementById('root')
);
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter as Router, Route, Routes, Outlet } from 'react-router-dom'
// home页面
function Home() {
return (
Home页面:
{/* 使用Outlet组件来指定子路由渲染的位置 */}
显示子路由:
)
}
// child页面
function Child() {
return (
home页面的子页面
)
}
// child1页面
function Child1() {
return (
home页面的子二页面
)
}
function App() {
return (
{/* 嵌套路由 */}
}>
{/* index表示默认展示 只有一个index */}
}>
}>
)
}
ReactDOM.render(
,
document.getElementById('root')
);
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter as Router, Route, Routes, Outlet,
useNavigate, Link } from 'react-router-dom'
// login页面
function Login() {
// 1. 使用useNavigate()函数来跳转路由页面
const navigate = useNavigate()
const loginClick = () => {
navigate('/')
}
return (
{/* 2. 使用link标签跳转路由,to属性指定跳转路径 */}
使用link标签跳转路由
)
}
// home页面
function Home() {
return (
Home页面:
显示子路由:
)
}
function App() {
return (
}>
{/* 路由跳转 */}
}>
)
}
ReactDOM.render(
,
document.getElementById('root')
);
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { BrowserRouter as Router, Route, Routes, Outlet, useNavigate, Link,
useParams, useSearchParams } from 'react-router-dom'
// login页面
function Login() {
const navigate = useNavigate()
const loginClick = () => {
navigate('/home')
}
// 3. 使用useSearchParams()函数来获取请求参数
// setSearchParams() 修改数据函数
const [searchParams, setSearchParams] = useSearchParams()
console.log(searchParams.get('list')); // 返回值
console.log(searchParams.getAll('list')); // 返回一个数组
return (
{/* 在后面跟上想要传递的参数 */}
使用link标签跳转路由
)
}
// home页面
function Home() {
const navigate = useNavigate()
const loginClick = () => {
navigate('/login?list=456789&id=我的天')
}
// 2. 使用useParams()函数来获取动态参数
const params = useParams()
console.log(params.id);
return (
Home页面:
)
}
function App() {
return (
{/* 使用:名字 来指定动态路由参数 */}
}>
{/* 路由跳转 */}
}>
)
}
ReactDOM.render(
,
document.getElementById('root')
);
// 路由懒加载
import { lazy } from 'react'
const Home = lazy(() => import('./view/Home'))
const Login = lazy(() => import('./view/Login'))
const Bar = lazy(() => import('./view/Bar'))
export const router = [
{
path: '/',
element:
},
{
path: '/login',
element: ,
children: [
{
path: '/bar',
element:
}
]
}
]
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { router } from './router'
import { useRoutes } from 'react-router-dom'
function App() {
// 使用useRoutes()函数
return useRoutes(router)
}
ReactDOM.render(
,
document.getElementById('root')
);