React

搭建开发环境

create-react-app(创建项目):npx create-react-app xxx(xxx 项目名)

解读项目目录

app.js

// 项目的根组件
// app被引入到index.js 被index.js里的核心代码 渲染到 public里的index.html里的root div上去
function App() {
  return (
    <div className="App">
     this is APP
    </div>
  );
}

export default App;

index.js

// 整个项目的入口
// 下面两个是react两个必要的核心包
import React from 'react';
import ReactDOM from 'react-dom/client';
// 导入项目的根组件
import App from './App';

// 把根组件渲染到id为root的dom节点上(在public下的index里)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);


JSX

jsx:js+xml(html)

本质

jsx不是标准的js语法,浏览器不认识,需要通过解析工具(babel)做解析之后才能在浏览器中允许

想深入了解jsx (Babel · Babel (babeljs.io))多看看区别呗

jsx中使用js表达式

{}

注意:仅支持表达式哦!!if语句、switch语句、变量声明属于语句 不是表达式 不允许出现在{}中


练习

实现列表渲染
const a = [
  {name:'ju',age:12},
  {name:'j',age:1},
  {name:'u',age:2},
]

 {/* 渲染a li标签 */}
     <ul>
      {a.map(item =><li key={item.id}>{item.name}</li>)}
     </ul>

key的作用:提升列表更新性能

条件渲染

在react中,可以通过&&、三元表达式实现基础的条件渲染

const loading = false

可以根据函数调用的方式实现复杂的条件渲染

React基础的事件绑定

onClick={}

组件的基础使用

useState

他是一个ReactHook函数,它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果(双向绑定我认为的哦)

// 项目的根组件
// app被引入到index.js 被index.js里的核心代码 渲染到 public里的index.html里的root div上去
import { useState } from "react";

function App() {
  {/* 使用useState实现一个计数器按钮 */}
const [count,setCount] =useState(0)
// 点击按钮事件回调
const handleClick = ()=>{
  setCount(count+1)
}
  return (
    <div> 
    {/* 这是计数器的按钮 */}
    <button onClick={handleClick}>计数器变化{count}</button>

    </div>
  );
}

export default App;

 修改状态的规则

在react中,状态被认为是只读的,我们应该替换他 而不是去修改,直接修改没啥用,不会引起视图层变化

修改对象状态

对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改

// 项目的根组件
// app被引入到index.js 被index.js里的核心代码 渲染到 public里的index.html里的root div上去
import { useState } from "react";

function App() {
  const [form,setForm] = useState({name:'ju',age:12})

// 点击按钮事件回调
const handleClick = ()=>{
  setForm({
    ...form,
    name:'luckly',
    age:18
  })
  console.log(form)
}
  return (
    <div> 
    {/* 这是计数器的按钮 */}
    <button onClick={handleClick}>{form.name}{form.age}</button>

    </div>
  );
}

export default App;

这就是新替换旧(覆盖)

基础样式处理

  1. 行内样式:style={{color:'red'}} 或者{类名}
  2. class类名控制:className='foo'

小工具:classnames

