react入门之react全家桶

react全家桶

Redux

redux: JS 应用的状态容器,提供可预测的状态管理

react入门之react全家桶_第1张图片

使用 redux 的步骤:

  • 安装:pnpm i redux -S
  • 编写store.js中的变量和方法
  • index.js 中订阅 store.js
  • 在其他文件中使用 store
    • 引入 store
    • 使用 store 的一些方法,比如dispatchgetState

案例:

store.jsx

import { createStore } from 'redux'
// 创建 reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'PLUS':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
// 创建 store
const store = createStore(counter)

// 抛出
export default store

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import store from './store'

ReactDOM.createRoot(document.getElementById('root')).render(
    
        
    
)

// 订阅 store
store.subscribe(() => {
    console.log(store.getState())
})

ReduxTest.jsx

import React, { Component } from 'react'
import store from '../store'
import { Button } from 'antd'

export default class ReduxTest extends Component {
    constructor(props) {
        super(props)
        this.state = {
            num: 0,
        }
    }
    increase = () => {
        store.dispatch({
            type: 'PLUS',
        })
        this.setState({
            num: store.getState(),
        })
    }
    render() {
        return (
            

{this.state.num}

) } }

结果:

react入门之react全家桶_第2张图片

react-redux

更加优雅的使用 redux

  • 事实上就是使用了高阶组件
  • 对方法进行封装

使用步骤:

  • 安装 pnpm add react-redux -S
  • 编写store.js中的变量和方法
  • 使用 Provider
  • react-redux 不需要手动订阅
  • 在其他文件中使用 store
    • 引入 storeconnect
    • 使用高阶组件 connect

案例(在redux 基础上进行更改):

store.jsx

import { createStore } from 'redux'
// 创建 reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'PLUS':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
// 创建 store
const store = createStore(counter)

// 抛出
export default store

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import store from './store'
import { Provider } from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    
        
    
)

ReduxTest.jsx

import React, { Component } from 'react'
import store from '../store'
import { Button } from 'antd'
import { connect } from 'react-redux'

const mapStateToProps = (state) => {
    return {
        num: state,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        increase: () => {
            dispatch({
                type: 'PLUS',
            })
        },
    }
}
class ReduxTest extends Component {
    render() {
        return (
            

{this.props.num}

) } } export default connect(mapStateToProps, mapDispatchToProps)(ReduxTest)

结果同上。

redux 中间件

它在 dispatch action 的时候和 action 到达 reducer 那一刻之间提供了三方的逻辑拓展点.

比较成熟的中间件:

  • redux-logger -> 打印
  • redux-thunk -> 处理异步操作

首先安装这两个中间件:

pnpm add redux-logger redux-thunk -S

thunk 和 logger 的使用方法:

import { createStore, applyMiddleware } from 'redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'

// 创建 reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'PLUS':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
// 创建 store
const store = createStore(counter, applyMiddleware(logger, thunk))

// 抛出
export default store
import React, { Component } from 'react'
import store from '../store'
import { Button } from 'antd'
import { connect } from 'react-redux'

const mapStateToProps = (state) => {
    return {
        num: state,
    }
}

// 模拟异步操作
const Increase = () => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch({
                type: 'PLUS',
            })
        }, 1000)
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        increase: () => {
            dispatch({
                type: 'PLUS',
            })
        },
        asyncIncrease: () => {
            dispatch(Increase())
        },
    }
}
@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends Component {
    render() {
        return (
            

{this.props.num}

) } } export default ReduxTest

效果:

react入门之react全家桶_第3张图片

store的模块化

模块化方案:

react入门之react全家桶_第4张图片

具体示例:

react入门之react全家桶_第5张图片

index.jsx

import { createStore, applyMiddleware, combineReducers } from 'redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
import { counter } from './reducer/counter'

// 创建 store
const store = createStore(
    combineReducers({
        counter,
    }),
    applyMiddleware(logger, thunk)
)

// 抛出
export default store

constants.js

export const PLUS = 'plus'
export const MINUS = 'minus'

action counter.js

import store from '..'

const mapStateToProps = (state) => {
    return {
        num: state.counter,
    }
}

