0


react18+ts4的项目初始化(带路由配置,ReduxToolkit,axios封装)(附github地址)

1.项目的初始化配置

(1)创建react+ts项目

create-react-app react_ts_music --template typescript

生成目录:
image.png
修改运行package.json中的配置
image.png
改成:
image.png

(2)项目基本配置

1.craco配置webpack

npm install @craco/craco@alpha -D

创建craco.config.json文件:

const path =require('path')constresolve=(dir)=> path.resolve(__dirname, dir)

module.exports ={
  webpack:{
    alias:{'@':resolve('src'),}}}
2.配置@指定的src文件下的内容

在tsconfig.json文件中加:
image.png

3.代码规范配置
创建.editorconfig文件配置
# http://editorconfig.org

root = true

[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
配置prettier工具

可让代码更加好看,风格一致
image.png

npm install prettier -D

创建_prettierrc文件

{"useTabs":false,"tabWidth":2,"printWidth":80,"singleQuote":false,"trailingComma":"none","semi":false}

改运行文件
image.png
image.png

# /build/*
# .local
# .output.js
# /node_modules/**

# **/*.svg
# **/*.sh

# /public/*
配置ESlint工具

npm install eslint -D

初始化:

npx eslint --init

image.png

module.exports ={
    env:{
      browser:true,
      node:true,
      es2021:true},extends:['eslint:recommended','plugin:react/recommended','plugin:@typescript-eslint/recommended',],
    overrides:[],
    parser:'@typescript-eslint/parser',
    parserOptions:{
      ecmaVersion:'latest',
      sourceType:'module'},
    plugins:['react','@typescript-eslint'],
    rules:{'@typescript-eslint/no-var-requires':'off',}}

2.项目内容配置

(1)目录的划分

image.png

(2)配置less

npm install craco-less@2.1.0-alpha.0

craco.config.js文件:

const path =require("path");const CracoLessPlugin =require('craco-less')constresolve=(dir)=> path.resolve(__dirname, dir);

module.exports ={
  plugins:[{
      plugin: CracoLessPlugin,}],
  webpack:{
    alias:{"@":resolve("src"),},},};

(3)路由的配置

npm install react-router-dom

router文件夹建立index.tsx

import React,{lazy} from "react";
import {Navigate} from 'react-router-dom'
import {RouteObject} from 'react-router-dom'
const Discover = lazy(() => import('@/views/discover'))

const routes: RouteObject[]=[
  {
    path: '/',
    element: <Navigate to="/discover" />
  },
  {
    path: '/discover',
    element: <Discover />,
    children: [

    ]
  },
]

export default routes
import React,{Suspense} from "react";
import {useRoutes,Link} from 'react-router-dom'
import routes from './router'
function App() {
  return <div className="App">
    <div className="topHeader">
      <Link to="/discover">discover</Link>
      <Link to="/users">users</Link>
      <Link to="/task">task</Link>
    </div>
    <Suspense fallback="Loading...">
      <div className="main">
        {useRoutes(routes)}
      </div>
    </Suspense>
  </div>;
}

export default App;
import React from "react";
import ReactDOM from "react-dom/client";
import {BrowserRouter} from 'react-router-dom'
import App from "@/App";
import "@/assets/css/base.less"
const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(<BrowserRouter><App /></BrowserRouter>);

(4)redux的配置

安装react-redux:react-redux和@reduxjs/toolkit。

npm install react-redux @reduxjs/toolkit -S

安装完相关包以后开始编写基本的 RTK 程序

  • 创建一个store文件夹
  • 创建一个index.ts做为主入口
  • 创建一个festures文件夹用来装所有的store
  • 创建一个counterSlice.js文件,并导出简单的加减方法
创建 Redux State Slice

创建 slice 需要一个字符串名称来标识切片、一个初始 state 以及一个或多个定义了该如何更新 state 的 reducer 函数。slice 创建后 ,我们可以导出 slice 中生成的 Redux action creators 和 reducer 函数。
image.png
store/features/counterSlice.js

import { createSlice } from '@reduxjs/toolkit'

// 创建一个Slice
export const counterSlice = createSlice({
  name: 'counter',
  initialState:{
    value: 0,
  },
  reducers: {
    // 定义一个加的方法
    increment:(state,{payload})  => { 
      state.value += parseInt(payload)
    },
    // 定义一个减的方法
    decrement: state => {
      state.value -= 1
    },
  },
})

// 导出加减方法
export const { increment, decrement } = counterSlice.actions

// 暴露reducer
export default counterSlice.reducer

createSlice是一个全自动的创建reducer切片的方法,在它的内部调用就是createAction和createReducer,之所以先介绍那两个也是这个原因。createSlice需要一个对象作为参数,对象中通过不同的属性来指定reducer的配置信息。
createSlice(configuration object)
配置对象中的属性:

  • name —— reducer的名字,会作为action中type属性的前缀,不要重复
  • initialState —— state的初始值
  • reducers —— reducer的具体方法,需要一个对象作为参数,可以以方法的形式添加reducer,RTK会自动生成action对象。

总的来说,使用createSlice创建切片后,切片会自动根据配置对象生成action和reducer,action需要导出给调用处,调用处可以使用action作为dispatch的参数触发state的修改。reducer需要传递给configureStore以使其在仓库中生效。
我们可以看看counterSlice和counterSlice.actions是什么样子
image.png

将 Slice Reducers 添加到 Store 中

下一步,我们需要从计数切片中引入 reducer 函数,并将它添加到我们的 store 中。通过在 reducer 参数中定义一个字段,我们告诉 store 使用这个 slice reducer 函数来处理对该状态的所有更新。
我们以前直接用redux是这样的

const reducer = combineReducers({
  counter:counterReducers
});

const store = createStore(reducer);

store/index.js
切片的reducer属性是切片根据我们传递的方法自动创建生成的reducer,需要将其作为reducer传递进configureStore的配置对象中以使其生效:

import { configureStore } from '@reduxjs/toolkit'
import { useSelector,useDispatch } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
import counterSlice from './features/counterSlice'
 
// configureStore创建一个redux数据
const store = configureStore({
  // 合并多个Slice
  reducer: {
    counter: counterSlice,
  },
})

//app的hook,ts类型定义
export type IRootState=ReturnType<typeof store.getState>
type AppDispatch = typeof store.dispatch
export const useAppSelector: TypedUseSelectorHook<IRootState> = useSelector
export const useAppDispatch: () => AppDispatch = useDispatch
export default store
  • configureStore需要一个对象作为参数,在这个对象中可以通过不同的属性来对store进行设置,比如:reducer属性用来设置store中关联到的reducer,preloadedState用来指定state的初始值等,还有一些值我们会放到后边讲解。
  • reducer属性可以直接传递一个reducer,也可以传递一个对象作为值。如果只传递一个reducer,则意味着store中只有一个reducer。若传递一个对象作为参数,对象的每个属性都可以执行一个reducer,在方法内部它会自动对这些reducer进行合并。
store加到全局

main.js

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

// redux toolkit
import { Provider } from 'react-redux'
import store from './store'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>,
)
在 React 组件中使用 Redux 状态和操作