他是一个简单的js库,可以非常非常方便的通过条件动态控制class类名的显示(GitHub - JedWatson/classnames: A simple javascript utility for conditionally joining classNames together

受控表单绑定

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

获取Dom

在react组件中获取、操作dom,需要使用useRef钩子函数。

  • 使用useRef创建ref对象,并和jsx绑定
  • 在dom可用时,通过inputRef.current拿到dom对象
// 项目的根组件
// app被引入到index.js 被index.js里的核心代码 渲染到 public里的index.html里的root div上去
import { useRef } from "react";

function App() {
  // null:他是个初始值,代表在没拿到dom之前 他是个null
  const inputDom = useRef(null)
  const click =()=>{
    console.log(inputDom.current)
    console.dir(inputDom.current)

  }
  return (
    <div> 
      {/* ref={inputDom}:和input进行绑定 */}
      <input type="text" ref={inputDom}/>
    
      <button onClick={click}>点击获取input的Dom</button>
    
    </div>
  );
}

export default App;

组件之间通信

父传子

实现步骤:

  • 父组件传递数据--在子组件身上绑定属性
  • 子组件接收数据--子组件通过props参数接收数据

// 父组件传递数据--在子组件身上绑定属性
// 子组件接收数据--子组件通过props参数接收数据
// 子组件
function Son(props){
  console.log(props)
return <div>Son---{props.name}</div>
}

function App() {
  const name = 'fatherData'
  return (
    <div> 
      <Son name={name}></Son>
    </div>
  );
}

export default App;

props说明

  • props可传递任意的数据(数字、字符串、布尔值、数组、对象、函数、jsx)
  • props是只读对象(子组件只能读,不能修改,父组件的数据只能父组件修改!!跟vue一样)

好神奇呀!!!!!

特殊的prop——children

标签里面写的东西也是可以识别的。

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

子传父

核心:在子组件里面调用父组件的函数并传递参数


import { useState } from "react"

// 通过解构赋值 接收父组件传递过来的函数 onGetSonData
function Son({onGetSonData}){
const name='sonData'
// 把子组件中的数据name当成实参传递过去 ,相当于去执行了父组件中的方法handlegetdata 把实参传递过去
return <div>son!!!-<button onClick={()=>onGetSonData(name)}>点击发送</button></div>
}

function App() {
  const [fname,setFname] = useState('11')
  // 在父组件里搞一个小函数
  const handlegetdata = (name)=>{
   console.log(name)
   setFname(name)
  }
  return (
    <div> 
       父亲!!——{fname}
      {/* 把父组件里的小函数绑定过来传递给子 */}
      <Son onGetSonData={handlegetdata} />
     
    </div>
  );
}

export default App;

使用状态提升实现兄弟组件通信

通过父组件进行兄弟之间的数据传递

   步骤:

  • A组件先通过子传父的方式把数据传给父组件APP
  • App拿到数据后通过父传子的方式再传递给B组件

活脱脱一个二房东!!!!

import { useState } from "react"

// A组件先通过子传父的方式把数据传给父组件APP
// App拿到数据后通过父传子的方式再传递给B组件
function SonA({onGetMessage}){
const aMess = "this is dataA"
return <div>A
  <button onClick={()=>{onGetMessage(aMess)}}>发送A数据到App</button>
</div>
}
function SonB(props){
console.log(props.aMess)
  return <div>B--:{props.aMess}</div>
  }
  
function App() {
  const [aMess,setMess] = useState('')
  // 拿A的数据
  const aMessage =(aMess)=>{
    console.log(aMess)
    setMess(aMess)
  }
  return (
    <div> 
      父中A的数据---:{aMess}
      <SonA onGetMessage={aMessage}></SonA>
      <SonB aMess={aMess}></SonB>
    </div>
  );
}

export default App;

跨级传递 (爷孙)

步骤:

  • 使用createContext方法创建一个上下文对象Ctx
  • 在顶层组件App中通过Ctx.provider组件提供数据
  • 在底层组件B通过useContext钩子函数获取消费数据
import { createContext, useContext} from "react"

// 使用createContext方法创建一个上下文对象Ctx
const MsgContext = createContext()


// 在底层组件B通过useContext钩子函数获取消费数据
function A(){
  return <div>A<B></B></div>
}
function B(){
  const msg = useContext(MsgContext)
  return <div>B {msg}</div>
}
function App() {
  const msg = '传递的数据'
 
  return (
    <div> 
      {/* 在顶层组件App中通过Ctx.provider组件提供数据 */}
      {/* value用来提供数据 要传递哪个数据就把哪个放过去 */}
      <MsgContext.Provider value={msg}>父
        <A></A>

      </MsgContext.Provider>
    
      
    </div>
  );
}

export default App;

useEffect

他是一个react hook函数,用于在react组件中创建不是由事件引起而是渲染本身引起的操作,比如 发送ajax请求、更改Dom等。

 // 第二个参数是依赖项数组
useEffect(()=>{
  // 想做什么操作就放进来
  
},[])


第二个参数 的集中不同的情况

  • 没有依赖项:代表组件初始化渲染+组件更新时执行。
  • [ ] 空数组依赖:只在初始化渲染时执行一次。
  • 添加特定的依赖项:代表 组件初始化渲染+特定依赖性变化时执行(有点监听的感觉)

清除副作用

它的意思是 比如:在useEffect中开启一个定时器,想在组件卸载时把定时器清掉,这个过程就叫清理副作用。

import { useEffect, useState } from "react";

function A(){
 // 第二个参数是依赖性数组
useEffect(()=>{
  // 实现副作用
  console.log("执行了")
  return ()=>{
    // 清除副作用
    console.log("清除了")
  }
},[])
  return <div>A</div>
}
function App() {

const [show,setMsg] = useState(true)

  return (
    <div> 
    <button onClick={()=>setMsg(false)}>点击变化{show}</button>
    {show && <A></A>}
    </div>
  );
}

export default App;

点击之前:

点击之后:

  

自定义Hook实现

他的意思是 以use打头的函数,通过自定义hook函数可以用来实现逻辑的封装和复用

步骤:

  • 声明一个以use打头的函数
  • 在函数体内封装客服用的逻辑(只要是可复用用的逻辑)
  • 把组件中用到的状态或者回调return出去(以对象或者数组)
  • 在哪个组件中要用到这个逻辑,就执行这个函数,结构出来状态和回调进行使用

react hooks使用规则:

  • 只能在组件中或者其他自定义hook函数中调用
  • 只能在组件的顶层调用,不能嵌套在if、for、其他函数中

Redux

他是react最常用的集中管理工具,类似于vue的pinia(vuex),可以独立于框架运行(就是虽然是react最常用,但是他和react不绑定,可以自己跑)

使用步骤:

  • 定义一个reducer函数(根据当前想做的事修改返回一个新的状态)

    • state :管理的数据初识状态

    • action:对象 type标记当前想要做什么样的修改(type === 增删改查这种操作)

  •  使用createStore方法传入reducer函数 生成一个store实例对象

  • 使用store实例 subscribe方法 订阅数据变化(数据一旦变化,可以得到通知)

  • 使用store实例的dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)

  • 使用store实例的getState方法获取最新的状态数据更新到视图里

