目录
1、新建src/redux/index.js用于存放redux的文件
React在HTML里的使用核心就是导入3个依赖:
- <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js">script>
- <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js">script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js">script>
案例代码如下:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>testtitle>
- head>
- <body>
- <div id="root">div>
- body>
- <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js">script>
- <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js">script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js">script>
- <script type="text/babel">
- //1、创建虚拟Dom
- // 方式一 JSX
- const VDOM = (
- <h1 id="title">
- <span>Hello,Reactspan>
- 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 官方中文文档 – 用于构建用户界面的 JavaScript 库
npx create-react-app projectName
打包时为了路径对应需要到package.json中添加"homepage":"./"这一属性。
案例代码: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;
效果:
参考文档:State & 生命周期 – React
发送网络请求一般在componentDidMount里执行,
销毁组件开销一般在componentWillUnmount里执行。
方法一:
- 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的操作参考:
参考:
参考:
避免一错全不渲染的情况
参考:错误边界 – React
函数组件里使用:
参考:高阶组件 – 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}/>
- )
- }
- }
- }
这样便实现了高阶组件的复用,需要使用这个功能时,只需要调用该函数对象进行功能追加即可。
参考:性能优化 – 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、组件用完后资源记得释放
使用函数组件的效率一般会比类组件效率高一些,但在函数组件(无状态组件)中又没有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开头
相当于Vue里的Computed
通过组件传参的方式:
- <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>
- )
- }
通过父组件传递方法对象给子组件,子组件再调用该方法并传入对应参数和处理给父组件。
父组件:
- 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进行调用并传入子组件的参数到方法里,此时会调用父组件里的方法以拿到子组件的数据。
(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>我是oneh1>
- <MyContext.Provider value={this.state}>
- <LayerTwo/>
- MyContext.Provider>
- div>
- )
- }
- }
第二层:包含第三层
- class LayerTwo extends Component {
- render () {
- return (
- <div>
- <h2>我是twoh2>
- <LayerThree/>
- div>
- )
- }
- }
第三层可以直接消费数据,注意这里需要声明一下是哪个MyContext
- class LayerThree extends Component {
- render () {
- return (
- <div>
- <h3>我是threeh3>
- <MyContext.Consumer>
- {value => <div><h2>{value.name}h2>div>}
- MyContext.Consumer>
- div>
- )
- }
- }
-
- LayerThree.contextType=MyContext
效果:
函数组件中的使用:Context Hook
见我博客:
前端框架 网络请求 Fetch Axios_Dragon Wu的博客-CSDN博客
官方文档: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>Homeh1>
- div>
- )
- }
- }
-
- class About extends Component {
- render () {
- return (
- <div>
- <h1>Abouth1>
- 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"}>文档1Link>
- <Link to={"/doc/two"}>文档2Link>
- <Link to={"/doc/three"}>文档3Link>
- {/*嵌套路由时注意书写这个标签*/}
- <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>oneh3>} />
- <Route path={"two"} element={<h3>twoh3>} />
- <Route path={"three"} element={<h3>threeh3>} />
- 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可以用路由表生成
... 结构 - // 根据路由表生成对应的路由规则
- 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)若有嵌套路由要使用标签标明子组件插入的位置
- class Home extends Component {
- render () {
- return (
- <div>
- <h1>Homeh1>
- <Link to={"/home/right"}>homeRightLink>
- <Link to={"/home"}>homeLeftLink>
- {/*嵌套路由时注意书写这个标签*/}
- <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)//传入数字
官方文档: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、组件里直接调用即可
当我们的项目稍微复杂一些时,原生的state可能无法高效的管理和操作数据缓存,通过Redux可以将数据统一管理,并且减低代码耦合。
其工作原理与hook reducer类似
yarn add @reduxjs/toolkit
安装参考:安装 | 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())
运行结果:可以看到数据已经被操作了
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"
- })
运行结果:
项目书写时我们需要加上命名空间以避免重复:
另外,我们还需要将常量单独提取到一个文件里管理:
分类管理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)
- })
-
返回值注意:
书写时最好按照这种格式的逻辑:
redux默认不支持异步操作,一般实现思路是等异步任务做完后同步回调时进行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的全程应用。