// 模拟异步操作
const Increase = () => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch({
                type: 'PLUS',
            })
        }, 1000)
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        increase: () => {
            dispatch({
                type: 'PLUS',
            })
        },
        asyncIncrease: () => {
            dispatch(Increase())
        },
    }
}

export { mapDispatchToProps, mapStateToProps }

reducer counter.js

import { PLUS, MINUS } from '../constants'

// 创建 reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'PLUS':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}

export { counter }

:::tip

注意,这里有着很多的引入,详细请查看 资料

:::

Mobx状态管理

MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。

react入门之react全家桶_第6张图片

react 和 mbox 是一对强力的组合(推荐在小型项目中使用)

  • react 是一个消费者,将应用状态state渲染成组件树对其进行渲染
  • mobx 是一个提供者,用于存储和更新状态 state

安装:

pnpm add mobx mobx-react -S

案例

mobx.jsx

import { observable, action } from 'mobx'

// 创建观察者
const appState = observable({
    num: 100,
})

// 创建 action
appState.plus2 = action(() => {
    appState.num = appState.num * 2
})

export default appState

MobxTest.jsx

import React, { Component } from 'react'
import { observer } from 'mobx-react'
import { Button } from 'antd'

@observer
class MobxTest extends Component {
    render() {
        return (
            
{this.props.appState.num}
) } } export default MobxTest

main.jsx

(以下代码中还存有 redux 的痕迹,选择和 mobx 相关的学习即可)

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import store from './store'
import appState from './store/mobx'
import MobxTest from './components/MobxTest'
import { Provider } from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    
        
        
)

结果:

react入门之react全家桶_第7张图片

案例(装饰器版)

装饰器是一种比较不稳定的写法,可能因为版本的问题不支持:

版本6之前的Mobx,不需要在构造函数中调用makeObservable(this)。在版本6中,为了让装饰器的实现更简单以及保证装饰器的兼容性,必须在构造函数中调用makeObservable(this)。Mobx可以根据 makeObservable第二个参数提供的装饰器信息,将实例设置为observable。

装饰器版本:

import { makeObservable, observable, action } from 'mobx'

// 创建观察者
// const appState = observable({
//     num: 100,
// })

// 创建 action
// appState.plus2 = action(() => {
//     appState.num = appState.num * 2
// })

class AppState {
    constructor() {
        makeObservable(this)
    }
    @observable num = 100
    @action
    plus2() {
        this.num = this.num * 2
    }
}

export default new AppState()

效果同上。

对比 redux 和 mobx

  • 学习难度
  • 工作量
  • 内存开销
  • 状态管理的集中性
  • 样板代码的必要性
  • 结论:
    • 小的项目可以使用 mobx
    • 最终回归 redux

react-router基本使用

:::warning

注意不同版本之间的问题

:::

安装:

pnpm add --save react-router-dom

组件名 作用 说明
一组路由 包裹所有
基础路由 Router是可以嵌套的
导航组件 在实际页面中跳转使用
自适应渲染组件 根据实际路由url自动选择组件
hooks名 作用 说明
useParams 返回当前参数 根据路径读取参数
useNavigate 返回当前路由 代替原有V5中的 useHistory
useOutlet 返回根据路由生成的element
useLocation 返回当前的location 对象
useRoutes 同Routers组件一样,只不过是在js中使用
useSearchParams 用来匹配URL中?后面的搜索参数

使用

import './App.css'
import Home from './components/Home'
import Learning from './components/Learning'
import Reading from './components/Reading'
import NotFound from './components/NotFound'
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
import React, { Component } from 'react'

class App extends Component {
    render() {
        return (
            
                
  • 首页
  • 读书笔记
  • 学习笔记
{/* 就在这里渲染,写法比较纯粹,像摩洛哥的足球一样 */} }> }> }> }>
) } } export default App

效果:

react入门之react全家桶_第8张图片

二级路由

嵌套路由、动态路由

App.jsx

class App extends Component {
    render() {
        return (
            
                
  • 首页
  • 读书笔记
  • 学习笔记
{/* 就在这里渲染 */} }> }> } > } > }> }>
) } }

BookDetail.jsx

import React from 'react'
import { useParams } from 'react-router-dom'

