React基础语法
1.React介绍
React由Meta公司开发,是一个用于构建Web和原生交互界面的库
1.1 React优势
相较于传统基于DOM开发的优势
1.组件化的开发方式
2.不错的性能
相较于其他前端框架的优势
1.丰富的生态
2.跨平台支持
1.2React的时长情况
全球最流行,大厂必备
2.开发环境创建
create-react-app是一个快速创建React开发环境的工具,底层由Webpack构件,封装了配置细节,开箱即用执行命令:
npx create-react-app react-basic
cd react-basic
npm start
创建项目更多方式:
https://zh-hans.react.dev/learn/start-a-new-react-project
3.JSX基础
3.1什么是JSX
JSX是JavaScript和XML(HTML)的缩写,表示在JS代码中编写HTML模板结构,是React构建UI的方式
const message = 'this is message'function App() {return (<div className="App"><h1>My First React App</h1>{message}</div>);
}export default App;
3.2 JSX的本质
JSX并不是标准的JS语法,它是 JS的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中使用
3.3JSX高频场景-JS表达式
在JSX中可以通过
<font style="color:rgb(119, 119, 119);background-color:rgb(248, 248, 248);">大括号语法{}</font>
识别JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等
使用引号传递字符串、JS变量、函数调用和方法调用、对象
注意:不能使用条件语句
const message = 'this is message'function getAge(){return 18
}function App() {return (<div className="App"><h1>My First React App</h1>{message}{/*函数调用*/}{getAge()}</div>);
}export default App;
3.4JS高频场景-列表场景
const list = [{id:1001, name:'Vue'},{id:1002, name: 'React'},{id:1003, name: 'Angular'}
]function App(){return (<ul>{list.map(item => <li key={item.id}>{item.name}</li>)}</ul>);
}export default App;
3.5JS高频场景-条件渲染
在React中,可以通过逻辑与运算&&、三元表达式(?:)实现基础的条件渲染
const flag = true;
const loading = true;function App() {return (<>{flag && <span>this is span</span>}{loading ? <span>loading...</span>:<span>this is span</span>}</>)
}export default App;
3.6JSX高频场景-复杂条件渲染
需求:列表中需要根据文章的状态适配
const type = 0; // 0 1 3function getArticleJSX() {if (type === 0) {return <div>无图模式</div>} else if (type === 1) {return <div>单图模式</div>} else if(type === 3){return <div>多图模式</div>}
}function App() {return (<>{getArticleJSX()}</>)
}export default App;
4.React的事件绑定
4.1基础实现
React中的事件绑定,通过语法on + 事件名称 = {事件处理程序}。整体上遵循驼峰命名
function App() {const clickHandle = () =>{console.log('button按钮点击了');}return (<button onClick={clickHandle}>click me</button>)
}export default App;
4.2使用事件参数
在事件回调函数中设置形参e即可
function App() {const clickHandle = (e) =>{console.log('button按钮点击了', e);}return (<button onClick={clickHandle}>click me</button>)
}export default App;
4.3传递自定义参数
语法:事件绑定的位置改造成箭头函数的写法,再执行clickHandler实际处理业务函数的时候传递实参
function App() {const clickHandle = (name) =>{console.log('button按钮点击了', name);}return (<button onClick={()=>clickHandle('jack')}>click me</button>)
}export default App;
注意:不能直接写函数调用,这里事件绑定需要一个函数引用
4.4同时传递事件对象的自定义参数
语法:在事件绑定位置传递事件实参e的自定义参数,clickHandle中声明形参,注意顺序对应
function App() {const clickHandle = (name, e) =>{console.log('button按钮点击了', name, e);}return (<button onClick={(e)=>clickHandle('jack', e)}>click me</button>)
}export default App;
5.React组件基础使用
5.1 组件是什么
概念:一个组件就是一个用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以服用多次
5.2组件基础使用
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染增加只需要把组件当作标签书写即可
function Button() {return <button>click me</button>
}
function App() {return (<div><Button/><Button></Button></div>)
}export default App;
6.组件状态管理-useStatue
6.1基础使用
useState是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果和普通的JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着发生变化(数据驱动试图)
import React from "react";function App() {const [count, setCount] = React.useState(0)return (<div><button onClick={()=>setCount(count + 1)}>{count}</button></div>)
}export default App;
6.2状态的修改规则
在React中状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新
6.3修改对象状态
对于对象类型的状态变量,应该始终给set方法一个全新的对象来进行修改
import React from "react";function App() {const [form, setForm] = React.useState({name : 'jack'})const handleChageName=() => {setForm({...form,name : 'john'})}return (<div><button onClick={()=>handleChageName()}>{form.name}</button></div>)
}export default App;
7.组建的基础样式处理
React组件基础的样式控制有两种方式,行内样式和class类名控制
import React from "react";function App() {return (<div><div style={{color:'red'}}> this is div</div></div>)
}export default App;
index.css
.foo{color: red;
}
App.js
import './index.css';function App() {return (<div><div className="foo"> this is div</div></div>)
}export default App;
8.B站评论案例
- 渲染评论列表
- 删除评论实现
- 渲染导航Tab和高亮实现
- 评论列表排序功能实现
8.1基础项目
App.js
import './App.scss'
import avatar from './images/bozai.png'/*** 评论列表的渲染和操作** 1. 根据状态渲染评论列表* 2. 删除评论*/// 评论列表数据
const defaultList = [{// 评论idrpid: 3,// 用户信息user: {uid: '13258165',avatar: '',uname: '周杰伦',},// 评论内容content: '哎哟,不错哦',// 评论时间ctime: '10-18 08:15',like: 88,},{rpid: 2,user: {uid: '36080105',avatar: '',uname: '许嵩',},content: '我寻你千百度 日出到迟暮',ctime: '11-13 11:29',like: 88,},{rpid: 1,user: {uid: '30009257',avatar,uname: '黑马前端',},content: '学前端就来黑马',ctime: '10-19 09:00',like: 66,},
]
// 当前登录用户信息
const user = {// 用户iduid: '30009257',// 用户头像avatar,// 用户昵称uname: '黑马前端',
}/*** 导航 Tab 的渲染和操作** 1. 渲染导航 Tab 和高亮* 2. 评论列表排序* 最热 => 喜欢数量降序* 最新 => 创建时间降序*/// 导航 Tab 数组
const tabs = [{ type: 'hot', text: '最热' },{ type: 'time', text: '最新' },
]const App = () => {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 */}<span className='nav-item'>最新</span><span className='nav-item'>最热</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">{/* 评论框 */}<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"/>{/* 发布按钮 */}<div className="reply-box-send"><div className="send-text">发布</div></div></div></div>{/* 评论列表 */}<div className="reply-list">{/* 评论项 */}<div className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"alt=""/></div></div><div className="content-wrap">{/* 用户名 */}<div className="user-info"><div className="user-name">jack</div></div>{/* 评论内容 */}<div className="root-reply"><span className="reply-content">这是一条评论回复</span><div className="reply-info">{/* 评论时间 */}<span className="reply-time">{'2023-11-11'}</span>{/* 评论数量 */}<span className="reply-time">点赞数:{100}</span><span className="delete-btn">删除</span></div></div></div></div></div></div></div>)
}export default App
App.scss
.app {width: 80%;margin: 50px auto;
}.reply-navigation {margin-bottom: 22px;.nav-bar {display: flex;align-items: center;margin: 0;padding: 0;list-style: none;.nav-title {display: flex;align-items: center;width: 114px;font-size: 20px;.nav-title-text {color: #18191c;font-weight: 500;}.total-reply {margin: 0 36px 0 6px;color: #9499a0;font-weight: normal;font-size: 13px;}}.nav-sort {display: flex;align-items: center;color: #9499a0;font-size: 13px;.nav-item {cursor: pointer;&:hover {color: #00aeec;}&:last-child::after {display: none;}&::after {content: ' ';display: inline-block;height: 10px;width: 1px;margin: -1px 12px;background-color: #9499a0;}}.nav-item.active {color: #18191c;}}}
}.reply-wrap {position: relative;
}
.box-normal {display: flex;transition: 0.2s;.reply-box-avatar {display: flex;align-items: center;justify-content: center;width: 80px;height: 50px;}.reply-box-wrap {display: flex;position: relative;flex: 1;.reply-box-textarea {width: 100%;height: 50px;padding: 5px 10px;box-sizing: border-box;color: #181931;font-family: inherit;line-height: 38px;background-color: #f1f2f3;border: 1px solid #f1f2f3;border-radius: 6px;outline: none;resize: none;transition: 0.2s;&::placeholder {color: #9499a0;font-size: 12px;}&:focus {height: 60px;background-color: #fff;border-color: #c9ccd0;}}}.reply-box-send {position: relative;display: flex;flex-basis: 86px;align-items: center;justify-content: center;margin-left: 10px;border-radius: 4px;cursor: pointer;transition: 0.2s;& .send-text {position: absolute;z-index: 1;color: #fff;font-size: 16px;}&::after {position: absolute;width: 100%;height: 100%;background-color: #00aeec;border-radius: 4px;opacity: 0.5;content: '';}&:hover::after {opacity: 1;}}
}
.bili-avatar {position: relative;display: block;width: 48px;height: 48px;margin: 0;padding: 0;border-radius: 50%;
}
.bili-avatar-img {position: absolute;top: 50%;left: 50%;display: block;width: 48px;height: 48px;object-fit: cover;border: none;border-radius: 50%;image-rendering: -webkit-optimize-contrast;transform: translate(-50%, -50%);
}// 评论列表
.reply-list {margin-top: 14px;
}
.reply-item {padding: 22px 0 0 80px;.root-reply-avatar {position: absolute;left: 0;display: flex;justify-content: center;width: 80px;cursor: pointer;}.content-wrap {position: relative;flex: 1;&::after {content: ' ';display: block;height: 1px;width: 100%;margin-top: 14px;background-color: #e3e5e7;}.user-info {display: flex;align-items: center;margin-bottom: 4px;.user-name {height: 30px;margin-right: 5px;color: #61666d;font-size: 13px;line-height: 30px;cursor: pointer;}}.root-reply {position: relative;padding: 2px 0;color: #181931;font-size: 15px;line-height: 24px;.reply-info {position: relative;display: flex;align-items: center;margin-top: 2px;color: #9499a0;font-size: 13px;.reply-time {width: 86px;margin-right: 20px;}.reply-like {display: flex;align-items: center;margin-right: 19px;.like-icon {width: 14px;height: 14px;margin-right: 5px;color: #9499a0;background-position: -153px -25px;&:hover {background-position: -218px -25px;}}.like-icon.liked {background-position: -154px -89px;}}.reply-dislike {display: flex;align-items: center;margin-right: 19px;.dislike-icon {width: 16px;height: 16px;background-position: -153px -153px;&:hover {background-position: -217px -153px;}}.dislike-icon.disliked {background-position: -154px -217px;}}.delete-btn {cursor: pointer;&:hover {color: #00aeec;}}}}}
}.reply-none {height: 64px;margin-bottom: 80px;color: #99a2aa;font-size: 13px;line-height: 64px;text-align: center;
}
8.2完成版本
8.2.1列表渲染
{/* 评论项 */}{list.map((item) => (<div key={item.user.uid} className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"alt=""/></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><span className="delete-btn" onClick={()=>deleteItem(item.rpid)}>
删除
</span></div></div></div></div>))
8.2.2 删除评论
const [list, setList] = useState(defaultList)const deleteItem = (rpid) => {setList(list.filter(item => item.rpid !== rpid));}
8.3.2渲染导航Tab和高亮显示
<li className="nav-sort">{/* 高亮类名: active */}{tabs.map((tab) => (<spankey={tab.type}className={`nav-item ${tab.type === activeTabType ? 'active' : ''}`}onClick={() => tabChange(tab.type)}>{tab.text}</span>))}{/*<span className='nav-item' onClick={()=>tabChange('time')}>最热</span>*/}</li>
const [activeTabType, setActiveTabType] = useState(tabs[0].type); // 初始激活第一个 tabconst tabChange = (type) => {setActiveTabType(type);};
8.3.4评论列表排序
安装lodash库
npm install lodash
const [list, setList] = useState(defaultList)
useEffect(() => {// 根据点赞数量排序(热度排序)const sortedComments = _.orderBy(list, 'like', 'desc')setList(sortedComments);
}, []); // 空依赖数组表示这个 effect 只在组件挂载和卸载时运行一次
const deleteItem = (rpid) => {setList(list.filter(item => item.rpid !== rpid));
}
const [activeTabType, setActiveTabType] = useState(tabs[0].type); // 初始激活第一个 tabconst tabChange = (type) => {setActiveTabType(type);if (type === 'hot') {setList(_.orderBy(list, 'like', 'desc'));} else if (type === 'time') {setList(_.orderBy(list, 'ctime', 'asc'));}
};
8.3.5 样式书写优化
npm install classnames
className={classNames('nav-item', {active: tab.type === activeTabType})}
9.React表单控制
9.1受控绑定
概念:使用React组件的状态(useState)控制表单状态
import {useState} from "react";function App() {const [value, setValue] = useState('');return (<input type="text"value={value}onChange={(e) => setValue(e.target.value)}></input>);
}export default App;
9.2非受控绑定
概念:通过DOM的方式获取表单的输入数据
import {useRef} from "react";function App() {var inputRef = useRef(null);const onChange = () => {console.log(inputRef.current.value)}return (<input type="text"ref={inputRef}onChange={onChange}></input>);
}export default App;
10.案例-B站评论案例
1.输入评论内容,并发布评论
1)进行表单受控绑定
<div className="reply-box-wrap">{/* 评论框 */}<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"value = {textValue}onChange={(e)=>setTextValue(e.target.value)}/>{/* 发布按钮 */}<div className="reply-box-send"><div className="send-text" onClick={()=>sendComment(textValue)}>发布</div></div>
</div>
2)点击“发布”触发发布评论
const [textValue, setTextValue] = useState('');
const sendComment = () => {setList([...list,{// 评论idrpid: 3,// 用户信息user: user,// 评论内容content: textValue,// 评论时间ctime: new Date().toLocaleString(),like: 88,}])setTextValue('');}
3)非受控绑定输入框
<div className="reply-box-wrap">{/* 评论框 */}<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"value = {textValue}ref={inputRef}onChange={(e)=>setTextValue(e.target.value)}/>{/* 发布按钮 */}<div className="reply-box-send"><div className="send-text" onClick={()=>sendComment(textValue)}>发布</div></div>
</div>
4)光标聚焦
const sendComment = () => {setList([...list,{// 评论idrpid: 3,// 用户信息user: user,// 评论内容content: textValue,// 评论时间ctime: new Date().toLocaleString(),like: 88,}])setTextValue('');inputRef.current.focus();}
2.id处理和时间处理(uuid和day.js)
npm install dayjs --save
npm install uuid --save
具体代码实现
const sendComment = () => {setList([...list,{// 评论idrpid: uuidV4(),// 用户信息user: user,// 评论内容content: textValue,// 评论时间ctime: dayjs(new Date()).format('MM-DD hh:mm'),like: 88,}])setTextValue('');inputRef.current.focus();}
11.React组件通信
概念:组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信手段和方法
- A-B 父子通信
- B-C 兄弟通信
- A-E 跨层通信
11.1 父子通信-父传子
11.1.1 基础实现
实现步骤:
1.父组件传递数据-在子组件标签上绑定属性
2.子组件接收数据-子组件通过props参数接受数据
function Son(props) {return (<div>{props.name}</div>)
}function App() {const name = 'react'return (<div><Son name={name} /></div>);
}export default App;
11.1.2props说明
props可以传递任意的合法数据,比如数字、字符串、布尔值、数组、对象、函数、JSX
props是只读对象
子组件只能读取props中的数据,不能直接进行修改,父组件的数据只能由父组件修改
11.1.3特殊的prop-chilren
场景:当我们把内容嵌套在组件的标签内部时,组件会自动在名为children的prop属性中接受该内容
function Son(props) {return <div>{props.children}</div>
}
function App() {const name = 'react'return (<Son><span>this is span</span></Son>);
}export default App;
11.2 父子通信-子传父
核心思想:子组件中调用父组件中的参数并传递函数
function Son({ onGetMsg}) {const msg = "this is son msg";return (<div><button onClick={() => onGetMsg(msg)}>send</button></div>)
}
function App() {const getMsg = (msg) => {console.log(msg)}return (<div><Son onGetMsg={getMsg}></Son></div>);
}export default App;
11.3兄弟组件通信
实现思路:
借助状态提升机制,通过共同的父组件进行相抵之间的数据传递
import {useState} from "react";function A({onGetAName}) {const mag = "this is A name";return (<div>this is A,<button onClick={() => onGetAName(mag)}>send</button></div>);
}function B({name}) {return <div>this is B,{name}</div>
}function App() {const [name, setName] = useState("");const getAName = (name) => {setName(name);}return (<div><A onGetAName={getAName}></A><B name={name}></B></div>);
}export default App;
11.4 跨层组件通信
实现步骤:
1.使用createContext方法创建一个上下文对象Ctx
2.在顶层组件{App}中通过Ctx.Provider组件提供数据
3.在底层组件{B}中通过useContext钩子函数获取消费消息
import {createContext, useContext} from "react";var MsgContext = createContext();function A() {const msg = "this is A Component";return (<div>{msg}.<B/></div>);
}function B() {const msg = useContext(MsgContext);return <div>this is B,{msg}</div>
}function App() {const msg = "this is App Component";return (<div><MsgContext.Provider value={msg}><A/></MsgContext.Provider></div>);
}export default App;
12.React副作用管理-useEffect
12.1概念理解
useEffect是一个React Hook函数,用于在React组件中创建不是由时间引起而是由渲染本身引起的操作(副作用),比如发送AJAX请求,更改DOM等等
说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要跟服务器要数据,整个过程属于“只渲染引起的操作”
12.2基础使用
需求:在组建渲染完毕之后,立刻从服务器端获取数据并展示在页面中
useEffect(() => { }, [])
说明:
- 参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
- 参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
接口地址:http://geek.itheima.net/v1_0/channels
1)安装axios
npm install axios
2)发送请求
const [list, setList] = useState([]);
useEffect(() => {const getData = async () => {const res = await axios.get("http://geek.itheima.net/v1_0/channels");console.log(res.data.data.channels)setList(res.data.data.channels)}getData();}, []);
3)展示数据
import axios from "axios";
import {useEffect, useState} from "react";function App() {const [list, setList] = useState([]);useEffect(() => {const getData = async () => {const res = await axios.get("http://geek.itheima.net/v1_0/channels");console.log(res.data.data.channels)setList(res.data.data.channels)}getData();}, []);return (<div><ul>{list.map(item => <li key={item.id}>{item.name}</li>)}</ul></div>);
}export default App;
12.3useEffect依赖说明
useEffect副作用函数执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现
依赖项 | 副作用功函数的执行时机 |
---|---|
没有依赖项 | 组件初始渲染 + 组件更新时执行 |
空数组依赖 | 只在初始渲染时执行一次 |
添加特定依赖项 | 组件初始渲染 + 依赖项变化时执行 |
12.4清除副作用
概念:在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用函数,比如在useEffect中开启一个定时器,在组件卸载时把这个定时器清除掉,这个过程就是清楚副作用。
useEffect(()=>{return ()=>{// 清除副作用}
},[])
说明:清除副作用函数最常见的执行时机是在组件卸载时自动执行
import {useEffect, useState} from "react";function Son() {useEffect(() => {const timer = setInterval(() => {console.log("我是子组件的定时器")}, 1000)return () => {clearInterval(timer)console.log("子组件的定时器被销毁了")}}, []);return (<div>我是子组件</div>)
}function App() {const [show, setShow] = useState(true)return (<div>{show && <Son/>}<button onClick={() => setShow(false)}>卸载组件</button></div>);
}export default App;
13.自定义Hook
13.1自定义Hook实现
概念:自定义Hook是以use大头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用
封装自定义hook通用思路:
1.声明一个以use打头的函数
2.在函数体内封装可复用的逻辑(只要是可复用的逻辑)
3.把组件中用到的状态或者回调return出去(以对象或者数组)
4.在哪个组件中要用这个逻辑,就执行这个函数,结构出来状态和回调进行使用
import {useState} from "react";function useToggle() {const [value, setValue] = useState(true);const toggle = () => setValue(!value);return {value, toggle};
}function App() {const {value, toggle} = useToggle();return (<div>{value && <div>this is div</div>}<button onClick={toggle}>toggle</button></div>);
}export default App;
13.2React Hooks使用规则
1.只能在组件或者其他自定义Hook函数中调用
2.只能在组件的顶层调用,不能嵌套在if、for、其它函数中
14.案例-优化B站评论案例
14.1使用接口的方式获取评论列表并渲染
1)使用json-server工具模拟接口服务,通过axios发送请求
下载json-server到当前项目
npm i json-server -D
在项目文件夹下创建db.json文件
{"list": [{"rpid": 3,"user": {"uid": "13258165","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "周杰伦"},"content": "哎哟,不错哦","ctime": "10-18 08: 15","like": 126},{"rpid": 2,"user": {"uid": "36080105","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "许嵩"},"content": "我寻你千百度 日出到迟暮","ctime": "11-13 11: 29","like": 88},{"rpid": 1,"user": {"uid": "30009257","avatar": "http://toutiao.itheima.net/resources/images/98.jpg","uname": "黑马前端"},"content": "学前端就来黑马","ctime": "10-19 09: 00","like": 66}]
}
在package.json下添加启动命令
"serve": "json-server db.json --port 3004"
2)使用useEffect调用接口获取数据
封装Hook调用接口函数
function useGetList() {const [list, setList] = useState([]);useEffect(() => {async function getList() {const res = await axios.get('http://localhost:3004/list');setList(res.data);}getList()}, []);return {list,setList}}
调用Hook函数
const {list, setList} = useGetList();
14.2把评论中的每一项抽象成一个独立的组件实现渲染
1)封装组件
function Item({item, onDel}) {return (<div key={item.rpid} className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"alt=""/></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><span className="delete-btn" onClick={() => onDel(item.rpid)}>删除</span></div></div></div></div>);
}
2)调用组件
<div className="reply-list">{/* 评论项 */}{list.map((item) => (<Item key={item.id} item={item} onDel={deleteItem}/>))}
</div>
相关文章:

React基础语法
1.React介绍 React由Meta公司开发,是一个用于构建Web和原生交互界面的库 1.1 React优势 相较于传统基于DOM开发的优势 1.组件化的开发方式 2.不错的性能 相较于其他前端框架的优势 1.丰富的生态 2.跨平台支持 1.2React的时长情况 全球最流行,大厂…...

《Kadane‘s Algorithm专题:最大和连续子数组》
🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀 🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷…...
Vue基础(5)
ref属性 在 Vue2 中,ref是一个特殊的属性,用于在模板中获取对某个 DOM 元素或子组件的引用。通过 ref,我们可以在 JavaScript 代码中直接访问该 DOM 元素或组件实例。 示例: <template><div><input ref"inputField&quo…...

面对复杂的软件需求:5大关键策略!
面对软件需求来源和场景的复杂性,有效地管理和处理需求资料是确保项目成功的关键,能够提高需求理解的准确性,增强团队协作和沟通,降低项目风险,提高开发效率。反之,项目可能面临需求理解不准确、团队沟通不…...

使用Git进行版本控制的最佳实践
文章目录 Git简介基本概念仓库(Repository)提交(Commit)分支(Branching) 常用命令初始化仓库添加文件提交修改查看状态克隆仓库分支操作合并分支推送更改 最佳实践使用有意义的提交信息定期推送至远程仓库使…...
【入门1】顺序结构 - B2025 输出字符菱形
题目描述 用 * 构造一个对角线长 55 个字符,倾斜放置的菱形。 输入格式 没有输入要求。 输出格式 如样例所示。用 * 构成的菱形。 输入输出样例 输入 #1 输出 #1**** ********* <C> : #include<stdio.h>int main() {printf(" *\n ***\n**…...
C#DLL热加载|动态替换
我有一个项目 开始取数据和结束数据部分是一样的,但中间处理数据是根据客户需求来转换的 又要求增加一个客户数据转换 主程序是不能停下来的 所以这个项目转数据转换部分做成插件式 每个客户的数据转换都是一个项目 都是一个DLL 主程序里面定义好接口类或者抽象…...
数据库三大范式
目录 第一范式(1NF) 第二范式(2NF) 第三范式(3NF) Oracle三大范式是数据库设计中的规范化过程,旨在减少数据冗余、提高数据一致性和数据库性能。这三大范式包括第一范式(1NF)、第二范式(2NF)和第三范式(3NF)。 第一范式(1NF) 数据库表的每一列都是不可分割…...

【linux】fdisk磁盘分区管理
介绍 fdisk是一个磁盘分区管理工具,可以用来创建、删除、修改和查看磁盘分区。 fdisk一般都是交互式使用,基础语法: fdisk /dev/sdd。进入交互窗口后,有一些选项,需要了解下: 选项含义n创建新分区p查看磁盘的分区情…...

asp.net core 入口 验证token,但有的接口要跳过验证
asp.net core 入口 验证token,但有的接口要跳过验证 在ASP.NET Core中,你可以使用中间件来验证token,并为特定的接口创建一个属性来标记是否跳过验证。以下是一个简化的例子: 创建一个自定义属性来标记是否跳过验证: public clas…...

[mysql]聚合函数GROUP BY和HAVING的使用和sql查询语句的底层执行逻辑
#GROUP BY的使用 还是先从需求出发,我们现在想求员工表里各个部门的平均工资,最高工资 SELECT department_id,AVG(salary) FROM employees GROUP BY department_id 我们就会知道它会把一样的id分组,没有部门的就会分为一组,我们也可以用其他字段来分组,我们想查询不同jb_id…...

从数据中台到数据飞轮:实现数据驱动的升级之路
从数据中台到数据飞轮:实现数据驱动的升级之路 随着数字化转型的推进,数据已经成为企业最重要的资产之一,企业普遍搭建了数据中台,用于整合、管理和共享数据;然而,近年来,数据中台的风潮逐渐减退…...
小记:SpringBoot中,@Alisa和@ApiModelProperty的区别
在 Spring Boot 中,Alias和ApiModelProperty 这两个注解用于不同的目的。 Alias Alias是一个用于定义别名的注解,通常用于 Bean 属性的别名功能,这样在使用某些框架(如 JPA 或 Jackson)时,可以将一个属性名…...

信捷 PLC C语言 定时器在FC中的使用
传统梯形图的定时器程序写起来简单,本文用C语言写定时器的使用。 定时器在c语言中使用,和普通梯形图中使用的区别之一是既有外部条件,也有内部条件。 1.建全局变量 2.建立FC POU 这个是功能POU程序。 这里的Enable是内部条件 3.调用包含定…...

k8s常用对象简介
Pod Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。 Pod 是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度&…...

【Kaggle | Pandas】练习2:索引,选择和分配
文章目录 数据总表1、读取列2、读取某列的第几行的值3、第一行数据4、读取列中前10个值5、读取索引标签为1 、 2 、 3 、 5和8的记录6、包含索引标签为0 、1 、10和100的记录的country 、province 、 region_1和region_2列7、 前 100 条记录的country和variety列8、包含Italy葡…...

【flask】 flask redis的使用
目的:如何使用在flask web项目中连接redis,并简单的使用 使用的库包:flask-redis pip install falsk-redis下面的写法是对项目代码进行模块化拆分的写法,在app.py中只进行对象的初始化等操作;exts.py中创建对象&…...
【Unity基础】Unity中的特殊文件夹详解
在Unity项目中,通常可以根据需要创建任意名称的文件夹来组织项目内容,但有一些特定的文件夹名称会触发Unity对其中资源和脚本的特殊处理。这篇文章将详细介绍这些特殊文件夹,帮助开发者在项目中合理地使用它们。 1. Assets 文件夹 Assets文…...

矩阵蠕虫,陈欣出品
第一章 陈欣是一名资深的软件工程师,专门从事分布式系统和人工智能的研究。她的最新项目叫做“MatrixWorm”,目标是创建一个简单而强大的远程控制系统。在这个系统中,控制端可以通过文字命令,让被控制端利用大语言模型的能力来理…...

python 爬虫 入门 五、抓取图片、视频
目录 一、图片、音频 二、下载视频: 一、图片、音频 抓取图片的手法在上一篇python 爬虫 入门 四、线程,进程,协程-CSDN博客里面其实有,就是文章中的图片部分,在那一篇文章,初始代码的28,29行…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...