Redux的核心流程

分为三个核心概念:state、action、reducer

  • state:一个对象  存放着我们管理的数据状态(数据库)
  • action:一个对象   用来描述你想怎么去修改数据(指挥者)
  • reducer:一个函数  根据action的描述生成一个新的state(真正的逻辑)

搭建环境

他得安装两个插件:redux Toolkit(简化书写  内有很多工具的工具包) 和 react-redux(用来连接redux和react  用来获取状态、更新状态)

 npm i @reduxjs/toolkit react-redux

React与Redux实现

创建counterStore

import { createSlice } from "@reduxjs/toolkit";
// createSlice 用它来创建store
const counterStore = createSlice({
    name:'counter',
    // 初始化状态
    initialState:{
        count:0
    },
    // 编写修改数据的方法  支持直接修改
    reducers:{
        inscrement(state){
            state.count++
        },
        decrement(state){
            state.count--
        }
    }
})

// 解构出来actionCreater函数
const {inscrement,decrement} = counterStore.actions
// 获取reducer
const reducer = counterStore.reducer

// 以按需导出的方式导出actionCreater
export {inscrement,decrement}
// 以默认导出方式导出reducer
export default reducer

在入口文件index.js文件中 组合导出

// 把子模块导入进来 产出一个子模块   组合 =》导出
// 导入子模块reducer
import counteReducer from "./modules/counterStore";
import { configureStore } from "@reduxjs/toolkit";
const store = configureStore({
    reducer:{
        counter:counteReducer
    }
})

export default store

为react注入store

React如何使用store中的数据

需要用到一个钩子函数useSelector,它的作用是把store中的数据映射到组件中:

react组件修改store中的数据

需要借助另一个hook函数 useDispatch ,它的作用是生成提交action对象的dispatch函数

// 项目的根组件
// app被引入到index.js 被index.js里的核心代码 渲染到 public里的index.html里的root div上去

import { useDispatch,useSelector } from "react-redux";
import { inscrement,decrement } from "./store/modules/counterStore";


function App() {
  const {count} = useSelector(state => state.counter)
  const dispatch = useDispatch()
  return (
    <div> 
      
      <button type="button" class="btn btn-default" onClick={()=>dispatch(decrement())}>-</button>
      
   {count}
   
   <button type="button" class="btn btn-default" onClick={()=>dispatch(inscrement() )}>+</button>
   
    </div>
  );
}

export default App;

如何要提交到action对象哇??? 执行store模块中导出的actionCreater方法

提交action传参实现需求

在reducer的同步修改方法中添加action对象参数,在调用actionCreater 的时候传递参数,参数会被传递到action对象的payload属性上!!!!

redux与react异步状态操作

异步操作步骤:

  • 创建store的写法保持不变,配置好同步修改状态的写法
  • 单独封装一个函数,在函数内部return一个新函数,在新函数中
    • 封装异步请求获取数据
    • 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交
  • 组建中dispatch的写法保持不变

路由

前端路由:一个path对应一个组件,当浏览器访问一个path时,path对应的组件会在页面中进行渲染

安装路由:npm i react-router-dom

使用:


import { createBrowserRouter,RouterProvider } from 'react-router-dom';
import "./index.css"
// 创建router实例化对象并且配置路由对应关系
const router = createBrowserRouter([
  {
    path:"/login",
    element:<div>这是登录 </div>
  },
   {
    path:"/article",
    element:<div>这是文章页</div>
  }
])
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

抽象路由模块

文章页:
const Article=()=>{
    return <div>我是文章页面</div>
}
export default Article

登录页:
const Login=()=>{
    return <div>我是登录页面</div>
}
export default Login

路由index.js:

import {createBrowserRouter} from "react-router-dom"
import Login from '../pages/Login'
import Article from "../pages/Article"
const router = createBrowserRouter([
   {
        path:"/login",
        Component:Login
    },
    {
        path:"/article",
        Component:Article
    }
])
export default router

路由导航

路由系统中多个路由之间 进行路由跳转,并且在跳转的同时需要传递参数进行通信。

声明式导航

<link to="/login">登录</link>:通过给组件的to属性指定要跳转到路由path,组件会被渲染为路由器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可  

编程式导航

编程式导航是通过useNavigate钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转,通过navigate方法传入地址path实现跳转

import { useNavigate } from "react-router-dom"

const Login=()=>{
    const navigate = useNavigate()
    return (
        <div>我是登录页面
            <button onClick={()=>{navigate('/article')}}>点击跳转文章页面</button>
        </div>
    )
}
export default Login

导航跳转传参

searchParams传参

传参数

接收参数

params传参

传参数

接收参数

别忘了占位符: ,这样param才能. 到东西

嵌套路由

在一级路由中又嵌套了其他路由 这种关系就叫做嵌套路由,嵌套至一级路由内的路由又称作二级路由。

实现步骤:

  • 使用children属性配置路由嵌套关系
  • 使用<Outlet/>组件配置二级路由渲染位置

在切换路由的时候会在<outlet>的地方进行渲染(有点占位置那味)

默认二级路由

当访问的是一级路由时,默认的二级路由组件可以得到渲染,只需要在二级路由的位置去掉path,设置index属性为true就行。

此行为的需求:进来这个页面的时候不需要点击去test,test就可以直接被渲染出来。

404路由

以*作为路由path 配置路由

路由的两种模式

history:url/login(没有# 好看一点)需要后端支持,底层原理(history+pushState事件)

        后端需要配置nginx转发(不然路由找不到)

hash:url/#/login(有#)不需要后端支持 ,底层原理(监听hashChange事件)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值