初学react17到项目实战(附开源代码) 您所在的位置:网站首页 react经验 初学react17到项目实战(附开源代码)

初学react17到项目实战(附开源代码)

2023-08-18 06:02| 来源: 网络整理| 查看: 265

项目背景 学习契机

说起react,一直拖到现在也不是说难学,可能是惰性使然,觉得用vue躺着挺舒服就懒得捣腾,每次想咸鱼翻身,但是看了看JSX语法,也就只是翻了个身继续躺着。

受各位卷王们的影响,我也焦虑起来了,所以定制了一系列的学习计划,争取今年能不再做API工程师,不然就要回去继承家产了。

这个react项目也是用的我们后台管理系统为模板写的,所以如果看过我之前的 VUE3项目,可能会觉得 这个react项目怎么V里V气的

附上github开源项目地址: vite-react

学习渠道

我个人不喜欢看视频,觉得太慢容易走神,看文档反而更能引人入胜,可能是对学习没耐心。所以直接从react官方文档入手。

在官方文档中发现看到官方对初学者推荐:

如果你觉得 React 官方文档节奏太快,不太适应,可以先去看看这篇 Tania Rascia 的 React 概览。它以新手友好的方式详细介绍了最重要的 React 概念,看完这篇概览,再回来试试看官方文档吧!

于是我就对号入座先去看了官方推荐的这篇博客,确实读解起来比较轻松,虽然我反复看了三遍,接着看了这位作者写的用 Hooks 在 React 中构建一个 CRUD 应用程序,接着就是回头去读react官方文档,期间有难以理解的地方就结合百度一步步解析,比如context, 比如hooks,这整个阅读理解过程用了一周时间,当然是这个一周是工作之余。

在读完这些后最大的感受就是,之前让我看不下去的JSX结构,现在略微清爽了一些,没那么抗拒了(在后面写过一遍之后,彻底不排斥了,反而觉得写起来挺有意思的)

项目实践

接下来就是实践阶段

项目构建

我这里脚手架直接用的vite2,根据文档流程正常走一遭。

使用npm构建vite项目:

$ npm create vite@latest

选择react => react-ts image.png

然后一个初始的react模板项目就构建完成了(蒸的C) image.png

接着下载依赖包:

$ npm install

然后启动开发环境:

$ npm run dev

image.png

这样一个最简洁的react的项目就跑起来了,然后在一步步配置项目所需要用的插件:

路由:react-router 状态管理:react-redux HTTP库:axios CSS预处理器:less 日期格式化组件库:moment UI组件库:Ant Design

删除scr目录下生成的模板页面文件,创建项目所需的目录结构:

image.png

路由配置

路由我这边用的是 react-router V6 貌似没找到官方的文档,这个版本新增了一个API useRoutes ,能读取路由配置数组,生成相应的路由组件列表,配方跟 vue-router 差不多,当然标签形式还是可以正常使用,只不过我习惯这种API模式:

创建路由管理文件:src/router/index.tsx

