参考视频:https://www.bilibili.com/video/BV1ZB4y1Z7o8/?p=3&spm_id_from=pageDriver&vd_source=5c584bd3b474d579d0bbbffdf0437c70
如果没有安装create-react-app需要先全局安装
命令:npm i -g create-react-app
1.快速搭建开发环境
create-react-app是一个快速 创建react开发环境的工具,底层由webpack构建,封装了配置细节,开箱即用
执行命令:
npx create-react-app react-basic
- npx Node.js工具命令,查找并执行后续的包命令
- create-react-app 核心包(固定写法),用于创建react项目
- react-basic React项目的名称(可以自定义)
- 创建React项目的更多方式:https://zh-hans.react.dev/learn/start-a-new-react-project
2.项目文件
src/index.js ==> 入口文件
src/App.js ==>导入项目的根组件
3.JSX基础:概念和本质
- 概念:
JSX是Javascript和XML(HTML)的缩写,表示在JS代码中编写HTML模板结构,它是React中编写UI模板的方式
优势:
- HTML的声明式模板写法
- JS的可编程能力
- 本质:
JSX并不是标准的JS语法,它是JS的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行。 (babel解析工具)
babel网址:babeljs.io , 可以在左边输入js代码看右边编译在浏览器运行的代码,需要把react勾上才能解析JSX
- 高频场景:
- JSX中使用JS表达式:在JSX中可以使用大括号语法{}识别Javascript中的表达式,比如常见的变量、函数调用、方法调用等等。
1.使用引号传递字符串 2.使用Javascript变量 3.函数调用和方法调用 4.使用Javascript对象
function getName() {
return 'jack'
}
function Contuer() {
const count = 100;
return(
<div>
{/* 1.使用引号传递字符串, 字符串识别 */}
{ 'this is message' }
{/* 2.使用Javascript变量, 识别js变量 */}
{ count }
{/* 3.函数调用和方法调用 */}
{ getName() } {/* 调用函数显示的是函数的返回值 */}
{ new Date().getDate() } {/* 方法调用 */}
{/* 4.使用Javascript对象, {}外层是识别对象的 */}
<div style={{color: 'red'}}>this is div</div>
</div>
)
}
注意:if语句、switch语句、变量声明属于语句,不是表达式,不能出现在{}中
4.JSX基础-实现列表渲染
map循环哪个结构直接return结构,通过map渲染需要绑定独一无二的key 字符串或者number,key的作用:React框架内部使用 提升更新性能的
5.JSX基础-实现条件渲染
语法:在React中,可以通过逻辑与运算符&&、三元表达式(?:)实现基础的条件渲染
{flag && <span>this is span</span>} // 判断一种情况,如果flag为true,span就显示,否则span不显示
{loading ? <span>loading...</span> : <span>加载完成</span>} // 两种情况(多个),loading为true就显示loading...,否则显示加载完成
6.JSX基础-复杂条件渲染
需求:列表中需要根据文章状态适配三种情况,单图、三图和无图三种模式
解决方案:自定义函数 + if 判断语句
const articleType = 3 // 0 1 3三种模式
function getArticleTem() {
// 可以用switch做
if (articleType === 0) {
return <div>无图</div>
} else if (articleType === 1) {
return <div>一图</div>
} else {
return <div>三图</div>
}
}
function Couter() {
return(
<div>
{ getArticleTem() } {/* 调用函数渲染不同的模板 */}
</div>
)
}
8.React中的时间绑定
语法: on + 事件名称 = { 事件处理程序 }, 整体上遵循驼峰命名法
- 使用事件对象参数
- 传递自定义参数
- 同时传递事件对象和自定义参数
9.React组件基础使用
组件是什么?
概念:一个组件就是用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以复用多次
组件化开发可以让开发者像搭积木一样构建一个完整的庞大的应用
在React中,一个组件就是首字母大写的函数(规定好的),内部存放了组建的逻辑和视图UI,渲染组件只需要把组件当成标签书写即可
function Button(){
return <button>click me</button>
}
不一定非要function Button(){}, const Button = () => {}也是可以的,只要是首字母大写的函数就是组件
const Button = () => {
return <button>click me</button>
}
10.useState基础使用
useState是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果
本质:和普通JS变量不同的是,状态变量一旦发生变化组建的视图UI也会跟着变化(数据驱动视图)
- useState修改状态的规则
- 修改对象状态
11.React组件基础样式控制
- 组件基础样式方案
使用style={{fontSize: '20px'}时,如果样式有带-的要改为驼峰写法}。使用class时<span className="class-name">span...</span>class要改为className
12.评论案例
12.1 列表渲染
思路:
- 使用useState维护评论列表
- 使用map方法对列表数据进行遍历渲染(别忘了加key)
-------------------------------------思考:为什么要用useState来维护评论列表?-----------------------------------
因为函数组件没有生命周期,执行完毕就结束了,即使值改变了,视图也不会更新。而用useState保存的数据改变之后会更新视图如图所示:
当点击事件发生,console.log(‘执行了’)也只会打印刚加载的一次,数据已经加上去了,但是视图还是两项没有更新
--------------------------------------------------思考结束:分割线--------------------------------------------
// 项目的根组件,组件从这往下分散
// App -> 引入到index.js -> 被里面的代码渲染到 public/index.html(root)
import { useState } from 'react';
const list = [
{
name: 'jack',
content: '这是评论回复111',
time: '2023-6-10',
likeNum: 100,
uid: '1'
},
{
name: 'rose',
content: '这是评论回复222',
time: '2023-6-11',
likeNum: 230,
uid: '2'
}
]
const user = {
uid: '3002017',
avatar: '',
uname: '张三'
}
// 渲染评论列表
// 1.使用useState维护list
function App() {
const [commentList, setCommentList] = useState(list);
return (
<div className="App">
<span>评论{commentList.length}</span> <button>最新</button>|<button>最热</button>
<div>
{user.uname}
<input />
<button>发布</button>
</div>
<div>
{commentList.map(item => {
return (
<div key={item.uid}>
<div>{item.name}</div>
<div>{item.content}</div>
<div>
<span>{item.time}</span>
<span>点赞数:{item.likeNum}</span>
<button>删除</button>
</div>
</div>
)
})}
</div>
</div>
);
}
export default App;
- 实现评论删除
做两件事情:删除按钮判断当前项里的uid是否等于用户uid,如果相等则显示删除按钮,如果不等不显示删除按钮
给删除按钮绑定点击事件,当点击删除按钮时传入当前项的uid,在事件中用数组过滤掉当前项
const handleDelete = (uid) => {
setCommentList(commentList.fliter(item => item.uid !== uid));
}
12.2 渲染tab + 点击高亮实现
map渲染tab数组之后,绑定一个点击事件,传当前项的type,判断当前项的type是否等于一个记录的值,如果相等就让字体颜色高亮
const [tabType, setTabType] = useState(0);
const handleTab = (type) => {
setTabType(type);
}
return内:
{tab.map(item => <span key={item.type} style={{color: tabType === item.type? 'blue' : '#aaa'}} onClick="() => handleTab(type)">{item.value}</span>)}
12.3 排序功能实现
- 排序可以使用lodash,会生成一份全新的数据,不会更改老数据
lodash官网:lodash.com / lodash.com/docs/
如果需要进来就按第一个高亮的排序,就在初始加载数据那排序
- 用array.sort()给数组排序
// 项目的根组件,组件从这往下分散
// App -> 引入到index.js -> 被里面的代码渲染到 public/index.html(root)
import { useState } from 'react';
const list = [
{
name: 'jack',
content: '这是评论回复111',
time: '2023-6-10',
likeNum: 100,
uid: '1'
},
{
name: 'rose',
content: '这是评论回复222',
time: '2023-6-11',
likeNum: 230,
uid: '2'
},
{
name: '张三',
content: '这是评论回复333',
time: '2023-6-9',
likeNum: 320,
uid: '3'
},
]
const tab = [
{
type: 'time',
value: '最新'
},
{
type: 'likeNum',
value: '最热'
}
]
const user = {
uid: '3',
avatar: '',
uname: '张三'
}
// 渲染评论列表
// 1.使用useState维护list
function App() {
const [commentList, setCommentList] = useState(list);
const [tabType, setTabType] = useState();
const handleDelete = (uid) => {
// 拿到需要删除uid的评论
setCommentList(commentList.filter(item => item.uid !== uid ));
}
const hanldeTab = (type) => {
// 拿到点击的type值
setTabType(type);
// 基于列表的排序
if (type === 'time') {
// 根据时间排序
setCommentList(
commentList.sort((a,b) => {
return new Date(b.time).getTime() - new Date(a.time).getTime();
})
)
} else {
// 根据点赞数排序
setCommentList(
commentList.sort((a,b) => {
return b.likeNum - a.likeNum;
})
)
}
}
return (
<div className="App">
<span>评论{commentList.length}</span>
{tab.map(item =>
<span key={item.type} style={{padding: '9px',color: tabType === item.type ? 'blue' : '#aaa', cursor: 'pointer'}}
onClick={() => hanldeTab(item.type)}>{item.value}</span>)}
<div>
{user.uname}
<input />
<button>发布</button>
</div>
<div>
{commentList.map(item => {
return (
<div key={item.uid}>
<div>{item.name}</div>
<div>{item.content}</div>
<div>
<span>{item.time}</span>
<span>点赞数:{item.likeNum}</span>
{ item.uid === user.uid && <button onClick={() => handleDelete(item.uid)}>删除</button> }
</div>
</div>
)
})}
</div>
</div>
);
}
export default App;
12.4发表评论
1.获取评论内容
const [content, setContent] = useState();
<input type="text" value={content} onChange={(e) => setContent(e.target.value)}>
2.点击发布按钮发布评论
const handlePublish = () => {
setCommentList([
...commentList,
{
name: user.uname,
content: content,
time: new Date(),
likeNum: 0,
uid: user.uid
}
])
}
<button onClick={handlePublish}></button>
- 随机id可以使用uuid: git上搜索:uuidjs/uuid
- 日期时间格式化可以使用dayjs:dayjs官网:dayjs.gitee.io/zh-CN/
12.5清空内容并重新聚焦
import { useRef, useState } from 'react';
const [content, setContent] = useState(''); // 绑定input的value需要加初始值,否则会报错
const inputRef = useRef(null);
const handlePublish = () => {
// ...做发布的逻辑
// 1.清空输入框的内容
setContent('');
// 2.重新聚焦 dom(useRef) - focus()
inputRef.current.focus();
}
<input type="text" ref={inputRef} value={content} onChange={(e) => setContent(e.target.value)} />
<button onClick={handlePublish}>发布</button>
13.classnames工具优化类名控制
用classNames库之后使用方法:
1.命令安装classnames包
npm install classnames
2.在组件中引入
import classNames from 'classname';
3.在组件内使用, ‘’包裹的是静态的class,
{
// active是动态的class名,值是判断条件,是否需要加active这个class
active: type === item.type
}包裹的是动态的class
从这样的:nav-item为静态class类名,active为动态class类名
<span className={`nav-item ${type === item.type && 'active'}`}>span...</span>
变成这样:
<span className = {classNames('nav-item', {
active: type === item.type
})}>span...</span>