React作为前端领域最流行的UI库之一,以其高效的虚拟DOM和组件化开发模式深受开发者喜爱。本文从React基础概念出发,详细讲解JSX语法、组件生命周期、状态管理、Hooks等核心知识,同时介绍Context、高阶组件等高级特性。通过实际代码示例,帮助你快速掌握React开发,构建出性能优秀、结构清晰的现代Web应用。
认识React
- React 中文文档1(国内社区):https://react.docschina.org/
- React 中文文档2(官方):https://zh-hans.reactjs.org
React 概述
React 是一个用于构建(动态显示)用户界面
的 JavaScript 库。
🏢 React源起:React 起源于
💡 定位:React本身只关注界面, 其它如:前后台交互、路由管理、状态管理等都由其扩展插件或其它第三方插件搞定
🧩 全家桶:React全家桶包括 react 、 react-router-dom、 redux
React 三个特点
- 1️⃣ 声明式 ==> 命令式编程 arr.filter(item => item.price>80)
- 利用JSX 语法来声明描述动态页面, 数据更新界面自动更新
- 我们不用亲自操作DOM, 只需要更新数据, 界面就会自动更新
- React.createElement() 是命令式
- 2️⃣ 组件化
- 将一个较大较复杂的界面拆分成几个可复用的部分封装成多个组件, 再组合使用
- 组件可以被反复使用
- 3️⃣ 一次学习,随处编写
- 不仅可以开发 web 应用(react-dom),还可以开发原生安卓或ios应用(react-native)
React 开发的网站
以下都是使用React开发的知名网站:
网站名称 | 网址 |
---|---|
MDN | https://developer.mozilla.org/zh-CN/ |
知乎 | https://www.zhihu.com/ |
阿里云 | https://www.aliyun.com/ |
美团 | https://bj.meituan.com/ |
飞猪旅行 | https://www.fliggy.com/ |
安装VSCode插件
开发React应用时,建议安装以下VSCode插件:
- ES7+ React/Redux
- open in browser
React基本使用
基本使用步骤
-
引入两个JS文件( 注意引入顺序 )
<!-- react库, 提供React对象 -->
<script src="../js/react.development.js"></script>
<!-- react-dom库, 提供了ReactDOM对象 -->
<script src="../js/react-dom.development.js"></script> -
在html定义一个根容器标签
<div id="root"></div>
-
创建react元素(类似html元素)
// 返回值:React元素
// 参数1:要创建的React元素名称 =》字符串
// 参数2:元素的属性 =》对象 {id: 'box'} 或者 null
// 后面参数:该React元素的所有子节点 =》文本或者其他react元素
const element = React.createElement(
'h1',
{title: '你好, React!'},
'Hello React!'
) -
渲染 react 元素
// 渲染React元素到页面容器div中
ReactDOM.render(element, document.getElementById('root'))
特殊属性
- class ==》 className
const element = React.createElement(
'h1',
{
title: '你好, React!',
className: 'active'
},
'Hello React!'
)
再来个复杂点的
const title = '北京疫情'
const content = '北京这段时间疫情还在持续中...'
const vDom = React.createElement('div', null,
React.createElement('h2', {title}, '你关注的北京疫情'),
React.createElement('p', null, content)
)
ReactDOM.render(vDom, document.getElementById('root2'))
理解 React 元素
-
也称
虚拟 DOM
(virtual DOM) 或虚拟节点
(virtual Node) -
它就是一个普通的 JS 对象, 它不是真实 DOM 元素对象
类型 特点 性能 虚拟 DOM 属性比较少 较轻
的对象真实 DOM 属性特别多 较重
的对象 -
但它有一些自己的特点
虚拟 DOM 可以转换为对应的真实 DOM => ReactDOM.render方法将虚拟DOM转换为真实DOM再插入页面
虚拟 DOM 对象包含了对应的真实 DOM 的关键信息属性:
标签名 => type: "h1"
标签属性 => props: {title: '你好, React!'}
子节点 => props: {children: 'Hello React!'}
JSX
基本理解和使用
❓ 问题: React.createElement()写起来太复杂了
💡 解决: 推荐使用更加简洁的JSX
JSX 是一种JS 的扩展语法, 用来快速创建 React 元素(虚拟DOM/虚拟节点)
形式上像HTML标签/任意其它标签, 且标签内部是可以套JS代码的
const h1 = <h1 className="active">哈哈哈</h1>
⚠️ 浏览器并不认识 JSX 所以需要引入babel将jsx 编译成React.createElement的形式
babel编译 JSX 语法的包为:@babel/preset-react
运行时编译可以直接使用babel的完整包:babel.js
🔍 线上测试: https://www.babeljs.cn/
<!-- 必须引入编译jsx的babel库 -->
<script src="../js/babel.min.js"></script>
<!-- 必须声明type为text/babel, 告诉babel对内部的代码进行jsx的编译 -->
<script type="text/babel">
// 创建React元素 (也称为虚拟DOM 或 虚拟节点)
const vDom = <h1 title="你好, React2!" className="active">Hello React2!</h1>
// 渲染React元素到页面容器div中
ReactDOM.render(vDom, document.getElementById('root'))
</script>
📝 JSX注意事项:
必须有结束标签 整个只能有一个根标签 空标签可以自闭合
JSX中使用 JS 表达式
- JSX中使用JS 表达式的语法:
{js表达式}
- 作用:
指定动态的属性值和标签体文本
可以是js的表达式, 不能是js的语句
可以是任意基本类型数据值, 但null、undefined和布尔值没有任何显示
可以是一个js数组, 但不能是js对象
可以是react元素对象
style属性值必须是一个包含样式的js对象
let title = 'I Like You'
const vNode = (
<div>
<h3 name={title}>{title.toUpperCase()}</h3>
<h3>{3}</h3>
<h3>{null}</h3>
<h3>{undefined}</h3>
<h3>{true}</h3>
<h3>{'true'}</h3>
<h3>{React.createElement('div', null, 'atguigu')}</h3>
<h3>{[1, 'abc', 3]}</h3>
<h3 title={title} id="name" className="ative" style={{color: 'red'}}></h3>
{/* <h3>{{a: 1}}</h3> */}
</div>
)
条件渲染
if...else
let vDom
if (isLoading) {
vDom = <h2>正在加载中...</h2>
} else {
vDom = <div>加载完成啦!</div>
}
ReactDOM.render(vDom, document.getElementById('root'))
三元表达式
const vDom = isLoading ? <h2>正在加载中2...</h2> : <div>加载完成啦2!</div>
ReactDOM.render(vDom, document.getElementById('root'))
&&
const vDom = isLoading && <h2>正在加载中3...</h2>
ReactDOM.render(vDom, document.getElementById('root'))
// 注意: 只适用于只在一种情况下才有界面的情况
💡 逻辑运算符复习:
表达式1 && 表达式2
如果表达式1对应的boolean为true, 结果就为表达式2的值
如果表达式1对应的boolean为false, 结果就为表达式1的值
表达式1 || 表达式1
如果表达式1对应的boolean为true, 结果就是表达式1的值
如果表达式1对应的boolean为false, 结果就为表达式2的值
列表渲染
- react中可以将数组中的元素依次渲染到页面上
- 可以直接往数组中存储react元素
- 推荐使用数组的 map 方法
- 注意:必须给列表项添加唯一的 key 属性, 推荐使用id作为key, 尽量不要用index作为key
🚀 实例需求: 根据下面的数组显示列表
const courses = [
{id: 1, name: 'React'},
{id: 3, name: 'Vue'},
{id: 5, name: '小程序'}
]
const courses = [
{id: 1, name: 'React'},
{id: 3, name: 'Vue'},
{id: 5, name: '小程序'}
]
const vDom = (
<div>
<h2>前端框架课程列表</h2>
<ul>
{courses.map(c => <li key={c.id}>{c.name}</li>)}
</ul>
</div>
)
ReactDOM.render(vDom, document.getElementById('root'))
样式处理
行内样式
- 样式属性名使用小驼峰命名法
- 如果样式是数值,可以省略单位
<h2 style={{color: 'red', fontSize: 30}}>React style</h2>
类名
- 必须用className, 不能用class
- 推荐, 效率更高些
<h2 className="title">React class</h2>
事件处理
绑定事件
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯 小写。比如:onClick、onFocus 、onMouseEnter
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
const div = <div onClick={事件处理函数}></div>
事件对象
React 根据 W3C 规范来自定义的合成事件, 与原生事件不完全相同
-
处理好了浏览器的兼容性问题
-
阻止事件默认行为不能使用return false, 必须要调用: event.preventDefault()
-
有自己特有的属性, 比如: nativeEvent --原生事件对象
-
input 标签的change监听在输入过程中触发, 而原生是在失去焦点才触发
-
原理:内部绑定的是原生input事件
-
function handleClick1(event) {
console.log(event)
alert(event.target.innerHTML)
}
const handleClick2 = (event) => {
const isOdd = Date.now()%2===1
alert(isOdd)
if (!isOdd) {
// return false // 不起作用
event.preventDefault()
}
}
const vDom = <div>
<button onClick={handleClick1}>点击提示按钮文本</button>
<br/>
<br/>
<a href="http://www.baidu.com" onClick={handleClick2}>奇数才去百度</a>
</div>
ReactDOM.render(vDom, document.getElementById('root'))
案例
-
需求:实现评论列表功能
li> a > [h3 p]
- 如果有评论数据,就展示列表结构 li( 列表渲染 )要包含a标签
- name 表示评论人,渲染 h3
- content 表示评论内容,渲染 p
- 如果没有评论数据,就展示一个 h1 标签,内容为: 暂无评论!
- 用户名的字体25px, 内容的字体20px
- 点击内容区域提示它发表的时间
- 如果有评论数据,就展示列表结构 li( 列表渲染 )要包含a标签
const list = [
{ id: 1, name: 'jack', content: 'rose, you jump i jump', time: '03:21' },
{ id: 2, name: 'rose', content: 'jack, you see you, one day day', time: '03:22' },
{ id: 3, name: 'tom', content: 'jack,。。。。。', time: '03:23' }
]
React的组件
组件允许你将 UI 拆分为独立可复用的代码片段,包括JS/CSS/IMG等。
组件从概念上类似于 JavaScript 函数。它接收参数(即 "props"),内部可以有自己的数据(即 "state"),并返回用于描述页面展示的 React 元素。
一个React应用就是由一个个的React组件组成的
快速创建React项目
react脚手架使用
❓ 问题: JSX 转 JS 和 ES6 转 ES5 语法运行时编译太慢了 💡 解决: 利用 Webpack 进行打包处理
❓ 问题: webpack打包环境搭建太麻烦, 且没有质量保证, 效率低 💡 解决: 使用官方提供的
脚手架
工具 搭建好了webpack打包环境 项目的目录结构
创建React项目
使用 create-react-app:
- 下载 npm i create-react-app -g
- 创建项目命令:
create-react-app 项目名称
也可以利用 npx 来下载 create-react-app 并创建项目
命令: npx create-react-app 项目名称
npx 做的事情:
- 先全局下载 create-react-app
- 执行 create-react-app 命令, 创建 react 项目
- 自动将 create-react-app 从全局中删除掉
从V18降级到V17的版本
最新的脚手架默认使用的是最新的 React18 的版本, 而这个版本是最近才出稳定版, 企业项目还未开始使用
如何降级到V17的最新版呢?
-
重新下载 react 和 react-dom, 并指定17的版本
npm i react@17 react-dom@17
-
修改入口JS的实现
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))
安装chrome调试工具
❓ 问题: 一旦开始进行组件化的应用开发, 我们需要查看应用中组件组成和各个组件的相关数据(props/state)
💡 解决: 使用React的chrome调试工具,
React Developer Tools
- 方式一: chrome应用商品搜索
React
, 下载安装React Developer Tools
- 问题: 需要使用翻墙工具
- 方式二: 使用本地的安装包
- 进入扩展程序列表
- 打开 开发者模式
- 将安装包的文件夹拖入扩展程序列表界面, 直接安装
- 测试
- 访问react项目, 插件图标会亮
- 多了调试选项: Components
创建组件的两种方式
函数组件
function App() {
// return null
return <div>App</div>
}
// 函数名就是组件名
ReactDom.render(<App />, document.getElementById('root'))
1️⃣ 组件名首字母必须大写。因为react以此来区分组件元素/标签 和 一般元素/标签
2️⃣ 组件内部如果有多个标签,必须使用一个根标签包裹。只能有一个根标签
3️⃣ 必须有返回值。返回的内容就是组件呈现的结构, 如果返回值为 null,表示不渲染任何内容
4️⃣ 会在组件标签渲染时调用, 但不会产生实例对象(this->undefined), 不能有状态
📝 注意: 后面我们会讲如何在函数组件中定义状态 ==> hooks语法
类组件
import React from "react"
class App extends React.Component {
render () {
return <div>App Component</div>
}
}
ReactDom.render(<App />, document.getElementById('root'))
1️⃣ 组件名首字母必须大写。
2️⃣ 组件内部如果有多个标签,必须使用一个根标签包裹。只能有一个根标签
3️⃣ 类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性
4️⃣ 类组件中必须要声明一个render函数, reander返回组件代表组件界面的虚拟DOM元素
5️⃣ 会在组件标签渲染时调用, 产生实例对象(this->组件实例对象), 可以有状态
类组件的状 态 state
函数组件又叫做无状态组件(不产生实例),类组件又叫做有状态组件(有实例)
状态(state)即数据
函数组件没有state, 只能根据外部传入的数据(props)动态渲染
类组件有自己的state数据,一旦更新state数据, 界面就会自动更新
state的基本使用
- 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
- 组件对象的state属性
- 属性值为对象, 可以在state对象中保存多个数据
- 初始化state
- 构造器中: this.state = {xxx: 2}
- 类体中: state = {}
- 读取state数据
- this.state.xxx
- 更新state数据
- 不能直接更新state数据
- 必须 this.setState({ 要修改的属性数据 })