export default function BookDetail() {
    const params = useParams()
    console.log(params)
    return 
{params.bookName}
}

Reading.jsx

import React, { Component } from 'react'
import {
    useParams,
    BrowserRouter,
    Link,
    Route,
    Routes,
    Outlet,
} from 'react-router-dom'

class Reading extends Component {
    render() {
        return (
            
reading
  • 读书笔记intro
  • 读书笔记es6
  • ) } } export default Reading

    结果:

    react入门之react全家桶_第9张图片

    编程式导航

    使用 useNavigate hook:

    import { Button } from 'antd/es/radio'
    import React from 'react'
    import { useNavigate } from 'react-router-dom/dist'
    
    export default function Intro() {
        const nav = useNavigate()
        const back = () => {
            nav('/')
        }
        return (
            
    Intro
    ) }

    效果:点击就会返回主页

    react入门之react全家桶_第10张图片

    react入门之react全家桶_第11张图片

    路由地址与路由参数

    使用 useParams 获取路由参数:

    使用useLocation获取路由地址:

    import React from 'react'
    import { useParams, useLocation } from 'react-router-dom'
    
    export default function BookDetail() {
        const params = useParams()
        const location = useLocation()
        console.log(params, location)
        return 
    {params.bookName}
    }

    react入门之react全家桶_第12张图片

    集中式路由管理

    使用useRoutes进行路由集中管理:

    image-20221220174830938

    index.jsx

    import { useRoutes } from 'react-router-dom/dist'
    import BookDetail from '../components/BookDetail'
    import Home from '../components/Home'
    import Intro from '../components/Intro'
    import Learning from '../components/Learning'
    import Reading from '../components/Reading'
    
    export const element = [
        {
            path: '/',
            element: ,
        },
        {
            path: '/reading',
            element: ,
            children: [
                {
                    path: 'detail/:bookName',
                    element: ,
                },
                {
                    path: 'intro',
                    element: ,
                },
            ],
        },
        {
            path: '/learning',
            element: ,
        },
    ]
    

    在 App.jsx 中使用 hook:

    function App() {
        const AllRoutes = useRoutes(element)
        return AllRoutes
    }
    
    export default App
    

    以上即可实现路由集中式管理。

    页面懒加载

    整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。

    在原来的情况下,如果我们直接通过 import 引入组件会造成下面这种情况,引入的时候代码就会执行了:

    新建一个 TestLazy 用来测试,我们引入但是不使用他:

    import React from 'react'
    console.log('testlazy')
    export default function TestLazy() {
        return 
    TestLazy
    }

    引入方式:

    import TestLazy from '../components/TestLazy'
    

    结果:

    react入门之react全家桶_第13张图片

    换一种引入方式:

    const TestLazy = lazy(() => import('../components/TestLazy'))
    

    结果:

    react入门之react全家桶_第14张图片

    由此可见使用懒加载可以帮助我们在引入组件的时候如果当前组件在初次渲染的时候不用执行,那么他就不会被渲染,从而节省资源。

    全部实现代码:

    TestLazy.jsx

    import React from 'react'
    console.log('testlazy')
    export default function TestLazy() {
        return 
    TestLazy
    }

    router.jsx

    import { lazy } from 'react'
    import { useRoutes } from 'react-router-dom/dist'
    import BookDetail from '../components/BookDetail'
    // import Home from '../components/Home'
    // import Intro from '../components/Intro'
    // import Learning from '../components/Learning'
    // import Reading from '../components/Reading'
    // import TestLazy from '../components/TestLazy'
    
    const Home = lazy(() => import('../components/Home'))
    const Intro = lazy(() => import('../components/Intro'))
    const Learning = lazy(() => import('../components/Learning'))
    const Reading = lazy(() => import('../components/Reading'))
    const TestLazy = lazy(() => import('../components/TestLazy'))
    
    export const element = [
        {
            path: '/',
            element: ,
        },
        {
            path: '/reading',
            element: ,
            children: [
                {
                    path: 'detail/:bookName',
                    element: ,
                },
                {
                    path: 'intro',
                    element: ,
                },
            ],
        },
        {
            path: '/learning',
            element: ,
        },
    ]
    

    App.jsx

    function App() {
        const AllRoutes = useRoutes(element)
        return loading...
    }>{AllRoutes} // return AllRoutes } export default App

    解决闪屏

    • 将大的背景直接渲染,不用进行懒加载

    react入门之react全家桶_第15张图片

    router传参

    useSearchParams()

    http://localhost:5173/learning?name="lijiajun"
    
    const [params] = useSearchParams()
    params.get('name')  // expected output "lijiajun"
    

    useNavigate()

    设置参数:

    import { useNavigate } from 'react-router-dom/dist'
    
    const nav = useNavigate()
    const back = () => {
        nav('/learning',{state:{"name":"lijiajun"}})
    }
    

    获取参数:

    import { useLocation } from 'react-router-dom';
    let location = useLocation();
    const { object } = location.state;
    

    效果:

    react入门之react全家桶_第16张图片

    实现路由守卫

    react 本身没有路由守卫,需要我们自己来实现。

    实现思路:

    • 先判断一个用户的权限(true or false)
    • 根据权限来决定是否跳转到对应页面
    • 所以需要一个组件
      • 参数1:是否有权限
      • 参数2:跳转的组件

    具体的代码实现:

    const Access = lazy(() => import('../components/Access'))
    
    export const element = [
        {
            path: '/',
            element: ,
        },
        {
            path: '/reading',
            element: ,
            children: [
                {
                    path: 'detail/:bookName',
                    element: ,
                },
                {
                    path: 'intro',
                    // 在这里进行权限的判定
                    element: } />,
                },
            ],
        },
        {
            path: '/learning',
            element: ,
        },
        {
            path: '/testlazy',
            element: ,
        },
    ]
    

    Access.jsx

    import React from 'react'
    
    export default function Access({ isAccess, to }) {
        if (isAccess === false) {
            return <>您没有权限
        } else {
            return <>{to}
        }
    }
    

    App.jsx

    function App() {
        // 实现路由的监听
        const location = useLocation()
        useEffect(() => {
            console.log(location.pathname, 'enter')
            return () => console.log(location.pathname, 'leave')
        }, [location.pathname])
    
        const AllRoutes = useRoutes(element)
        return loading...
    }>{AllRoutes} } export default App

    效果:

    isAccess 为 false 和 true 的两种情况

    react入门之react全家桶_第17张图片

    react入门之react全家桶_第18张图片

    Umijs蚂蚁金服框架

    react入门之react全家桶_第19张图片

    Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。

    Umi 有很多非常有意思的特性,比如。

    1、企业级,在安全性、稳定性、最佳实践、约束能力方面会考虑更多
    2、插件化,啥都能改,Umi 本身也是由插件构成
    3、MFSU,比 Vite 还快的 Webpack 打包方案
    4、基于 React Router 6 的完备路由
    5、默认最快的请求
    6、SSR & SSG
    7、稳定白盒性能好的 ESLint 和 Jest
    8、React 18 的框架级接入
    9、Monorepo 最佳实践

    react入门之react全家桶_第20张图片

    技术收敛对团队而言尤其重要,他包含两层含义,1)技术栈收敛 2)依赖收敛。技术栈收敛指社区那么多技术栈,每个技术方向都有非常多选择,比如数据流应该就不下 100 种,开发者应该如何选择;收敛了技术栈之后还需要收敛依赖,团队中,开发者的项目不应该有很多细碎的依赖,每一个依赖都附带着升级成本。

    我们希望开发者依赖 Umi 之后就无需关心 babel、webpack、postcss、react、react-router 等依赖,而依赖 @umijs/max 之后无需再关心开发中台项目的依赖和技术栈。

    安装

    pnpm dlx create-umi@latest

    可以使用的参数:

    option description
    --no-git 创建项目,但不初始化 Git
    --no-install 创建项目,但不自动安装依赖

    react入门之react全家桶_第21张图片

    启动Prettier

    如果需要用 prettier 做项目代码的自动格式化,执行 pnpm umi g

    目录结构

    react入门之react全家桶_第22张图片

    运行成功

    ant-design-pro版本:

    react入门之react全家桶_第23张图片

    simple版本:

    react入门之react全家桶_第24张图片

    你可能感兴趣的:(react,react.js,前端,前端框架)