一、React在HTML里的运用
React在HTML里的使用核心就是导入3个依赖:
<!--React核心库-->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<!--React-dom用于支持react操作DOM-->
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!--引入babel用于将jsx转为js-->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
案例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div id="root"></div>
</body>
<!--React核心库-->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<!--React-dom用于支持react操作DOM-->
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!--引入babel用于将jsx转为js-->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!--这里一定要用这个type,让babel翻译script标签中的代码-->
<script type="text/babel">
//1、创建虚拟Dom
// 方式一 JSX
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
)
//方式二 js语法创建DOM
// const VDOM=React.createElement("h1",{id:"title"},React.createElement("span",{},"Hello,React"))
//2、将虚拟DOM转成真实DOM,并插入页面
ReactDOM.render(VDOM, document.getElementById("root"))
</script>
</html>
运行效果:
执行流程如下:
二、React框架的常用操作
脚手架创建:
使用详情查看官网,这里只记录我的笔记:React 官方中文文档 – 用于构建用户界面的 JavaScript 库
npx create-react-app projectName
项目打包
打包时为了路径对应需要到package.json中添加"homepage":"./"这一属性。
1、JSX基础语法规则
案例代码:object对象不能执行渲染,arr可以通过map函数进行遍历输出。
import React from 'react';
function App () {
const num=16
const bool=true
const name="string"
const arr=[1,2,3,4,"Jack",true]
const obj={name:"Mary",age:12}
return(
<div style={{marginLeft:"20px"}}>
<div>num:{num}</div>
<div>bool:{bool}</div>
<div>name:{name}</div>
<div>arr:{arr}</div>
<div>arr遍历: {
arr.map((item,index)=>(
<p key={index}><span color={"red"}>{item}</span></p>
))
}</div>
{/*对象不能直接打印*/}
{/*<div>obj:{obj}</div>*/}
<div>obj.name:{obj["name"]}</div>
</div>
)
}
export default App;
效果:
2、state数据的使用
参考文档:State & 生命周期 – React
3、生命周期
发送网络请求一般在componentDidMount里执行,
销毁组件开销一般在componentWillUnmount里执行。
4、数据的双向绑定与Ref
方法一:
class Test extends Component {
constructor (props, context) {
super(props, context)
this.state={
val: "233"
}
}
changeVal=(e)=>{
console.log(e)
this.setState({
val: e.target.value
})
console.log(e.target.value)
}
render () {
return (
<div>
<input type="text" onChange={this.changeVal} value={this.state.val} />
</div>
)
}
}
方法二:通过ref实现
class Test extends Component {
constructor (props, context) {
super(props, context)
this.state={
val: "233"
}
//1、在构造函数里创建ref的语法
this.myRef=React.createRef()
}
search=()=>{
//3、获取DOM元素
console.log(this.myRef.current.value)
}
render () {
return (
<div>
{/*2、绑定到元素上去*/}
<input type="text" ref={this.myRef} onChange={this.search}/>
</div>
)
}
}
refs的操作参考:
Refs and the DOM – React
5、PropTypes验证规则
参考:
使用 PropTypes 进行类型检查 – React
6、React里的插槽
参考:
组合 vs 继承 – React
7、错误边界
避免一错全不渲染的情况
参考:错误边界 – React
8、直接操作refs元素
函数组件里使用:
参考:Refs 转发 – React
9、高阶组件的运用案例
参考:高阶组件 – React
实现组件加载时间的复用
import React from "react"
export default function showTime (Comp) {
return class extends React.Component {
constructor (props, context) {
super(props, context)
this.state = {
startTime: new Date().getTime(),
loadingTime: 0
}
}
componentDidMount () {
let endTime = new Date().getTime()
this.setState({
loadingTime: endTime - this.state.startTime
})
}
render () {
return (
<Comp loadingTime={this.state.loadingTime}/>
)
}
}
}
这样便实现了高阶组件的复用,需要使用这个功能时,只需要调用该函数对象进行功能追加即可。
10、性能优化
参考:性能优化 – React
1、新版本以后Component用PureComponent,
PureComponent自带state和props的检查,但其中有变化时才重写渲染,否则不重新渲染,提升性能。
函数组件中的优化:相当于pureComonent
React.memo()生命周期钩子,相当于pureCompnent会在props和state变化时才发生更新。
案例代码:
import React from "react"
const componentTwo = React.memo((props) => {
return (
<div>
</div>
)
})
export default componentTwo
2、组件用完后资源记得释放
11、Hook生命周期钩子的使用
参考:Hook API 索引 – React
使用函数组件的效率一般会比类组件效率高一些,但在函数组件(无状态组件)中又没有state等属性,所以这里诞生了Hook为函数组件添加state和生命周期等元素。
Hook 简介 – React
案例一: useState 修改状态
import React,{useState} from "react"
function FunComponentOne () {
//定义一个变量,初始值为0,可通过setCount方法修改值
//第一个位置变量名,第二个位置方法名:修改变量的方法
const [count,setCount]=useState(0)
return (
<div>
<h2>{count}</h2>
<button onClick={()=>setCount(count+1)}>+</button>
</div>
)
}
export default FunComponentOne
案例二:useEffect 生命周期钩子
import React,{useState,useEffect} from "react"
function FunComponentOne () {
//定义一个变量,初始值为0,可通过setCount方法修改值
//第一个位置变量名,第二个位置方法名
const [count,setCount]=useState(0)
//第二个参数为空,相当于生命周期钩子:componentDidMount+componentDidUpdate
//加载完成和数据修改都会走该函数
useEffect(() => {
console.log("=======================")
})
//第二个参数为空数组[],相当于componentDidMount,可以用于网络请求
useEffect(() => {
console.log("++++++++++++++++++++++++++++")
}, [])
//第二个参数可以传入有值的数组,当数组里的变量修改,会调用该函数
useEffect(() => {
console.log("!!!!!!!!!!!!!!!!!!!!!!!!!")
}, [count])
//当第二个参数为空数组[],且有返回函数时相当于componentWillUnMount
//一般用于销毁主键
useEffect(() => {
return function clearUp(){
console.log("clearUp")
}
},[])
return (
<div>
<h2>{count}</h2>
<button onClick={()=>setCount(count+1)}>+</button>
</div>
)
}
export default FunComponentOne
案例3: Hook reducer类似于升级版的useState
参考:Hook API 索引 – React
案例4:自定义Hook 降低耦合度
参考:自定义 Hook – React
可以将频繁调用的hook定义到自己的hook里,注意要用use开头
12、React里的计算属性
相当于Vue里的Computed
三、组件之间的传值
1、父子组件之间传值
通过组件传参的方式:
<ChildOne num={12}/>
<ChildTwo num={24}/>
子组件接收参数:
class ChildTwo extends Component {
render () {
return (
<div>
<h2>我是子组件ChildTwo: {this.props.name}</h2>
</div>
)
}
}
function ChildOne (prop) {
return(
<div>
<h2>ChildOne子组件:{prop.num}</h2>
</div>
)
}
2、子向父传值
通过父组件传递方法对象给子组件,子组件再调用该方法并传入对应参数和处理给父组件。
父组件:
function ParentOne () {
function getSonData (data) {
console.log(data)
}
return (
<div>
<ChildThree getSonData={getSonData} />
</div>
)
}
子组件:
function ChildThree (prop) {
function sendData(){
prop.getSonData("我是子组件的数据")
}
return(
<div>
<button onClick={sendData}>点击向父组件传值</button>
</div>
)
}
这里父组件将getSonData方法对象先传递给子组件,子组件拿到方法对象后可以通过prop进行调用并传入子组件的参数到方法里,此时会调用父组件里的方法以拿到子组件的数据。
3、context实现跨层级通信
参考:Context – React
(1)首先,创建一个MyContext.js用来管理context环境变量
import React from "react"
//创建中间仓库
const MyContext=React.createContext(undefined)
export default MyContext
(2)案例
第一层父组件,提供数据
class LayerOne extends Component {
constructor (props, context) {
super(props, context)
this.state={
name: "cute Tom"
}
}
render () {
return (
<div>
<h1>我是one</h1>
<MyContext.Provider value={this.state}>
<LayerTwo/>
</MyContext.Provider>
</div>
)
}
}
第二层:包含第三层
class LayerTwo extends Component {
render () {
return (
<div>
<h2>我是two</h2>
<LayerThree/>
</div>
)
}
}
第三层可以直接消费数据,注意这里需要声明一下是哪个MyContext
class LayerThree extends Component {
render () {
return (
<div>
<h3>我是three</h3>
<MyContext.Consumer>
{value => <div><h2>{value.name}</h2></div>}
</MyContext.Consumer>
</div>
)
}
}
LayerThree.contextType=MyContext
效果:
context hook案例
函数组件中的使用:Context Hook
链接:useContext使用 - 简书
四、网络请求框架使用
见我博客:
前端框架 网络请求 Fetch Axios_Dragon Wu的博客-CSDN博客
五、React路由的使用
官方文档:React Router: Declarative Routing for React.js
参考:
React Router 6 (React路由) 最详细教程-阿里云开发者社区
6.v新特性:
react-router 6 新特性总结 - 知乎
首先安装依赖:
yarn add react-router-dom@6
声明式导航
(推荐使用编程式导航效率更高)
案例代码:一般使用BrowseRouter有history记录
import React, { Component } from "react"
import { BrowserRouter, Routes, Route, Navigate, NavLink, useParams,Link,Outlet } from "react-router-dom"
class Home extends Component {
render () {
return (
<div>
<h1>Home</h1>
</div>
)
}
}
class About extends Component {
render () {
return (
<div>
<h1>About</h1>
</div>
)
}
}
function Detail () {
console.log(useParams())
return (
<div>
<h2>详情</h2>
<p>id: {useParams().id}</p>
</div>
)
}
function Main(){
return (
<div>
<h1>文档</h1>
<Link to={"/doc/one"}>文档1</Link>
<Link to={"/doc/two"}>文档2</Link>
<Link to={"/doc/three"}>文档3</Link>
{/*嵌套路由时注意书写这个标签*/}
<Outlet/> {/* 指定路由组件呈现的位置 */}
</div>
)
}
function App () {
return (
<div>
<BrowserRouter>
<div>
<NavLink to={"/home"}>首页</NavLink>|
<NavLink to={"/about"}>关于</NavLink>|
<NavLink to={"/detail/123"}>详情</NavLink>|
<NavLink to={"/doc"}>文档</NavLink>
</div>
<Routes>
<Route path={"/home"} element={<Home />} />
<Route path={"/about"} element={<About />} />
{/*嵌套路由*/}
<Route path={"doc"} element={<Main/>}>
<Route path={"one"} element={<h3>one</h3>} />
<Route path={"two"} element={<h3>two</h3>} />
<Route path={"three"} element={<h3>three</h3>} />
</Route>
{/*动态路由*/}
<Route path={"/detail/:id"} element={<Detail />} />
{/*路由重定向,也可用于404处理*/}
<Route path="/*" element={<Navigate to="/home" replace />} />
{/*404处理*/}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</div>
)
}
export default App
// 用来作为 404 页面的组件
const NotFound = () => {
return <div>你来到了没有知识的荒原</div>
}
编程式导航
(1)新建src/router/index.js文件
import { Navigate } from "react-router-dom"
import Home from "../page/home/Home"
import HomeLeft from "../page/home/HomeLeft"
import HomeRight from "../page/home/HomeRight"
import About from "../page/About"
import Detail from "../page/Detail"
const routes=[
{
path: "/",
element: <Navigate to="/home" />
},
{
path: "home",
element: <Home />,
children: [
{
index: true,
element: <HomeLeft />
},
{
path: "right",
element: <HomeRight />
}]
},
{
path: "/about",
element: <About />
},
{
path: "/detail",
element: <Detail />
},
{ path: "*", element: <p>404页面不存在</p> }
]
export default routes
(2)为src/index.js添加BrowserRouter容器
注意: BrowserRouter必须在App标签的外层
** (3)App.js如下:**
import React from 'react'
import routes from "./router/index.js"
import {useRoutes,NavLink } from "react-router-dom"
function App () {
// useRoutes可以用路由表生成<Routes>...</Routes>结构
// 根据路由表生成对应的路由规则
const element = useRoutes(routes)
return(
<div id="App">
<div>
<NavLink to={"/home"} >首页</NavLink>|
<NavLink to={"/about"}>关于</NavLink>|
<NavLink to={"/detail"}>详情</NavLink>
</div>
<div>
{element}
</div>
</div>
)
}
export default App
**(4)若有嵌套路由要使用<Outlet/>标签标明子组件插入的位置**
class Home extends Component {
render () {
return (
<div>
<h1>Home</h1>
<Link to={"/home/right"}>homeRight</Link>
<Link to={"/home"}>homeLeft</Link>
{/*嵌套路由时注意书写这个标签*/}
<Outlet/> {/* 指定路由组件呈现的位置 */}
</div>
)
}
}
编程式跳转
默认是
push
模式
export default function HomeNews() {
const navigate = useNavigate();
const jump = ()=>{
navigate('/home')
}
return (
<News>
<button onClick={jump}>点击跳转</button>
</News>
)
}
使用
{replace:true}
就会变为
replace
模式
navigate('/home', { replace: true });
也可以使用
navigate(-1)
传入一个数字来进行跳转
navigate(1)//传入数字
六、Anti Desgin的使用实现UI界面
官方文档:Ant Design - A UI Design Language
1、添加到项目:
yarn add antd
2、样式引入
全局映入样式:在src/index.js里引入样式,不推荐,会导入很多无用的样式
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
按需映入,推荐
下面两种方式都可以只加载用到的组件。
- 使用 babel-plugin-import(推荐)。
yarn add babel-plugin-import``````// .babelrc or babel-loader option{ "plugins": [ ["import", { "libraryName": "antd", "style": "css" }] // `style: true` 会加载 less 文件 ]}
- 然后只需从 antd 引入模块即可,无需单独引入样式。等同于下面手动引入的方式。
// babel-plugin-import 会帮助你加载 JS 和 CSSimport { DatePicker } from 'antd';
- 手动引入
import DatePicker from 'antd/lib/date-picker'; // 加载 JSimport 'antd/lib/date-picker/style/css'; // 加载 CSS// import 'antd/lib/date-picker/style'; // 加载 LESS
3、组件里直接调用即可
七、Redux中央仓库的使用
当我们的项目稍微复杂一些时,原生的state可能无法高效的管理和操作数据缓存,通过Redux可以将数据统一管理,并且减低代码耦合。
参考:入门 Redux | Redux 中文官网
其工作原理与hook reducer类似
yarn add @reduxjs/toolkit
安装参考:安装 | Redux 中文官网
1、新建src/redux/index.js用于存放redux的文件
//1、引入redux
import { configureStore } from "@reduxjs/toolkit"
//2、创建仓库
const store = configureStore({reducer})
//3、reducer为store服务的执行者, action={type:"",data:5}
function reducer (preState = 10, action) {
switch (action.type) {
case "add":
return preState + action.data
case "sub":
return preState - action.data
default:
return preState
}
}
//4、使用store
console.log(store)
//获取仓库的数据
console.log(store.getState())
//触发action
store.dispatch({
type: "add",
data: 5
})
console.log(store.getState())
2、在src/index.js里引入
运行结果:可以看到数据已经被操作了
3、可以通过logger查看redux日志
yarn add redux-logger
添加logger后:
//1、引入redux
import { configureStore } from "@reduxjs/toolkit"
//引入日志
import { logger } from "redux-logger/src"
//2、创建仓库
// const store = configureStore({reducer})
const store = configureStore({
reducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger)
})
//3、reducer为store服务的执行者, action={type:"",data:5}
function reducer (preState = 10, action) {
switch (action.type) {
case "add":
return preState + action.data
case "sub":
return preState - action.data
default:
return preState
}
}
//4、使用store
console.log(store)
//获取仓库的数据
console.log(store.getState())
//触发action
store.dispatch({
type: "add",
data: 5
})
console.log(store.getState())
可以看到日志的打印:
**合并多个reducer **
//1、引入redux
import { configureStore} from "@reduxjs/toolkit"
//引入日志
import { logger } from "redux-logger/src"
//2、创建仓库
// const store = configureStore({reducer})
const store = configureStore({
//合并多个reducer
reducer:{
reducer,
reducer2
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger)
})
//3、reducer为store服务的执行者, action={type:"",data:5}
function reducer (preState = 10, action) {
switch (action.type) {
case "add":
return preState + action.data
case "sub":
return preState - action.data
default:
return preState
}
}
function reducer2 (preState = { user: "", num: 5 }, action) {
const { type, data } = action
let newState = { ...preState }
switch (type) {
case "addUser":
newState.user = data.user
return newState
case "delUser":
newState.user = ""
return newState
default:
return newState
}
}
//4、使用store
console.log(store)
//获取仓库的数据
console.log(store.getState())
// //触发action
store.dispatch({
type: "add",
data: 5
})
console.log(store.getState())
store.dispatch({
type: "addUser",
data: {
user: "大猫"
}
})
store.dispatch({
type: "delUser"
})
运行结果:
4、Redux的模块化管理
项目书写时我们需要加上命名空间以避免重复:
另外,我们还需要将常量单独提取到一个文件里管理:
分类管理reducers:
reducer/index.js: 引入所有reducers方便调用
import { count } from "./countReducer"
import {user} from "./userReducer"
export const reducers={
count,
user
}
redux/index.js引入reducer的索引文件即可:
//1、引入redux
import { configureStore} from "@reduxjs/toolkit"
//引入日志
import { logger } from "redux-logger/src"
//引入reducers
import { reducers } from "./reducers"
//2、创建仓库
// const store = configureStore({reducer})
const store = configureStore({
//合并多个reducer
reducer: reducers,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger)
})
5、注意事项
返回值注意:
书写时最好按照这种格式的逻辑:
redux默认不支持异步操作,一般实现思路是等异步任务做完后同步回调时进行redux操作:
6、总结Redux使用的完整步骤
安装依赖:
yarn add @reduxjs/toolkit
yarn add react-redux
日志依赖:
yarn add redux-logger
(1)创建store仓库,reducers分开管理
reducers: 数据开发中type字符串应放入一个常量管理
export function count(preState={num:0},action){
const {type,data}=action
let newState={...preState}
switch (type) {
case "num/add":
newState.num+=data.num
return newState
case "num/sub":
newState.num-=data.num
return newState
default:
return newState
}
}
export function user(preState={user:{name:"",age:1}},action){
const {type,data}=action
let newState={...preState}
switch (type) {
case "user/update":
newState.user=data.user
return newState
case "user/delete":
newState.user={name:"",age:1}
return newState
default:
return newState
}
}
索引所有reducers:
import { count } from "./countReducer"
import { user } from "./userReducer"
export const reducers = {
count,
user
}
创建store仓库并开启日志:
//1、引入redux
import { configureStore } from "@reduxjs/toolkit"
//引入日志
import { logger } from "redux-logger/src"
//导入reducers
import { reducers } from "./reducers"
//2、创建仓库
// const store = configureStore({reducer})
const store = configureStore({
//合并多个reducer
reducer: reducers,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger)
})
export default store
(2)导入到src/index.js文件,全局配置
(3)组件中的使用
import React from "react"
import { connect } from "react-redux"
function ReduxTestComp (props) {
return (
<div>
<h1>count: {props.count.num}</h1>
<h1>user:{props.user.user.age},{props.user.user.name}</h1>
<button onClick={()=>props.updateUser({user:{ name: "Jack", age: 12 }})}>修改用户</button>
<button onClick={()=> props.deleteUser()}>删除用户</button>
<button onClick={()=>props.addNum()}>+</button>
</div>
)
}
export default connect((state) => {//读取仓库中所有state
console.log(state)
return {
count: state.count,
user: state.user
}
}, (dispatch) => {//action操作
console.log(dispatch)
return {
updateUser: (data) => {
return dispatch({ type: "user/update", data: data })
},
deleteUser: ()=>{
return dispatch({type:"user/delete"})
},
addNum:()=>{
return dispatch({type:"num/add",data:{num:12}})
}
}
})(ReduxTestComp)
import React from "react"
import ReduxTestComp from "./component/reduxTest/ReduxTestComp"
function App () {
return (
<div id="App">
<ReduxTestComp />
</div>
)
}
export default App
至此,实现redux的全程应用。
版权归原作者 Dragon Wu 所有, 如有侵权,请联系我们删除。