react学习

在一切开始之前,react启动命令是:

复制代码
pnpm start

一、创建项目

  1. 命令
javascript 复制代码
npx create-react-app demo
cd demo
pnpm start

2.清除文件,使得src文件夹中只剩下index.js和app.js文件

3.一些常用的包

axios:

javascript 复制代码
pnpm i axios

dayjs:

javascript 复制代码
pnpm i dayjs

classnames:

javascript 复制代码
pnpm i classnames

lodash:

javascript 复制代码
pnpm i lodash

json-server:

javascript 复制代码
pnpm i json-server -D

@reduxjs/toolkit react-redux仓库的使用:

javascript 复制代码
pnpm i @reduxjs/toolkit react-redux

react-router-dom关于路由的使用:

javascript 复制代码
pnpm i react-router-dom

craco路径解析配置:

复制代码
pnpm i -D @craco/craco

组件库

复制代码
pnpm add antd-mobile

统一:用于代码复制:

复制代码
pnpm i dayjs react-router-dom @reduxjs/toolkit react-redux classnames axios lodash json-server craco antd-mobile

二、JSX语法

1.{}

引号字符串、变量、函数调用、方法调用、对象

2.条件渲染

flag &&

三元表达式

函数return

三、react

1.事件绑定及传参

基础:

