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行…...
ubantu 编译安装ceph 18.2.4
下载ceph代码 git clone https://github.com/ceph/ceph.git #切换tag git checkout v18.2.4 -b v18.2.4 #下载子模块 会有报错重新执行即可 git submodule update --init --recursive安装ceph所需要的依赖 #curl命令安装 sudo apt install curl#安装ceph依赖 ./install-deps.…...
哈希封装“unordered_set·map“
本文与对setmap的封装高度相似,可以参考我之前的对setmap封装的文章: 链接:(没看过的话就点点我吧😚😚😚😚😚😚😚😚😚&am…...
Bi-LSTM-CRF实现中文命名实体识别工具(TensorFlow)
项目源码获取方式见文章末尾! 回复暗号:13,免费获取600多个深度学习项目资料,快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【MobileNetV2实现实时口罩检测tensorflow】 2.【卫星图像道路检测DeepLabV3P…...
从JDK 17 到 JDK 21:Java 新特性
JDK17 密封类 概念:密封类允许开发者控制哪些类可以继承或实现特定的类或接口。通过这种方式,密封类为类的继承提供了更高的安全性和可维护性。 定义:使用sealed代表该类为密封类,并用permits限制哪些类可以继承。 public sea…...
【计算机网络 - 基础问题】每日 3 题(五十七)
✍个人博客:https://blog.csdn.net/Newin2020?typeblog 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞…...
第十二章 章节练习created的应用
目录 一、引言 二、运行效果图 三、完整代码 一、引言 构建一个新闻的页面,页面在响应式数据准备好之后(即created),就向后台接口请求获取新闻数据列表,然后赋值给Vue实例中的list列表,这个请求逻辑我…...
Unity 游戏性能优化实践:内存管理与帧率提升技巧
1. 引言 随着移动设备性能的逐步提升,游戏玩家对画质和流畅度的要求越来越高。优化 Unity 游戏性能不仅可以提升用户体验,还能降低设备的功耗,延长电池寿命。这篇文章将深入探讨如何在 Unity 中优化游戏的内存管理与帧率,通过多方…...
C++游戏开发详解
C 是一种广泛使用的编程语言,尤其在游戏开发领域有着不可替代的地位。它提供了对底层硬件的直接访问能力,允许开发者优化性能,这对于追求高帧率和低延迟的游戏来说至关重要。本文将详细介绍使用 C 进行游戏开发的基础知识和技术要点ÿ…...
三、大模型(LLMs)微调面
本文精心汇总了多家顶尖互联网公司在大模型基础知识考核中的核心考点,并针对这些考点提供了详尽的解答。并提供电子版本,见于文末百度云盘链接中,供读者查阅。 一、大模型微调 • 1 如果想要在某个模型基础上做全参数微调,究竟需要…...
Flutter升级与降级
升级 版本升级 // 升级到指定版本flutter upgrade 版本号// 升级到最新版本flutter upgrade 降级 1.需要先确定想要降级的版本号。 2.切换到系统安装Flutter的目录 3.在https://github.com/flutter/flutter,找到要回退的版本号对应的commit序号(具…...
wordpress支付配置/seo文章外包
问题:jsonsql中文排序不支持首先需要判断传入字符串是否含有中文,正则啥的,大家随便上吧然后需要对中文增加一条localeCompare的规则紧接着,高潮~需要把中文转换成拼音(要不你说怎么搞?)用拼音做排序,这里需…...
信息技术用C 做登录界面网站 csdn/建网站建设
影调: 对摄影作品而言,“影调”,又称为照片的基调或调子。指画面的明暗层次、虚实对比和色彩的色相明暗等之间的关系。通过这些关系,使欣赏者感到光的流动与变化。摄影画面中的线条、形状、色彩等元素是由影调来体现的࿰…...
企业网站开发注册/如何自己做一个网站
2019独角兽企业重金招聘Python工程师标准>>> <span style"color:grey">就像我们时不时的打开QQ,看看微信,在工作岗位上的职员们离不开邮件。工作安排得依靠邮件通知,任务完成情况得用邮件汇报,大量邮件…...
wordpress代码添加/智能建站平台
问题简述:登录MSSQLSERVER的时候,如果输入ip而导致无法登录,可以参考一下步骤,设置完成后即可使用ip登录数据库step1:step2:step3:step4:step5:解决MSSQLSERVER连接服务器时候,输入IP无法连接的问题问题描述࿱…...
在网页做动态图片的网站/杭州网络推广有限公司
虚拟机快照操作 1.什么是Bash shell? 它就是命令解释器,将用户输入的指令翻译给内核程序,内核处理完成之后将结果返回给Bash 2.Bash shell的用途? 几乎能完成所有的操作: 文件管理 (创建 移动 复制 删除 编…...
网站制作中文版/百度关键词搜索次数
原题地址:http://oj.leetcode.com/problems/insertion-sort-list/ 题意:对链表进行插入排序。 解题思路:首先来对插入排序有一个直观的认识,来自维基百科。 代码循环部分图示: 代码: class Solution: # par…...