【React】前端框架 React 学习总结
目录
1、新建src/redux/index.js用于存放redux的文件
一、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// 方式一 JSXconst 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=16const bool=trueconst 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的操作参考:
5、PropTypes验证规则
参考:
6、React里的插槽
参考:
7、错误边界
避免一错全不渲染的情况
参考:错误边界 – React
8、直接操作refs元素
函数组件里使用:
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生命周期钩子的使用
使用函数组件的效率一般会比类组件效率高一些,但在函数组件(无状态组件)中又没有state等属性,所以这里诞生了Hook为函数组件添加state和生命周期等元素。
案例一: 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
案例4:自定义Hook 降低耦合度
可以将频繁调用的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实现跨层级通信
(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
四、网络请求框架使用
见我博客:
前端框架 网络请求 Fetch Axios_Dragon Wu的博客-CSDN博客
五、React路由的使用
官方文档:React Router: Declarative Routing for React.js
参考:
React Router 6 (React路由) 最详细教程-阿里云开发者社区
6.v新特性:
首先安装依赖:
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 和 CSS
- import { DatePicker } from 'antd';
手动引入
- import DatePicker from 'antd/lib/date-picker'; // 加载 JS
- import 'antd/lib/date-picker/style/css'; // 加载 CSS
- // import 'antd/lib/date-picker/style'; // 加载 LESS
3、组件里直接调用即可
七、Redux中央仓库的使用
当我们的项目稍微复杂一些时,原生的state可能无法高效的管理和操作数据缓存,通过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/toolkityarn 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的全程应用。
猜你喜欢
- 【AntDesignPro】Ant Design Pro学习记录—ModalForm的使用(一)
- 目录前言一、ModalForm销毁二、ModalForm编辑赋值三、ProFormUploadButton赋值四、其它总结前言使用了AntDesignPro,仿照TableList创建了自己的列表,列表添加编辑确成了困扰,添加编辑使用了ModalForm,有两个问题,一个使用后,页面数据无法清除,再点弹框还是原来的数据,第二个编辑的时候,初始数据赋值问题。AntDesignPro版本V5,开发工具VsCode。一、ModalForm销毁参考ant design的关闭ModalForm和Modal
- 【React】 react 绘制矩形
- 在React中绘制矩形,可以使用HTML5的<canvas>元素结合React组件的生命周期方法来实现。以下是一个简单的React组件示例,用于绘制矩形:import React, { Component } from 'react'; class Rectangle extends Component { componentDid
- 【React】react面试题
- React面试题文章目录React面试题一、react特性***React与Vue的区别*******1. Jsx的使用规范*******1.说说对 React 的理解?有哪些特性?DOM2.说说 Real DOM 和 Virtual DOM 的区别?优缺点?*******说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系*******3.说说React Jsx转换成真实DOM过程?4.说说你第mvc和mvvm的理解5.说说react中引入css的方式有哪几种?区
- 【AntDesignPro】L7Plot地理可视化组件的使用
- L7Plot介绍L7Plot 基于 L7 实现的开箱即用地理空间数据可视化图表库L7Plot 专注于地理可视化图表。以声明配置式的方式,降低用户使用成本;以常见地理图表分类的方式,降低用户选择成本;内部集成全国行政区域数据,降低用户使用地理数据心智;支持多图层及多图表层叠,方便用户定制复杂的业务场景;L7Plot 专注于地理数据可视化展示,不会涉及数据编辑能力。前言项目需要,使用antdesignpro做前端,并绘制可视化大屏,回执地图组件,于是使用了L7Plot。功能目的绘
- 【AntDesignPro】Ant Design Pro学习记录—自定义菜单选中
- 页面增删改查,打开子页面时,要让父页面菜单选中,参考官网给出的方案菜单的高级用法 - Ant Design Pro 使用的V5版本,直接设置 parentKeys:['/product'] 即可export default [ { path: '/product', // 不展示菜单
- 【AntDesignPro】Ant Design Pro学习记录—DrawerForm的使用
- 在AntDesignPro中,ModalForm和DrawerForm是我最常用的两个表单组件,配置上,他们有很多相同的地方,也有差异的地方,相比较而言,DrawerForm我使用的更多。ModalForm<ModalForm title={标题} width={600} &n
- 【AntDesignPro】Ant Design Pro学习记录—ProTable的使用(二)
- 目录一、列表检索初始值设置二、字段缩略显示一、列表检索初始值设置使用initialValue属性,配置列表检索的初始值const columns: ProColumns<API.RoomItem>[] = [ { title: '名称', &nb
- 【React】使用React实现一个内容滑动组件
- 最近在做项目时遇到一个需求,需要让一个列表能够通过点击按钮进行滚动,每次都是一屏的距离,不足则结束。并且,这个列表项是在react-grid-layout中的某一个模块内。所以包裹这个列表的容器会随时发生变化。在完成这个组件后,通过这篇文章总结一下。UI/原型分析那么从上面的功能描述以及项目中的UI,我们可以分析得到这样一个假想图:我们需要实现一个容器来作为我们的可视区域,并且这个容器是可以伸缩的。列表内容如果超出容器的可视区域,那么就会被隐藏。需要左右都有按钮,来支持用户左右滑动内容来查看,每
- 【UniApp】UniApp实现动态效果与动画展示的设计与开发方法
- 【MySql】MySql给数据库表添加新字段
- 【Vue】uniapp(vue3)+node.js+websocket(实现实时通信效果)
- 【Vue】Antd Pro Vue的使用(四)—— 打包发布到站点二级目录,生产环境请求接口配置
- 【PHP】php哪些函数可以用来去幂方值
- 【UniApp】Uniapp中如何实现左右点击滚动功能
- 【Python】如何使用Python实现拓扑排序算法
- 【Python】如何使用Python中的数据分析库进行数据处理
- 【PHP】php中的compact()的用法
- 【UniApp】如何在uniapp中实现图片上传和预览