javascript 复制代码
function App(){
  const clickHandler = ()=>{
    console.log('button按钮点击了')
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

事件参数:

javascript 复制代码
function App(){
  const clickHandler = (e)=>{
    console.log('button按钮点击了', e)
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

自定义参数:

javascript 复制代码
function App(){
  const clickHandler = (name)=>{
    console.log('button按钮点击了', name)
  }
  return (
    <button onClick={()=>clickHandler('jack')}>click me</button>
  )
}

自定义参数和事件参数都使用:

javascript 复制代码
function App(){
  const clickHandler = (name,e)=>{
    console.log('button按钮点击了', name,e)
  }
  return (
    <button onClick={(e)=>clickHandler('jack',e)}>click me</button>
  )
}

2.组件化

javascript 复制代码
// 1. 定义组件
function Button(){
  return <button>click me</button>
}

// 2. 使用组件
function App(){
  return (
    <div>
      {/* 自闭和 */}
      <Button/>
      {/* 成对标签 */}
      <Button></Button>
    </div>
  )
}

3.useState组件状态管理

javascript 复制代码
import { useState } from 'react'

function App() {
// useState()返回的结果是个数组
  const [age, setage] = useState(0)
  return (
    <div className="App">
      <button onClick={()=>setage(age+1)}>{ age}</button>
    </div>
  );
}

export default App;

4.样式表示

4.1行内样式

style属性内部是一个对象类型数据,因为是在html中包裹js,所以要加大括号。

4.1.1直接写对象

外层大括号是jsx语法,内层是对象类型数据的大括号。

javascript 复制代码
function App() {
  return (
    <div className="App">
      <div style={{ color: 'red', fontSize: '20px' }}>style行内样式测试</div>
    </div>
  );
}

export default App;
4.1.2抽离出对象
javascript 复制代码
const classObj = { color: 'red', fontSize: '40px' }
function App() {
  return (
    <div className="App">
      <div style={classObj}>style行内样式测试</div>
  );
}

export default App;

4.2class类名控制

className属性值是一个字符串

4.2.1基础用法

index.css

css 复制代码
.test {
    color: aqua;
    font-size: 15px;
}

index.js

注意用法:是className而不是class

javascript 复制代码
import './index.css'
function App() {
  return (
    <div className="App">
      <div className='test'>引入css文件,class类名控制</div>
    </div>
  );
}

export default App;
4.2.2示例

解读:第一种方法className属性值是一个三元表达式,所以在大括号内部。第二种方法className属性值是一个模板字符串,所以在大括号内部,${}内部是一个判断语句,如果item.type === active成立,就返回'active'否则不返回。

javascript 复制代码
return <span className={item.type === active ? 'nav-item active' : 'nav-item'} key={item.type} onClick={() => chooseActive(item.type)}>{item.text}</span>

return <span className={`nav-item ${item.type === active && 'active'}`} key={item.type} onClick={() => chooseActive(item.type)}>{ item.text}</span>
4.2.3扩展用法------classnames
4.2.3.1安装
javascript 复制代码
pnpm i classnames
4.2.3.2引入和使用

classNames函数返回值为字符串,有两个参数,第一个是字符串,表示静态类名,第二个是对象,属性名为动态类名,属性值为类名存在条件。

javascript 复制代码
import classNames from 'classnames'
return <span className={classNames('nav-item',{active:item.type === active})} key={item.type} onClick={() => chooseActive(item.type)}>{item.text}</span>

5.lodash根据某个字段对对象进行排序

5.1安装

javascript 复制代码
pnpm i lodash

5.2引入

javascript 复制代码
import _ from 'lodash

5.3使用

orderBy(对谁进行排序, 按照谁来排, 顺序)

javascript 复制代码
_.orderBy(commentList,'like','desc')

6.表单控制,双向绑定

javascript 复制代码
import { useState } from 'react'
function App() {
  const [value, setvalue] = useState('')
  return (
    <div className="App">
      <input type='text' value={value} onChange={(e)=>{setvalue(e.target.value)}}></input>
    </div>  
);
}

export default App;

7.获取DOM元素

通过inputRef.current获取到元素

javascript 复制代码
function App(){
  const inputRef = useRef(null)

  const onChange = ()=>{
    console.log(inputRef.current.value)
  }
  
  return (
    <input 
      type="text" 
      ref={inputRef}
      onChange={onChange}
    />
  )
}

8.两个包:uuid随机id,dayjs

装包->引入->使用

javascript 复制代码
import { v4 as uuidv4 } from 'uuid'
import {dayjs} from 'dayjs'
uuidv4()//生成随机id
dayjs(new Date()).format('MM-DD hh:mm')//格式化时间

9.组件通信

9.1父子通信:父传子+子传父

<Son msg={msg} onsetmsg={setmsg} />

上面代码中,等号左边是在子组件中接受的方式,右边是父组件中的形式。

可以传数据或函数。

javascript 复制代码
import { useState } from "react"

function Son({ msg,onsetmsg }) {
    const sonMsg = 'this is son msg'
    return (
        <div>
            {/* 在子组件中执行父组件传递过来的函数 */}
            <button onClick={() => onsetmsg(sonMsg)}>send</button>
        </div>
    )
}


function App() {
    const [msg,setmsg]=useState('')
    return (
        <div>
            {/* 这里可以通过添加属性的方式向子组件传递数据或函数 */}
            {/* 传递的数据是"父传子" */}
            {/* 传递的函数可以实现在子组件内部"子传父" */}
            <Son msg={msg} onsetmsg={setmsg} />
        </div>
    )
}

export default App

9.2兄弟组件通信:状态提升

javascript 复制代码
// 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

9.3跨层组件通信

步骤:

  1. 使用 createContext方法创建一个上下文对象Ctx

  2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据

  3. 在底层组件(B)中通过 useContext 钩子函数获取消费数据

javascript 复制代码
// 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

10.useEffect()

参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作

参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行

依赖项 副作用功函数的执行时机
没有依赖项 组件初始渲染 + 组件更新时执行
空数组依赖 只在初始渲染时执行一次
添加特定依赖项 组件初始渲染 + 依赖项变化时执行

清除副作用 :在useEffect()的第一个参数(也就是函数)的函数体中,添加一个return语句,return的是一个函数,函数体中执行的语句就是在清除副作用。通常是在组件卸载时执行。起到的作用例如关掉定时器等。

javascript 复制代码
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

11.自定义Hook函数

抽离可复用的逻辑。

javascript 复制代码
// 封装自定义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

12.React Hooks使用规则

  1. 只能在组件中或者其他自定义Hook函数中调用

  2. 只能在组件的顶层调用,不能嵌套在if、for、其它的函数中

13.json-server的使用(接口服务)

json-server是一个快速以.json文件作为数据源模拟接口服务的工具

13.1装包

javascript 复制代码
pnpm i json-server -D

13.2准备json文件

13.3在package.json文件中添加命令

在scripts字段中添加

键值对形式,键可以随便起名(键用于执行命令,如键若为a,则执行命令为npm run a),值分为三部分,其中第二部分是json文件名,第三部分可以自定义端口

javascript 复制代码
"serve":"json-server db.json --port 3004"

注意,如果数据json文件在项目根目录下的server文件夹中,名为data.json,那么配置应该是

javascript 复制代码
"serve":"json-server ./server/data.json --port 3004"

13.4执行命令

javascript 复制代码
npm run serve

14.axios的使用

装包

javascript 复制代码
pnpm i axios

15.redux使用

15.1.装包

两个包

javascript 复制代码
pnpm i @reduxjs/toolkit react-redux

15.2.使用

步骤:

写仓库模块(@reduxjs/toolkit)

组合模块

链接仓库和react

在react中使用仓库(react-redux)

15.2.1写仓库

store/modules/counterStore.js

javascript 复制代码
import { createSlice } from '@reduxjs/toolkit'
const counterStore=createSlice({
    name: 'counter',
    // 初始化数据
    initialState: {
        count:1
    },
    // 修改数据的方法
    reducers: {
        add(state) {
            state.count++
        },
        sub(state) {
            state.count--
        },
        // 传参使用
        addTo10(state, action) {
            // 通过action.payload获取到参数
            state.count=action.payload
        }
    }
})
const { add, sub, addTo10 } = counterStore.actions
const counterReducer = counterStore.reducer
// 按需导出修改数据的方法
export { add, sub, addTo10 }
// 默认导出reducer函数
export default counterReducer
15.2.2组合模块

store/index.js

javascript 复制代码
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counterStore";
export default configureStore({
    reducer: {
        counter:counterReducer
    }
})
15.2.3链接仓库和react

index.js

javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
+import store from './store/index'
+import {Provider} from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
+    <Provider store={store}><App /></Provider>
);
15.2.4使用仓库
javascript 复制代码
import { useSelector, useDispatch } from "react-redux"
import {sub,add} from './store/modules/counterStore'
function App() {
    const { count } = useSelector(state => state.counter)
    const dispatch = useDispatch()
    return (
        <div className="app">
            <button onClick={() => { dispatch(sub()) }}>-</button>
            {count}
            <button onClick={dispatch(add())}>+</button>
            <!--传参使用-->
            <button onClick={()=>dispatch(addTo10())}>加到10</button>
        </div>
    )

}
export default App
15.2.5关于异步请求

store/modules/counterStore.js

javascript 复制代码
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
const counterStore=createSlice({
    name: 'counter',
    // 初始化数据
    initialState: {
        List:[]
    },
    // 修改数据的方法
    reducers: {
        setChannelList (state, action) {
            state.List = action.payload
        }
    }
})
const { setChannelList } = counterStore.actions
// 封装异步请求
const fetchChannelList = () => {
    return async (dispatch) => {
        const res = await axios.get('http://geek.itheima.net/v1_0/channels')
        dispatch(setChannelList(res.data.data.channels))
    }
}
const counterReducer = counterStore.reducer
// 按需导出修改数据的方法
export { fetchChannelList }
// 默认导出reducer函数
export default counterReducer

App.js

java 复制代码
import { useSelector, useDispatch } from "react-redux"
import { fetchChannelList } from './store/modules/counterStore'
function App() {
    const { List } = useSelector(state => state.counter)
    const dispatch = useDispatch()
    return (
        <div className="app">
      <button onClick={() => dispatch(fetchChannelList())}>异步请求更新仓库</button>
      {List.map((item) => {
        return <li>{item.name}</li>
      })}
        </div>
    )

}
export default App

16.reactRouter路由使用

执行命令:

javascript 复制代码
pnpm i react-router-dom

16.1配置

src/router/index.js

javascript 复制代码
import Login from '../page/login/index'
import Article from '../page/article/index'
import { createBrowserRouter } from 'react-router-dom'

const router = createBrowserRouter([
  {
    path:'/login',
    element: <Login></Login>
  },
  {
    path:'/article',
    element: <Article></Article>
  }
])
export default router

src/index.js

javascript 复制代码
import React from 'react';
+import ReactDOM from 'react-dom/client';
import App from './App';
+import router from './router/index'
+import { RouterProvider } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
+    // 这里本来是<App />,改成下行
+    <RouterProvider router={router}><App /></RouterProvider>
  </React.StrictMode>
);

16.2路由跳转

声明式导航:

javascript 复制代码
import { Link } from "react-router-dom"

const Login = () => {
    return (<div>
        登录页
        <Link to='/article'>跳转至article页面</Link>
    </div>)
}
export default Login

编程式导航:

javascript 复制代码
import { useNavigate } from "react-router-dom"

const Login = () => {
    const navigate=useNavigate()
    return (<div>
        登录页
        <button onClick={()=>navigate('/article')}>跳转至article页面</button>
    </div>)
}
export default Login

16.3传参

searchParams传参(类似于path传参):

javascript 复制代码
import { useSearchParams } from "react-router-dom"

const Article = () => {
    // 获取path传参
    const [searchParams] = useSearchParams()
    const name=searchParams.get('name')
    const job = searchParams.get('job')
    return <div>文章页
        <div>searchParams:{name}-{job}</div>
    </div>
}
export default Article

params传参(类似于动态路由传参):

javascript 复制代码
import { useParams } from "react-router-dom"

const Article = () => {
    // 获取动态路由传参
    const params = useParams()
    const hobby=params.hobby
    const age=params.age
    return <div>文章页
        <div>searchParams:{name}-{job}</div>
        <div>params:{hobby}-{age}</div>
    </div>
}
export default Article

16.4嵌套路由配置

router/index.js

javascript 复制代码
import Login from '../page/login/index'
import { createBrowserRouter } from 'react-router-dom'
import Login01 from '../page/login01/index'
import Login02 from '../page/login02/index'

const router = createBrowserRouter([
  {
    path:'/login',
    element: <Login></Login>,
    children: [
      {
        // 注意这里的path前面不能加/,否则访问login/login01时报404错
        path: 'login01',
        element:<Login01></Login01>
      },
      {
        path: 'login02',
        element:<Login02></Login02>
      },
    ]
  }
])
export default router

login/index.js

javascript 复制代码
import { Link, Outlet } from "react-router-dom"

const Login = () => {
    return (<div>
        登录页
        <Link to="/login/login01">跳转至二级路由login01</Link>
        <Link to="/login/login02">跳转至二级路由login02</Link>
        // 二级路由出口
        <Outlet />
    </div>)
}
export default Login

展示:

一级路由login:

二级路由login/login02:

默认和二级路由和404:

以上页面在展示的时候,当访问login这个一级路由时只会展示到一级,如果在配置时添加默认二级路由login01,则访问login时会跳转至login/login01

代码:

javascript 复制代码
import Login from '../page/login/index'
import { createBrowserRouter } from 'react-router-dom'
import Login01 from '../page/login01/index'
import Login02 from '../page/login02/index'
import NotFound from '../page/notFound/index'

const router = createBrowserRouter([
  {
    path:'/login',
    element: <Login></Login>,
    children: [
      {
        // 默认二级路由
        index: true,
        element:<Login01></Login01>
      },
      {
        path: 'login02',
        element:<Login02></Login02>
      },
    ]
  },

  {
    path: '*',
    element:<NotFound></NotFound>
  }
])
export default router

展示:

16.5两种路由模式

history和hash模式

区别:有无#

创建区别:history:createBrowserRouter:没有#

hash:createHashRouter:有#

17.路径配置

17.1路径解析配置(webpack)

目的:解析@为src

步骤:

安装插件craco
复制代码
pnpm i -D @craco/craco
项目根目录下创建配置文件 craco.config.js
javascript 复制代码
const path = require('path')

module.exports = {
  webpack: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
}
包文件中配置启动和打包命令

package.json中scripts:

javascript 复制代码
"scripts": {
    "start": "craco start",
    "build": "craco build"
 },

17.2路径现象配置(vscode)

目的:VsCode 在输入 @/ 时,自动联想出来对应的 src/下的子级目录

步骤:

根目录下新增配置文件 - jsconfig.json
javascript 复制代码
{
  "compilerOptions":{
    "baseUrl":"./",
    "paths":{
      "@/*":[
        "src/*"
      ]
    }
  }
}

18.数据mock

用json-server

19.antD-mobile主题定制

装包

复制代码
pnpm add antd-mobile

全局定制:

在src/theme.css文件中写样式,在src/index.js中导入

css 复制代码
:root:root {
  --adm-color-primary: #a062d4;
}

局部定制:

html 复制代码
<div className='purple-theme'>
  <Button color='primary'>Purple</Button>
</div>
css 复制代码
.purple-theme {
  --adm-color-primary: #a062d4;
}

四、杂记

1.关于&&

可以根据前面的条件是否成立而决定是否渲染后面的样式。

考研终止。。。