现在我们可以使用 React-Redux 钩子让 React 组件与 Redux store 交互。我们可以使用 useSelector 从 store 中读取数据,使用 useDispatch dispatch actions。
App.jsx

import React, { memo ,useRef,createRef} from 'react'
import { increment, decrement } from '@/store/features/counterSlice'
import type { FC, ReactNode } from 'react'
// import { IRootState } from '@/store'
import { useAppSelector ,useAppDispatch} from '@/store'
interface IProps {
  children?: ReactNode
}

const Artist: FC<IProps> = () => {
  const count = useAppSelector((state) => state.counter.value)
  const dispatch = useAppDispatch()
  const InputRef= useRef<HTMLInputElement>(null);
  const InputRef2= createRef();
  return (
    <div>
      <h1>count</h1>
      <div style={{ width: 100, margin: '10px' }}>
        <input ref = { InputRef } type="text"/>
        <button onClick={() => dispatch(increment(InputRef.current?.value))}>+</button>
        <button onClick={() => dispatch(decrement())}>-</button>

        <span>{count}</span>
      </div>
    </div> 
  )
}

export default memo(Artist)

image.png
现在,每当你点击”递增“和“递减”按钮。

  • 会 dispatch 对应的 Redux action 到 store
  • 在计数器切片对应的 reducer 中将看到 action 并更新其状态
  • 组件将从 store 中看到新的状态,并使用新数据重新渲染组件

(5)axios配置

在service文件下:创建requset文件夹:

service/requset:

配置接口的路径:

const BASE_URL = 'http://codercba.com:9002/'
const TIME_OUT = 10000

console.log(process.env.REACT_APP_NAME)
console.log(process.env.REACT_APP_AGE)

export { BASE_URL, TIME_OUT }

拦截器类型:

import type { AxiosRequestConfig, AxiosResponse } from 'axios'

export interface HYRequestInterceptors<T = AxiosResponse> {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (res: T) => T
  responseInterceptorCatch?: (error: any) => any
}

export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: HYRequestInterceptors<T>
  showLoading?: boolean
}

请求类的封装:

import axios from'axios'importtype{ AxiosInstance }from'axios'importtype{ HYRequestInterceptors, HYRequestConfig }from'./type'constDEAFULT_LOADING=trueclassHYRequest{
  instance: AxiosInstance
  interceptors?: HYRequestInterceptors
  showLoading:booleanconstructor(config: HYRequestConfig){// 创建axios实例this.instance = axios.create(config)// 保存基本信息this.showLoading = config.showLoading ??DEAFULT_LOADINGthis.interceptors = config.interceptors

    // 使用拦截器// 1.从config中取出的拦截器是对应的实例的拦截器this.instance.interceptors.request.use(// this.interceptors?.requestInterceptor,this.interceptors?.requestInterceptorCatch
    )this.instance.interceptors.response.use(this.interceptors?.responseInterceptor,this.interceptors?.responseInterceptorCatch
    )// 2.添加所有的实例都有的拦截器this.instance.interceptors.request.use((config)=>{return config
      },(err)=>{return err
      })this.instance.interceptors.response.use((res)=>{const data = res.data
        if(data.returnCode ==='-1001'){console.log('请求失败~, 错误信息')}else{return data
        }},(err)=>{// 例子: 判断不同的HttpErrorCode显示不同的错误信息if(err.response.status ===404){console.log('404的错误~')}return err
      })}request<T=any>(config: HYRequestConfig<T>):Promise<T>{returnnewPromise((resolve, reject)=>{// 1.单个请求对请求config的处理if(config.interceptors?.requestInterceptor){
        config = config.interceptors.requestInterceptor(config)}// 2.判断是否需要显示loadingif(config.showLoading ===false){this.showLoading = config.showLoading
      }this.instance
        .request<any,T>(config).then((res)=>{// 1.单个请求对数据的处理if(config.interceptors?.responseInterceptor){
            res = config.interceptors.responseInterceptor(res)}// 2.将showLoading设置true, 这样不会影响下一个请求this.showLoading =DEAFULT_LOADING// 3.将结果resolve返回出去resolve(res)}).catch((err)=>{// 将showLoading设置true, 这样不会影响下一个请求this.showLoading =DEAFULT_LOADINGreject(err)return err
        })})}get<T=any>(config: HYRequestConfig<T>):Promise<T>{returnthis.request<T>({...config, method:'GET'})}post<T=any>(config: HYRequestConfig<T>):Promise<T>{returnthis.request<T>({...config, method:'POST'})}delete<T=any>(config: HYRequestConfig<T>):Promise<T>{returnthis.request<T>({...config, method:'DELETE'})}patch<T=any>(config: HYRequestConfig<T>):Promise<T>{returnthis.request<T>({...config, method:'PATCH'})}}exportdefault HYRequest
service/modules对要用的接口进行封装:
import hyRequest from'..'exportfunctiongetTopBanner(){return hyRequest.get({
    url:'/banner'})}exportfunctiongetHotRecommend(){return hyRequest.get({
    url:'/personalized'})}exportfunctiongetNewAlbum(offset =0, limit =10){return hyRequest.get({
    url:'/album/new',
    params:{
      offset,
      limit
    }})}exportfunctiongetPlayListDetail(id:number){return hyRequest.get({
    url:'/playlist/detail',
    params:{
      id
    }})}
service/index

请求函数的封装

// service统一出口import HYRequest from'./request'import{BASE_URL,TIME_OUT}from'./request/config'const hyRequest =newHYRequest({
  baseURL:BASE_URL,
  timeout:TIME_OUT,
  interceptors:{requestInterceptor:(config)=>{return config
    },requestInterceptorCatch:(err)=>{return err
    },responseInterceptor:(res)=>{return res
    },responseInterceptorCatch:(err)=>{return err
    }}})exportdefault hyRequest
路由的请求:
import hyRequest from '@/service';
import React, { memo,useEffect,useState} from 'react'
import type { FC, ReactNode } from 'react'
import {getTopBanner} from '@/service/modules/recommend'
interface IProps {
  children?: ReactNode
}
interface listRoot {
  imageUrl: string
  targetId: number
  adid: any
  targetType: number
  titleColor: string
  typeTitle: string
  url: any
  exclusive: boolean
  monitorImpress: any
  monitorClick: any
  monitorType: any
  monitorImpressList: any
  monitorClickList: any
  monitorBlackList: any
  extMonitor: any
  extMonitorInfo: any
  adSource: any
  adLocation: any
  adDispatchJson: any
  encodeId: string
  program: any
  event: any
  video: any
  song: any
  scm: string
  bannerBizType: string
}

const AxiosTemplate: FC<IProps> = () => {
  const [list , setList]=useState<listRoot[]>([])
  useEffect(() =>{
    (async function() {
      const res= await getTopBanner()
      setList(res.banners)
    })()
  },[])
  return <div>
    <h1>我是axios请求的数据:</h1>
    <ul>
      {
        list.map((item)=>{
          return (
            <li key={item.encodeId}>{item.imageUrl}</li>
          )
        })
      }
    </ul>
  </div>
}

export default memo(AxiosTemplate)

3.github代码地址:

https://github.com/wzz778/react_ts_template


本文转载自: https://blog.csdn.net/Azbtt/article/details/130781849
版权归原作者 阿泽不会飞 所有, 如有侵权,请联系我们删除。

“react18+ts4的项目初始化(带路由配置,ReduxToolkit,axios封装)(附github地址)”的评论:

还没有评论