目录
补充:(input标签的value属性详解、e.target.value)
补充:(Promise,async,await,fetch详解)
1.使用creat-react-app快速搭建开发环境
清理一些不必要的内容
2.JSX基础-概念和本质
2.1什么是JSX
2.2JSX的本质
2.3JSX中使用JS表达式
2.4JSX中实现列表渲染
补充:(Array.map的使用&key的作用)
注意事项:加上一个独一无二的key,字符串或者munber、id
key的作用:React框架内部使用,提升更新性能的
Array.map方法的使用:
2.5JSX中实现条件渲染
补充:(逻辑运算符)
2.6JSX中实现复杂条件渲染
3.React中的事件绑定
补充:(箭头函数)
目标:能够熟悉箭头函数不同写法
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
基本语法:
<script>
const fn = function () {
console.log(123)
}
//箭头函数
const fn = () => {
console.log(123)
}
fn()
//1.传参
const fn = (x) => {
console.log(x)
}
fn(1)
//2.只有一个形参的时候可以省略
const fn = x => {
console.log(x)
}
fn(1)
//3.只有一行代码的时候可以省略大括号
const fn = x => console.log(x)
fn(1)
//4.如果在一行里面可以省略return
// const fn = x => {
// return x + x
// }
const fn = x => x + x
console.log(fn(1)) //2
//5.箭头函数可以直接返回一个对象
//因为箭头函数的大括号与对象的大括号冲突了,所以使用小括号
//name是对象的属性名,uname是传入的参数
cosnt fn = (uname) => ({ name: uname })
fn('刘德华')
</script>
箭头函数参数:
1.普通函数有arguments动态参数
2.箭头函数没有arguments动态参数,但是有剩余参数..args
<script>
//利用箭头函数求和
const getSum = (...arr) => {
let sum = 0
for (i = 0; i < arr.length; i++)
sum += arr[i]
}
const result = getSum(2,3,4)
console.log(result)
</script>
箭头函数this:
- 在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌。
- 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
- 在开发中【使用箭头函数前需要考虑函数中this的值】,事件回调函数使用箭头函数时,this为全局的window,因此DOM事件回调函数为了简便,还是不太推荐使用箭头函数
<script>
// 箭头函数的this 是上一层作用域的this指向
const fn = () => {
console.log(this)//windows
}
fn()
//对象方法箭头函数this
const obj = {
uname: 'pink',
sayHi: () => {
console.log(this)//windows
}
}
obj.sayHi()
const obj = {
uname: 'pink',
sayHi: function () {
console.log(this) //obj
const count = () => {
console.log(this) //obj
}
count()
}
}
obj.sayHi()
</script>
4.React中的组件
4.1组件基础样式控制
行内样式控制:
class类名控制:
5.useState基础使用
用useState实现点击计数:
5.1useState修改状态的规则
补充:(展开运算符)
6.B站评论列表:
6.1实现评论删除
补充:(filter的使用方法)
1.使用splice()方法删除元素
JavaScript 中的 splice() 方法可用于在数组中添加或删除元素。如果我们需要删除数组中的元素,可以使用 splice() 方法。该方法接受两个参数,第一个参数指定要删除的元素的位置,第二个参数指定要删除的元素个数:
let myArray = ["apple", "banana", "orange", "grape"];
myArray.splice(1, 1);
console.log(myArray); // ["apple", "orange", "grape"]
2.使用filter()方法删除元素
除了使用 splice() 方法,我们还可以使用 filter() 方法来删除数组中的元素。该方法可用于创建一个新的数组,其中包含符合特定条件的元素。我们可以使用以下代码删除数组中的所有 “banana” 元素:
let myArray = ["apple", "banana", "orange", "grape"];
myArray = myArray.filter(function(item) {
return item !== "banana"
});
console.log(myArray); // ["apple", "orange", "grape"]
“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr2 = arr.filter(function(x, index) {
return index % 3 === 0 || x >= 8;
});
console.log(arr2); //[1, 4, 7, 8, 9, 10]
3.使用pop()和shift()方法删除元素
pop() 和 shift() 方法可用于删除数组的最后一个元素和第一个元素。如果我们想删除数组中的特定元素,可以使用这些方法与 indexOf() 方法结合使用。例如,以下代码可以删除数组中的第二个元素:
let myArray = ["apple", "banana", "orange", "grape"];
let index = myArray.indexOf("banana");
if (index !== -1) {
myArray.splice(index, 1);
}
console.log(myArray); // ["apple", "orange", "grape"]
4.使用slice()方法删除元素
slice() 方法是一个纯函数,它不会改变原始数组,而是返回一个新的数组,该数组包含从开始到结束(不包含结束)的元素。我们可以使用以下代码删除数组中的第二个元素:
let myArray = ["apple", "banana", "orange", "grape"];
let newArray = myArray.slice(0, 1).concat(myArray.slice(2));
console.log(newArray); //["apple", "orange", "grape"]
5.使用ES6的filter()方法删除元素
ES6 中的 filter() 方法也可以用于删除数组中的元素。我们可以使用以下代码删除数组中的所有 “banana” 元素:
let myArray = ["apple", "banana", "orange", "grape"];
myArray = myArray.filter(item => item !== "banana");
console.log(myArray); // ["apple", "orange", "grape"]
6.2渲染Tab+点击高亮实现
6.3排序功能实现
补充:(lodash工具)
lodash是一套工具库,它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数,官网入口
安装命令: npm i lodash
_.orderBy(collection, [iteratees=[_.identity]], [orders])
此方法类似于_.sortBy,除了它允许指定 iteratee(迭代函数)结果如何排序。 如果没指定 orders
(排序),所有值以升序排序。 否则,指定为"desc" 降序,或者指定为 "asc" 升序,排序对应值。
添加版本
4.0.0
参数
collection
(Array|Object): 用来迭代的集合。[iteratees=[_.identity]]
(Array[]|Function[]|Object[]|string[]): 排序的迭代函数。[orders]
(string[]):iteratees
迭代函数的排序顺序。
返回
(Array): 排序排序后的新数组。
例子
var users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 34 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 36 }
];
// 以 `user` 升序排序 再 `age` 以降序排序。
_.orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
6.4classnames优化类名控制
安装方式:
7.受控表单绑定
![](https://i-blog.csdnimg.cn/blog_migrate/5ab5937d5eaef14a11e72a9c1d2d9267.png)
补充:(input标签的value属性详解、e.target.value)
input标签的value属性详解
input 标签的 value 属性的作用是由 input 标签的 type 属性的值决定的
当 type 的取值为 button、reset、submit 中的其中一个时,此时 value 属性的值表示的是按钮上显示的文本
当 type 的取值为 text、password、hidden 中的其中一个时,此时 value 属性的值表示的是输入框中显示的初始值,此初始值可以更改,并且在提交表单时,value 属性的值会发送给服务器(既是初始值,也是提交给服务器的值)
当 type 的取值为 checkbox、radio 中的其中一个时,此时 value 属性的值表示的是提交给服务器的值
当 type 的取值为 image 时,点击它提交表单后,会将用户的点击位置相对于图像左上角的 x 坐标和 y 坐标提交给服务器
type="checkbox" 时,其 value 属性的值表示在复选框呈勾选状态时提交给服务器的数据值,默认为 on
type="image" 定义图像形式的提交按钮,此时必须把 src 属性 和 alt 属性 与 <input type="image"> 结合使用(alt 属性表示图片未正常显示时,用于替换图片的提示;如果不写这个属性,当图片未正常显示时,会默认显示提交这两个字)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>input标签的value属性</title>
</head>
<body>
<form action="#">
<fieldset>
<legend>value的值是按钮上的文本</legend>
<input type="button" value="按钮"> <br>
<input type="reset" value="重置"> <br>
<input type="submit" value="提交"> <br>
</fieldset>
<br><br>
<fieldset>
<legend>value的值是输入框中的初始值</legend>
<input type="text" value="我的type属性值是text"> <br>
<input type="password" value="我的type属性值是password"> <br>
<!--
定义隐藏字段,隐藏字段对于用户是不可见的
隐藏字段通常会存储一个默认值,它们的值也可以由 JavaScript 进行修改
-->
<input type="hidden" value="我的type属性值是hidden"> <br>
</fieldset>
<br><br>
<fieldset>
<legend>value的值在提交表单时会发送给服务器</legend>
<input type="checkbox" value="v1"> <br>
<input type="radio" value="v2"> <br>
<!--image型input标签生成的按钮显示为一幅图像,点击它可以提交表单-->
<input type="image" src="xxx.png" alt="Submit"> <br>
<input type="image" src="xxx.png"> <br>
</fieldset>
</form>
</body>
</html>
【注】:
checkbox 型的 input 标签的不足之处在于:提交表单时,只有处于勾选状态的复选框的数据值才会发送给服务器。也就是说,如果没有任何一个复选框被选中,那么服务器就不会收到与其相关的数据项
当设置 input 标签的 type 属性值为checkbox 或者 radio 时,必须同时设置 input 标签的 value 属性
当 type="file" 时,不能使用 value 属性
e.target.value
8.React中获取DOM元素 (useRef钩子函数)
9.B站评论案例-核心功能实现
9.1 id处理和时间处理
安装uuid组件
引入Day.js组件
补充:(uuid组件安装、Day.js组件安装)
uuid组件安装
Day.js组件安装
9.2清空内容并重新聚焦
10.组件通信
10.1父传子-基础实现
10.2父子组件通信-子传父
如何让子传父的数据显示到页面中
10.3兄弟组件通信
10.4使用Context机制跨层级组件通信
11.useEffect的概念理解
接口地址:http://geek.itheima.net/v1_0/channels
补充:(Promise,async,await,fetch详解)
Promise,async,await,fetch详解_fetch await promise-CSDN博客
11.1useEffect依赖项参数说明
1.没有依赖项
2.空数组依赖
3. 传入特定依赖
11.2useEffect-清除副作用
12 .自定义Hook实现
补充:(解构)
解构赋值
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
基本语法:
1.赋值运算符=左侧的 [ ] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
2.变量的顺序对应数组单元值的位置依次进行赋值操作
<script>
const arr = [100, 60, 80]
//数组解构 赋值
//const [max,min,avg]=arr
const [max, min, avg] = arr
//const max = arr[0]
//const min = arr[1]
//const avg = arr[2]
</script>
独立完成数组解构赋值
需求①:有个数组:const pc=['海尔','联想','小米','方正]
结构为变量:hr lx mi fz
需求②:请将最大值和最小值函数返回值解构max和min两个变量
变量多 单元值少 的情况:
<script>
const [a, b, c, d] = ['苹果', '小米', '华为']
console.log(a)//苹果
console.log(b)//小米
console.log(c)//华为
console.log(d)//undefined
</script>
变量少 单元值多 的情况:
<script>
const [a, b] = ['苹果', '小米', '华为']
console.log(a)//苹果
console.log(b)//小米
</script>
剩余参数 变量少 单元值多 的情况:
<script>
const [a, b,...c] = ['1', '2', '3','4']
console.log(a)//1
console.log(b)//2
console.log(c)//[3,4]
</script>
防止有undefined传递单元值的情况,可以设置默认值:
<script>
const [a='1',b='2'] = ['3']
console.log(a)//3
console.log(b)//2
</script>
按需导入赋值:
<script>
const [a,b, ,d] = ['1','2','3','4']
console.log(a)//1
console.log(b)//2
console.log(d)//4
</script>
支持多维数组的解构:
<script>
const arr = [1, 2, [3, 4]]
const [a, b, c] = [1, 2, [3, 4]]
console.log(a)//1
console.log(b)//2
console.log(c)//[3,4]
//多维数组解构
const [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a)//1
console.log(b)//2
console.log(c)//3
console.log(d)//4
</script>
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
1.基本语法:
1.赋值运算符=左侧的{ } 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
2.对象属性的值将被赋值给与属性名相同的变量
3.注意解构的变量名不要和外面的变量名冲突否则报错
4.对象中找不到与变量名一致的属性时变量值为undefined
<script>
// const obj = {
// uname: 'pink老师',
// age: 18
// }
//解构的语法
const{uname,age}={uname:'pink老师',age:20}
//等价于 const uname = obj.uname
//要求属性名和变量名必须一致才可以
console.log(uname)//pink老师
console.log(age)//20
</script>
给新的变量名赋值:
可以从一个对象中提取变量并同时修改新的变量名
冒号表示 “什么值:赋值给谁”
//给新的变量名赋值
const{uname:username,age}={uname:'pink老师',age:20}
console.log(username)//pink老师
console.log(age)//20
数组对象解构:
<script>
//数组对象解构
const pig = [
{
uname: 'red老师',
age: 20
}
]
const [{ uname, age }] = pig
console.log(uname) //red老师
console.log(age)//20
</script>
多级对象解构:
<script>
const pig = {
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
sister: '乔治'
},
age: 6
}
//多级对象解构
const {name,family:{mother,father,sister}}=pig
console.log(name)//佩奇
console.log(mother)//猪妈妈
console.log(father)//猪爸爸
console.log(sister)//乔治
</script>
多级数组对象解构:
<script>
const pig = [
{
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
sister: '乔治'
},
age: 6
}
]
//多级数组对象解构
const [{ name, family: { mother, father, sister } }] = pig
console.log(name)//佩奇
console.log(mother)//猪妈妈
console.log(father)//猪爸爸
console.log(sister)//乔治
</script>
多级对象解构案例:
<script>
// 1. 这是后台传递过来的数据
const msg = {
"code": 200,
"msg": "获取新闻列表成功",
"data": [
{
"id": 1,
"title": "5G商用自己,三大运用商收入下降",
"count": 58
},
{
"id": 2,
"title": "国际媒体头条速览",
"count": 56
},
{
"id": 3,
"title": "乌克兰和俄罗斯持续冲突",
"count": 1669
},
]
}
// 需求1: 请将以上msg对象 采用对象解构的方式 只选出 data 方面后面使用渲染页面
const { data } = msg
console.log(data)
// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
// const { data } = msg
// msg 虽然很多属性,但是我们利用解构只要 data值
function render({ data }) {
// const { data } = arr
// 我们只要 data 数据
// 内部处理
console.log(data)
}
render(msg)
// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({ data: myData }) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理
console.log(myData)
}
render(msg)
</script>
12.1ReactHook使用规则
13.优化案例-使用useEffect获取数据
13.1自定义Hook函数封装数据请求
13.2封装评论项item组件
import { useEffect, useRef, useState } from 'react'
import './App.scss'
import avatar from './images/bozai.png'
import _ from 'lodash'
import classNames from 'classnames'
import { v4 as uuidV4 } from 'uuid'
import dayjs from 'dayjs'
import axios from 'axios'
// 当前登录用户信息
const user = {
// 用户id
uid: '30009257',
// 用户头像
avatar,
// 用户昵称
uname: '黑马前端',
}
// 导航 Tab 数组
const tabs = [
{ type: 'hot', text: '最热' },
{ type: 'time', text: '最新' },
]
// 封装请求数据的Hook
function useGetList () {
// 获取接口数据渲染
const [commentList, setCommentList] = useState([])
useEffect(() => {
// 请求数据
async function getList () {
// axios请求数据
const res = await axios.get(' http://localhost:3004/list')
setCommentList(res.data)
}
getList()
}, [])
return {
commentList,
setCommentList
}
}
// 封装Item组件
// Item ({ item, onDel }) 中的item是父传子传过来的
// 接收父组件中传过来的onDel
function Item ({ item, onDel }) {
return (
<div className="reply-item">
{/* 头像 */}
<div className="root-reply-avatar">
<div className="bili-avatar">
<img
className="bili-avatar-img"
alt=""
src={item.user.avatar}
/>
</div>
</div>
<div className="content-wrap">
{/* 用户名 */}
<div className="user-info">
<div className="user-name">{item.user.uname}</div>
</div>
{/* 评论内容 */}
<div className="root-reply">
<span className="reply-content">{item.content}</span>
<div className="reply-info">
{/* 评论时间 */}
<span className="reply-time">{item.ctime}</span>
{/* 评论数量 */}
<span className="reply-time">点赞数:{item.like}</span>
{/* 条件:user.id === item.user.id */}
{user.uid === item.user.uid &&
// 在子组件中触发父组件中的删除函数并且需要传递handleDel(id)中的id ——>子传父
<span className="delete-btn" onClick={() => onDel(item.rpid)}>
删除
</span>}
</div>
</div>
</div>
</div>
)
}
const App = () => {
// 渲染评论列表
// 1. 使用useState维护list
// const [commentList, setCommentList] = useState(_.orderBy(list, 'like', 'desc'))
const { commentList, setCommentList } = useGetList()
// 删除功能
// 功能位于父组件中,但是删除按钮位于子组件中
const handleDel = (id) => {
console.log(id)
// 对commentList做过滤处理
setCommentList(commentList.filter(item => item.rpid !== id))
}
// tab切换功能
// 1. 点击谁就把谁的type记录下来
// 2. 通过记录的type和每一项遍历时的type做匹配 控制激活类名的显示
const [type, setType] = useState('hot')
const handleTabChange = (type) => {
console.log(type)
setType(type)
// 基于列表的排序
if (type === 'hot') {
// 根据点赞数量排序
// lodash
setCommentList(_.orderBy(commentList, 'like', 'desc'))
} else {
// 根据创建时间排序
setCommentList(_.orderBy(commentList, 'ctime', 'desc'))
}
}
// 发表评论
const [content, setContent] = useState('')
const inputRef = useRef(null)
const handlPublish = () => {
setCommentList([
...commentList,
{
rpid: uuidV4(), // 随机id
user: {
uid: '30009257',
avatar,
uname: '黑马前端',
},
content: content,
ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分
like: 66,
}
])
// 1. 清空输入框的内容
setContent('')
// 2. 重新聚焦 dom(useRef) - focus
inputRef.current.focus()
}
return (
<div className="app">
{/* 导航 Tab */}
<div className="reply-navigation">
<ul className="nav-bar">
<li className="nav-title">
<span className="nav-title-text">评论</span>
{/* 评论数量 */}
<span className="total-reply">{10}</span>
</li>
<li className="nav-sort">
{/* 高亮类名: active */}
{tabs.map(item =>
<span
key={item.type}
onClick={() => handleTabChange(item.type)}
className={classNames('nav-item', { active: type === item.type })}>
{item.text}
</span>)}
</li>
</ul>
</div>
<div className="reply-wrap">
{/* 发表评论 */}
<div className="box-normal">
{/* 当前用户头像 */}
<div className="reply-box-avatar">
<div className="bili-avatar">
<img className="bili-avatar-img" src={avatar} alt="用户头像" />
</div>
</div>
<div className="reply-box-wrap">
{/* 评论框 */}
<textarea
className="reply-box-textarea"
placeholder="发一条友善的评论"
ref={inputRef}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
{/* 发布按钮 */}
<div className="reply-box-send">
<div className="send-text" onClick={handlPublish}>发布</div>
</div>
</div>
</div>
{/* 评论列表 */}
<div className="reply-list">
{/* 评论项 */}
{/* 将handleDel函数传递给子组件 */}
{commentList.map(item => <Item key={item.id} item={item} onDel={handleDel} />)}
</div>
</div>
</div>
)
}
export default App