import LoginPage from "./../views/login"; import LayoutPage from "./../views/layout"; import HomePage from "./../views/home"; import LandPage from "./../views/api/land"; import IndustryPage from "./../views/api/industry"; import RolePage from "./../views/sys/role"; import MenuPage from "./../views/sys/menu"; const routes:any = [ {path: "/login", element: , isHome: true}, { path: "/", element: , // 设置子路由 children: [ {path: "/home", element: }, {path: "/land", element: }, {path: "/industry", element: }, {path: "/role", element: }, {path: "/menu", element: } ] } ] export default routes

path: '/'为默认路由,配置好路由列表后,到项目入口文件mian.tsx中引入路由配置:

修改入口文件:src/main.tsx

import ReactDOM from 'react-dom' import routes from "./router"; import { useRoutes } from 'react-router-dom'; import {BrowserRouter as Router} from 'react-router-dom' import './assets/css/index.css' import 'antd/dist/antd.css'; import zhCN from 'antd/lib/locale/zh_CN'; import { ConfigProvider } from 'antd'; function App() { return useRoutes(routes) } const renderApp = () => { ReactDOM.render( , document.getElementById('root') ) } renderApp();

创建一个函数组件,将路由数组传入useRoutes中,useRoutes只能作用于router context中,所以useRoutes需要写组件BrowserRouter里。

ConfigProvider是Ant Design国际化组件。

状态管理

状态管理器集成的react-redux,但是最终没有使用这种方式,而是用 context 组件树全局共享数据的方式,这个后续描述。

创建入口文件:src/store/index.tsx

import { configureStore, combineReducers } from '@reduxjs/toolkit'; import { TypedUseSelectorHook, useSelector, useDispatch } from 'react-redux'; import slice from './slices'; const reducer = combineReducers({ slice }); export const store = configureStore({ reducer, }); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; export const useAppDispatch = () => useDispatch(); export const useAppSelector: TypedUseSelectorHook = useSelector; export default store;

创建数据中心文件:src/store/slices.tsx

import { createSlice } from '@reduxjs/toolkit'; import { RootState } from '../store'; export interface IGlobalState { pageTabActive: any; tabsList: Array; } const initialState: IGlobalState = { pageTabActive: -1, // 当前menu页面ID tabsList: [], // 当前已打开的tab页列表 collapsed: window.innerWidth < 1000 // 菜单闭合 }; const globalSlice = createSlice({ name: "global", initialState, reducers: { setPageTab: (state, action) => { state.pageTabActive = action.payload || -1 }, setTabsList: (state, action) => { state.tabsList = action.payload || [] } } }); export const selectGlobal = (state: RootState) => state.slice; export const { setPageTab, setTabsList } = globalSlice.actions; export default globalSlice.reducer;

修改入口文件:src/main.tsx

... import { Provider } from 'react-redux'; import store from './store/index'; const renderApp = () => { ReactDOM.render( ... ... , document.getElementById('root') ) } ...

组件中使用:src/views/layout/components/c-nav/index.tsx

import { useAppDispatch, useAppSelector } from './store'; import { selectGlobal, setPageTab, setTabsList } from './store/slices'; function LayoutNav() { const globalState:any = useAppSelector(selectGlobal); // 属性 const dispatch = useAppDispatch(); // 方法 dispatch(setPageTab("-1")) dispatch(setTabsList([])) return {globalState.pageTabActive} } export default LayoutNav

我这里用了reduxjs/toolkit库,它内将redux一些繁琐配置都进行了封装,具体还待探究。

redux三大原则: 1)单一数据源 2)state 只读 3)使用函数来执行修改

在redux上面没做过多纠结,因为最终我采用了context代替,所以更多的去研究context了,当然项目中还是保留了了redux方案。

Hooks // 函数组件 function Welcome(props) { return Hello, {props.name}; } // class组件 class Welcome extends React.Component { render() { return Hello, {this.props.name}; } }

react 组件分为函数组件和class组件,它们的区别在于: 类组件有this,函数组件没有。 类组件有生命周期,函数组件没有。 类组件有state状态,函数组件没有。

在hooks出现之前,react中的函数组件通常只考虑负责UI的渲染,没有自身的状态没有业务逻辑代码,是一个纯函数,下面看看有hooks之前的函数组件:

import {useState} from 'react' function Welcome() { const [name, setName] = useState('小明') useEffect(() => { setName('小南') },[]) return Hello, {props.name}; } export default Welcome

hooks为函数组件提供了:useState状态,useEffect副作用,useCallback缓存函数等,让一个应用程序可以不用写任何类组件。

useState状态只能通过解构函数进行修改。 useEffect副作用可看做class组件中三个生命周期函数的组合,第二个参数表示需要监听的状态变化,如果没设置则表示监听所以状态变化执行。

Context

react是单向数据流,数据通过props自上由下传递,但这种做法在组件树较深较复杂的时候,多个组件都需要的时候使用极其繁琐,Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

上面说过我项目使用Context代替redux做全局状态管理,因为Context对于一个组件树而言就是全局的,虽然它的设计目的是为了简化props逐层传递,但在所有路由组件都集中在一个主路由下的时候,那么这个主路由组件的状态就可以通过Context做到全局状态管理的效果,这是目前的个人浅见~~。

import {createContext, useState} from 'react' import style from './index.module.less' import LayoutNav from "./components/c-nav/index"; import LayoutMain from "./components/c-main/index"; interface IGlobalState { pageTabActive: String; tabsList: Array; collapsed: boolean; } const initialState: IGlobalState = { pageTabActive: "-1", // 当前menu页面ID tabsList: [] // 当前已打开的tab页列表 }; export const MyContext = createContext({}) function App() { const [state, setState] = useState(initialState) return ( ) } export default App

通过createContext创建一个名为MyContext的Context对象,组件树会将Context匹配到离自己最近的Provider中。

MyContext.Provider为生产者,value属性为需要传递的属性,供内部组件使用。

在组件内如何使用Context,我这里使用的是useContextAPI获取导入的context对象值,可以跟自身组件中的状态一样使用。

import { MyContext } from "./../../index"; function LayoutNav() { const {state, setState}:any = useContext(MyContext) setState({...state, tabsList: []}); console.log(state) }

还有其他用法待探究

最后

因为react还未学习全面,上述见解有不对的地方望见谅。 后续通过更多的项目去锤炼。

不积蛙步,无以至千里 不积小流,无以成江海



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有