React最新企业标准技术栈(React18+Redux+ ReactRouter +AntD),React+TypeScript(为大型项目奠定基础)

一、React介绍
React由Meta公司开发,是一个用于 构建Web和原生交互界面(IOS和Android上面的APP)的库
(1)、组件化的开发方式
(2)、不错的性能
相较于其它前端框架(如VUE)的优势
(1)、丰富的生态
(2)、跨平台支持
全球最流行,大厂必备

npx create-react-app react-basic
-
-
create-react-app - 核心包(固定写法),用于创建React项目
-
react-basic React项目的名称(可以自定义)
打开D:\project\react目录,cmd进入后输入命令:npx create-react-app react-basic,执行完成后如下所示:

此时react目录下就多出了一个react-basic目录

目录结构如下所示:

启动
cd react-basic
npm start
报错:

移除 Jest 相关的 ESLint 配置(推荐,最快捷)
如果你的项目目前不需要使用 Jest 进行测试,或者只是想先让项目跑起来,可以直接在 package.json 中删掉 react-app/jest 这一项。
将你的 eslintConfig 修改为:
"eslintConfig": { "extends": [ "react-app" ] }
修改后重新启动

保留src目录下的index.js和App.js,其他文件全部删除,如下所示:

index.js是整个项目的入口,从这里开始运行
index.js中的内容如下:
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals();
清理后如下所示:
// index.js是整个项目的入口,从这里开始运行 // react必要的两个核心包 import React from 'react'; import ReactDOM from 'react-dom/client'; // 导入项目的根组件 import App from './App'; // 把APP跟组件渲染到ip为root的dom节点上,root节点在public/index.html中 const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
把项目的根组件App以react组件的方式渲染到public/index.html的root节点上

App.js内容如下:
import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;
清理后如下所示
function App() { return ( <div className="App"> this is APP </div> ); } export default App;
App.js是项目的根组件,
此时页面效果如下:

二、
概念:JSX是JavaScript和XMl(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中构建UI的方式
const message = 'this is message' function App() { return ( <div className="App"> <h1>this is title</h1> {message} </div> ); } export default App;
优势:
-
HTML的声明式模版写法
-
JavaScript的可编程能力
JSX并不是标准的JS语法,它是 JS的语法扩展,浏览器本身不能识别,需要通过解析工具BABEL做解析之后才能在浏览器中使用

使用JS变量
(3)、函数调用和方法调用
-
使用JavaScript对象
注意:if语句、switch语句、变量声明不属于表达式,不能出现在{}中
const message = 'this is message' function getAge(){ return 18 } function App() { return ( <div className="App"> <h1>this is title</h1> {/* 字符串识别 */} {'this is str'} {/* 变量识别 */} {message} {/* 变量识别 */} {message} {/* 函数调用 渲染为函数的返回值 */} {getAge()} {/* 方法调用 */} {new Date().getDate()} {/* 使用js对象 */} <div style={{color: 'red'}}>this is div</div> </div> ); } export default App;
效果:


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;
效果如下:


在React中,可以通过逻辑与运算符&&、三元表达式(?:) 实现基础的条件渲染
const flag = true const loading = false function App() { return ( <> {flag && <span>this is span</span>} {loading ? <span>loading...</span>:<span>this is span</span>} </> ); } export default App;
效果:this is spanthis is span
需求:列表中需要根据文章的状态适配
解决方案:自定义函数 + 判断语句
const type = 1 // 0|1|3 function 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;
效果:单图模式模版
三、

在事件回调函数中设置形参e即可
function App() { const clickHandler = (e)=>{ console.log('button按钮点击了', e) } return ( <button onClick={clickHandler}>click me</button> ); } export default App;
效果:

function App() { const clickHandler = (name)=>{ console.log('button按钮点击了', name) } return ( <button onClick={()=>clickHandler('jack')}>click me</button> ); } export default App;
效果:

注意:不能直接写函数调用,这里事件绑定需要一个函数引用
语法:在事件绑定的位置传递事件实参e和自定义参数,clickHandler中声明形参,注意顺序对应
function App() { const clickHandler = (name,e)=>{ console.log('button按钮点击了', name,e) } return ( <button onClick={(e)=>clickHandler('jack',e)}>click me</button> ); } export default App;
效果:

四、
概念:一个组件就是一个用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以服用多次

// 1. 定义组件 function Button(){ return <button>click me</button> } function App() { return ( <div> {/* 自闭和 */} <Button/> {/* 成对标签 */} <Button></Button> </div> ); } export default App;
效果:


// useState实现一个计数器按钮 import { useState } from 'react' function App() { // 1. 调用useState添加一个状态变量 // count 状态变量 // setCount 修改状态变量的方法 const [ count, setCount ] = useState(0) // 初始值为0,返回一个数组, return ( <div> <button onClick={()=>setCount(count+1)}>{ count }</button> </div> ); } export default App;

另一种写法
// useState实现一个计数器按钮 import { useState } from 'react' function App() { // 1. 调用useState添加一个状态变量 // count 状态变量 // setCount 修改状态变量的方法 const [ count, setCount ] = useState(0) // 2. 点击事件回调 const handleClick = () => { // 作用: // 1. 用传入的新值修改count // 2. 重新使用新的count渲染UI setCount(count + 1) } return ( <div> <button onClick={handleClick}>{ count }</button> </div> ); } export default App;
下面的代码是修改count的值,不会引发视图更新
// useState实现一个计数器按钮 import { useState } from 'react' function App() { // 1. 调用useState添加一个状态变量 // count 状态变量 // setCount 修改状态变量的方法 let [ count, setCount ] = useState(0) // 2. 点击事件回调 const handleClick = () => { // 作用: // 1. 用传入的新值修改count // 2. 重新使用新的count渲染UI count++ console.log(count) } return ( <div> <button onClick={handleClick}>{ count }</button> </div> ); } export default App;
下面的方法使用setCount方法,用传入的新值count+1修改count,重新使用新的count渲染UI
// useState实现一个计数器按钮 import { useState } from 'react' function App() { // 1. 调用useState添加一个状态变量 // count 状态变量 // setCount 修改状态变量的方法 const [ count, setCount ] = useState(0) // 2. 点击事件回调 const handleClick = () => { // 作用: // 1. 用传入的新值修改count // 2. 重新使用新的count渲染UI setCount(count + 1) console.log(count) } return ( <div> <button onClick={handleClick}>{ count }</button> </div> ); } export default App;
效果:

下面的代码给useState传递一个对象,对象的属性为name,如果在回调函数中直接通过form.name进行修改是不会引发视图的变化
// useState实现一个计数器按钮 import { useState } from 'react' function App() { const [ form, setForm ] = useState({name: 'jack'}) // 2. 点击事件回调 const handleChangeName = () => { form.name = 'john' } return ( <div> <button onClick={handleChangeName}>{ form.name }</button> </div> ); } export default App;
下面的代码在回调函数中调用set方法,传过来一个全新的对象,
// useState实现一个计数器按钮 import { useState } from 'react' function App() { const [ form, setForm ] = useState({name: 'jack'}) // 2. 点击事件回调 const handleChangeName = () => { setForm({ ...form, name: 'john' }) } return ( <div> <button onClick={handleChangeName}>{ form.name }</button> </div> ); } export default App;
效果如下:

React组件基础的样式控制有俩种方式,行内样式和class类名控制
方式一:行内样式
<div style={{ color:'red'}}>this is div</div>
完整代码如下:
import './index.css' function App() { return ( <div> <div style={{ color:'red'}}>this is div</div> </div> ); } export default App;
方式二:class类名
新建index.css文件,内容如下:
.foo{
color: red;
}
完整代码如下:
import './index.css' function App() { return ( <div> <span className="foo">this is span</span> </div> ); } export default App;
效果如下:

概念:使用React组件的状态(useState)控制表单的状态

react的状态:通过useState生成状态值,这个状态值的变化会引起视图更新,
input框的状态:通过e.target.value可以拿到当前用户在input输入框里面输入的最新数据,
左边的react状态来控制右边input框的状态,input框到底显示什么由state来决定,而且input框里面输入的最新数据和state是保持同步的。
import { useState } from 'react'
function App(){
const [value, setValue] = useState('')
return (
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
/>
)
}
export default App
当用户输入最新数据的时候,state中的数据相应变化,当state中的值改变,input输入框的值也跟着改变。

概念:通过获取DOM的方式获取表单的输入数据
使用useRef创建ref对象,
const inputRef = useRef(null) // 初始值为null,
ref对象与JSX绑定
<input type="text" ref={inputRef}/>
在DOM可用时,通过inputRef.current拿到DOM对象,
console.log(inputRef.current)
获取dom元素
import React, { useRef } from 'react';
function App(){
const inputRef = useRef(null)
const showDom = ()=>{
console.log(inputRef.current)
}
return (
<div>
<input type="text" ref={inputRef}/>
<button onClick={showDom}>获取dom</button>
</div>
)
}
export default App
效果如下:

获取dom属性:
import React, { useRef } from 'react';
function App(){
const inputRef = useRef(null)
const onChange = ()=>{
console.log(inputRef.current.value)
}
return (
<div>
<input
type="text"
ref={inputRef}
onChange={onChange}
/>
</div>
)
}
export default App
效果如下:

import React, { useRef } from 'react';
function App(){
const inputRef = useRef(null)
const showDom = ()=>{
console.log(inputRef.current.value)
}
return (
<div>
<input type="text" ref={inputRef}/>
<button onClick={showDom}>获取dom</button>
</div>
)
}
export default App
效果:

五、

-
-
B-C 兄弟通信
-

实现步骤
(1)、父组件传递数据 - 在子组件标签上绑定属性
<Son name={name}/>
(2)、子组件接收数据 - 子组件通过props参数接收数据
function Son(props){ return <div>{ props.name }</div> }
完整代码如下:
function Son(props){ return <div>{ props.name }</div> } function App(){ const name = 'this is app name' return ( <div> <Son name={name}/> </div> ) } export default App

场景:当我们把内容嵌套在组件的标签内部时,组件会自动在名为children的prop属性中接收该内容


核心思路:在子组件中调用父组件中的函数并传递参数
function Son({ onGetMsg }){ const sonMsg = 'this is son msg' return ( <div> {/* 在子组件中执行父组件传递过来的函数 */} <button onClick={()=>onGetMsg(sonMsg)}>send</button> </div> ) } function App(){ const getMsg = (msg)=>console.log(msg) return ( <div> {/* 传递父组件中的函数到子组件 */} <Son onGetMsg={ getMsg }/> </div> ) } export default App
效果:


// 1. 通过子传父 A -> App // 2. 通过父传子 App -> B import { useState } from "react" function A ({ onGetAName }) { // Son组件中的数据 const name = 'this is A name' return ( <div> this is A compnent, <button onClick={() => onGetAName(name)}>send</button> </div> ) } function B ({ name }) { return ( <div> this is B compnent, {name} </div> ) } function App(){ const [name, setName] = useState('') const getAName = (name) => { setName(name) } return ( <div> this is App <A onGetAName={getAName} /> <B name={name} /> </div> ) } export default App
效果如下:


实现步骤:
-
使用
createContext方法创建一个上下文对象Ctx -
在顶层组件(App)中通过
Ctx.Provider组件提供数据 -
在底层组件(B)中通过
useContext钩子函数获取消费数据
// App -> A -> B import { createContext, useContext } from "react" // 1. createContext方法创建一个上下文对象 const MsgContext = createContext() function A () { return ( <div> this is A component <B /> </div> ) } function B () { // 3. 在底层组件 通过useContext钩子函数使用数据 const msg = useContext(MsgContext) return ( <div> this is B compnent,{msg} </div> ) } function App () { const msg = 'this is app msg' return ( <div> {/* 2. 在顶层组件 通过Provider组件提供数据 */} <MsgContext.Provider value={msg}> this is App <A /> </MsgContext.Provider> </div> ) } export default App
效果:

六、
useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比 如发送AJAX请求,更改DOM等等

说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”
需求:在组件渲染完毕之后,立刻从服务端获取频道列表数据并显示到页面中
语法:
useEffect(() => {},[])
说明:
-
参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
-
参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
import { useEffect, useState } from "react"
function App () {
// 创建一个状态数据
const [list,setList] = useState([])
useEffect(() => {
const URL = 'http://geek.itheima.net/v1_0/channels'
// 实现副作用操作逻辑-获取频道列表
async function getList(){
const res = await fetch(URL)
const jsonRes = await res.json()
console.log(jsonRes)
setList(jsonRes.data.channels)
}
getList()
},[])
return (
<div>
this is app
<ul>
{list.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
</div>
)
}
export default App
效果:

useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现

(1)、没有依赖项的情况,副作用函数的执行时机是:组件初始渲染和组件更新时执行
import { useEffect, useState } from "react"
function App () {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("副作用函数执行了!")
})
return (
<div>
this is app
<button onClick={()=>setCount(count+1)}>+{count}</button>
</div>
)
}
export default App
效果:

(2)、空数组依赖,只在初始渲染时执行一次
import { useEffect, useState } from "react"
function App () {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("副作用函数执行了!")
},[])
return (
<div>
this is app
<button onClick={()=>setCount(count+1)}>+{count}</button>
</div>
)
}
export default App
效果:

(3)、传入特定依赖项,组件初始渲染和依赖项变化时执行
import { useEffect, useState } from "react"
function App () {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("副作用函数执行了!")
},[count])
return (
<div>
this is app
<button onClick={()=>setCount(count+1)}>+{count}</button>
</div>
)
}
export default App
效果:

概念:在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉(定时器如果不及时清理会产生内存泄漏),这个过程就是清理副作用
useEffect(() => { // 实现副作用操作逻辑 return () =>{ // 清除副作用逻辑 } },[])
说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行
import { useEffect, useState } from "react"
function Son () {
// 1. 渲染时开启一个定时器
useEffect(() => {
const timer = setInterval(() => {
console.log('定时器执行中...')
}, 1000) // 每隔一秒钟
return () => {
// 清除副作用(组件卸载时)
clearInterval(timer)
}
}, []) // 空数组:只在渲染时执行一次
return <div>this is son</div>
}
function App () {
// 通过条件渲染模拟组件卸载
const [show, setShow] = useState(true)
return (
<div>
{show && <Son />}
<button onClick={() => setShow(false)}>卸载Son组件</button>
</div>
)
}
export default App
如果在清除副作用的函数中不清除定时器,那么即使卸载了Son组件,定时器依然在执行。
效果如下:我们发现:卸载Son组件后,定时器不再执行。

七、

需求:点击toggle按钮控制“this is div”的显示和隐藏
(1)、不封装直接实现
import { useState } from "react"
function App () {
const [ value, setValue ] = useState(true)
const toggle = ()=> setValue(!value)
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
export default App
如果有一天在其他地方控制的不是div,而是其他元素,还是需要把下面的两行代码再写一遍,即不可以复用。
布尔切换的逻辑是和当前组件耦合在一起的,不能在其他组件实现复用
const [ value, setValue ] = useState(true) const toggle = ()=> setValue(!value)
那有没有一种办法可以实现不管在哪个地方想要切换显示和隐藏的逻辑,都可以快速的复用过去
解决思路:自定义Hook
// 封装自定义Hook import { useState } from "react" function useToggle () { // 可复用的逻辑代码 const [value, setValue] = useState(true) const toggle = () => setValue(!value) // 哪些状态和回调函数需要在其他组件中使用 return return { value, toggle } } // 封装自定义hook通用思路 // 1. 声明一个以use打头的函数 // 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑) // 3. 把组件中用到的状态或者回调return出去(以对象或者数组) // 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用 function App () { const { value, toggle } = useToggle() return ( <div> {value && <div>this is div</div>} <button onClick={toggle}>toggle</button> </div> ) } export default App
-
-
只能在组件的顶层调用,不能嵌套在if、for、其它的函数中
(1)、在外部调用是不被允许的,如下所示

(2)、把Hook函数的执行放到if条件语句中是不被允许的,如下所示

浙公网安备 33010602011771号