国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux

這篇具有很好參考價(jià)值的文章主要介紹了react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、項(xiàng)目搭建

1、創(chuàng)建項(xiàng)目

  • 1、該項(xiàng)目使用的是ts創(chuàng)建的 所以需要加上--template typescript
    • create-react-app kiki_ts_react_music --template typescript
  • 2、整理項(xiàng)目結(jié)構(gòu) 刪除一些自己用不到的文件
  • react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css
    react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.2 配置項(xiàng)目

1.2.1 更換icon

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.2.2 更換項(xiàng)目名稱

在index.html文件里面

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.2.1 配置項(xiàng)目別名

  • 1、npm i -D @craco/craco
  • 2、在根文件創(chuàng)建 craco.config.ts
const path = require("path");
const CracoLessPlugin = require("craco-less");

// path.resolve返回當(dāng)前文件的絕對(duì)路徑 拼接+dir
const resolve = (dir) => path.resolve(__dirname, dir);
module.exports = {
 plugins: [{ plugin: CracoLessPlugin }],
 webpack: {
   alias: {
     "@": resolve("src"),
   },
 },
};
  • 3、修改tsconfig.json
    react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  • 4、修改 package.json
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

1.3 代碼規(guī)范

1.3.1 集成editorconfig配置

EditorConfig 有助于為不同 IDE 編輯器上處理同一項(xiàng)目的多個(gè)開發(fā)人員維護(hù)一致的編碼風(fēng)格。

  • 1、在根目錄下創(chuàng)建.editorconfig文件
# http://editorconfig.org

root = true

[*] # 表示所有文件適用
charset = utf-8 # 設(shè)置文件字符集為 utf-8
indent_style = space # 縮進(jìn)風(fēng)格(tab | space)
indent_size = 2 # 縮進(jìn)大小
end_of_line = lf # 控制換行類型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行尾的任意空白字符
insert_final_newline = true # 始終在文件末尾插入一個(gè)新行

[*.md] # 表示僅 md 文件適用以下規(guī)則
max_line_length = off
trim_trailing_whitespace = false

**同時(shí)需要安裝插件 **EditorConfig for VS Code

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.3.2 使用prettier工具

Prettier 是一款強(qiáng)大的代碼格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等語言,基本上前端能用到的文件格式它都可以搞定,是當(dāng)下最流行的代碼格式化工具。

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css
react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

  • 1.安裝prettier

    npm install prettier -D

  • 2、配置.prettierrc文件:在根目錄下創(chuàng)建該文件
    react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

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

  • 3、創(chuàng)建.prettierignore忽略文件 在根目錄下
/dist/*
.local
.output.js
/node_modules/**

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

/public/*

  • 4、在package.json中配置一個(gè)scripts:
    "prettier": "prettier --write ."

執(zhí)行 npm run prettier就會(huì)將項(xiàng)目全部按照prettier的配置進(jìn)行格式化

1.4 項(xiàng)目結(jié)構(gòu)

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.5 對(duì)css進(jìn)行重置

  • 1、下載normalize.css
    cnpm install normalize.css
    在index.tsx里面引入import 'normalize.css'

  • 2、使用less
    cnpm install craco-less

const path = require('path')
const CracoLessPlugin = require('craco-less')

const resolve = (dir) => path.resolve(__dirname, dir)
module.exports = {
 plugins: [{ plugin: CracoLessPlugin }],
 webpack: {
   alias: {
     '@': resolve('src')
   }
 }
}

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

  • 3、配置自定義的css
    react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css
    最后都在index.jsx中引入
import 'normalize.css'
import '@/assets/css/index.less'

1.6 注入router

npm install react-router-dom

  • 在tsx中 使用到dom的頁面都需要引入import React from 'react'

  • router/index.tsx

import React from 'react'
import type { RouteObject } from 'react-router-dom'
import Discover from '@/views/discover'
import Mime from '@/views/mime'

const routes: RouteObject[] = [
  { path: '/', element: <Mime /> },
  { path: '/discover', element: <Discover /> }
]

export default routes

  • index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from '@/App'
import { BrowserRouter } from 'react-router-dom'
import 'normalize.css'
import '@/assets/css/index.less'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

  • index.tsx
import React, { Suspense } from 'react'
import { Link, useRoutes } from 'react-router-dom'
import routes from './router'

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>hahah</h1>
        <Link to="/discover">發(fā)現(xiàn)音樂</Link>
        <Suspense fallback="正在加載">{useRoutes(routes)}</Suspense>
      </header>
    </div>
  )
}

export default App

1.7 定義TS組件的規(guī)范

import React, { memo } from 'react'
import type { ReactNode } from 'react'

// 定義傳進(jìn)來的props類型
interface IProps {
  // 在之前的版本props默認(rèn)會(huì)有children是插槽 在后來取消了得自己寫
  children?: ReactNode
  name?: string
  age?: number
}

const Download: React.FC<IProps> = (props) => {
  return (
    <div>
      {props.children}
      <h1>{props.age}</h1>
      <h1>{props.name}</h1>
    </div>
  )
}

export default memo(Download)

import React, { Suspense } from 'react'
import { Link, useRoutes } from 'react-router-dom'
import routes from './router'
import Download from './views/download'

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>hahah</h1>
        <Link to="/discover">發(fā)現(xiàn)音樂</Link>

        <Download name="kiki">
          <h1>我是downLoad的插槽</h1>
        </Download>

        <Suspense fallback="正在加載">{useRoutes(routes)}</Suspense>
      </header>
    </div>
  )
}

export default App

1.8 創(chuàng)建代碼片段

首選項(xiàng)=>設(shè)置代碼片段=>react-ts
react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

生成代碼片段的網(wǎng)站
https://snippet-generator.app/?description=&tabtrigger=&snippet=&mode=vscode
react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

import React, { memo } from 'react'
import type { FC, ReactNode } from 'react'

interface IProps {
  children?: ReactNode
}

const Template: FC<IProps> = () => {
  return <div>Template</div>
}

export default memo(Template)

1.9 二級(jí)路由和懶加載

  • discover頁面
import React, { memo, Suspense } from 'react'
import type { FC, ReactNode } from 'react'
import { Outlet, Link } from 'react-router-dom'

interface IProps {
  children?: ReactNode
}

const Discover: FC<IProps> = () => {
  return (
    <div>
      <div>
        <Link to="/discover/recommend">推薦</Link>
        <Link to="/discover/ranking">排行榜</Link>
        <Link to="/discover/songs">歌單</Link>
        <Link to="/discover/djradio">主播電臺(tái)</Link>
        <Link to="/discover/artist">歌手</Link>
        <Link to="/discover/album">新碟上架</Link>
      </div>
      {/* 二級(jí)路由也可以用suspense */}
      <Suspense fallback="正在加載">
        <Outlet />
      </Suspense>
    </div>
  )
}

export default memo(Discover)

  • App.jsx
import React, { Suspense } from 'react'
import { Link, useRoutes } from 'react-router-dom'
import routes from './router'
import Download from './views/download'

function App() {
  return (
    <div className="App">
      <div className="nav">
        <Link to="/discover">發(fā)現(xiàn)音樂</Link>
        <Link to="/mine">我的音樂</Link>
        <Link to="/focus">關(guān)注</Link>
        <Link to="/download">下載客戶端</Link>
      </div>
      <Suspense fallback="正在加載">{useRoutes(routes)}</Suspense>
      <div className="main"></div>
    </div>
  )
}

export default App

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

1.10 redux-reduxtk

cnpm install @reduxjs/toolkit react-redux

  • index.tsx 提供Provide
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from '@/App'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import 'normalize.css'
import '@/assets/css/index.less'
import store from './store'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
)

  • store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import { useSelector, useDispatch, TypedUseSelectorHook } from 'react-redux'
import counterReducer from './modules/counter'

const store = configureStore({
  reducer: {
    counter: counterReducer
  }
})

// 獲取函數(shù)的返回類型
type GetStateFnType = typeof store.getState
// 獲取函數(shù)返回類型的類型
type IRootState = ReturnType<GetStateFnType>
type DispatchType = typeof store.dispatch

export const useAppSelector: TypedUseSelectorHook<IRootState> = useSelector
export const useAppDisPatch: () => DispatchType = useDispatch

export default store


  • store/count.ts
import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    count: 1,
    message: 'hello'
  },
  reducers: {
    changeMessageAction(state, { payload }) {
      state.message = payload
    }
  }
})

export const { changeMessageAction } = counterSlice.actions
export default counterSlice.reducer

  • 使用的頁面
import React, { memo, Suspense } from 'react'
import type { FC, ReactNode } from 'react'
import { Outlet, Link } from 'react-router-dom'
import { useAppDisPatch, useAppSelector } from '@/store'
import { shallowEqual } from 'react-redux'
import { changeMessageAction } from '@/store/modules/counter'

interface IProps {
  children?: ReactNode
}

const Discover: FC<IProps> = () => {
  const { count, message } = useAppSelector(
    (state) => ({
      count: state.counter.count,
      message: state.counter.message
    }),
    shallowEqual
  )

  const dispatch = useAppDisPatch()

  const changeMessage = (message: string) => {
    dispatch(changeMessageAction(message))
  }
  return (
    <div>
      <div>
        {count}=={message}
        <button onClick={() => changeMessage('修改message')}>
          修改message
        </button>
        <Link to="/discover/recommend">推薦</Link>
        <Link to="/discover/ranking">排行榜</Link>
        <Link to="/discover/songs">歌單</Link>
        <Link to="/discover/djradio">主播電臺(tái)</Link>
        <Link to="/discover/artist">歌手</Link>
        <Link to="/discover/album">新碟上架</Link>
      </div>
      {/* 二級(jí)路由也可以用suspense */}
      <Suspense fallback="正在加載">
        <Outlet />
      </Suspense>
    </div>
  )
}

export default memo(Discover)

1.10 axios的封裝

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

  • request/index.ts
import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { HYRequestConfig } from './type'

// 攔截器: 蒙版Loading/token/修改配置

/**
 * 兩個(gè)難點(diǎn):
 *  1.攔截器進(jìn)行精細(xì)控制
 *    > 全局?jǐn)r截器
 *    > 實(shí)例攔截器
 *    > 單次請(qǐng)求攔截器
 *
 *  2.響應(yīng)結(jié)果的類型處理(泛型)
 */

class HYRequest {
  instance: AxiosInstance

  // request實(shí)例 => axios的實(shí)例
  constructor(config: any) {
    this.instance = axios.create(config)

    // 每個(gè)instance實(shí)例都添加攔截器
    this.instance.interceptors.request.use(
      (config) => {
        // loading/token
        return config
      },
      (err) => {
        return err
      }
    )
    this.instance.interceptors.response.use(
      (res) => {
        return res.data
      },
      (err) => {
        return err
      }
    )

    // 針對(duì)特定的hyRequest實(shí)例添加攔截器
    this.instance.interceptors.request.use(
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    )
    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.responseFailureFn
    )
  }

  // 封裝網(wǎng)絡(luò)請(qǐng)求的方法
  // T => IHomeData
  request<T = any>(config: HYRequestConfig<T>) {
    // 單次請(qǐng)求的成功攔截處理
    if (config.interceptors?.requestSuccessFn) {
      config = config.interceptors.requestSuccessFn(config)
    }

    // 返回Promise
    return new Promise<T>((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 單詞響應(yīng)的成功攔截處理
          if (config.interceptors?.responseSuccessFn) {
            res = config.interceptors.responseSuccessFn(res)
          }
          resolve(res)
        })
        .catch((err) => {
          reject(err)
        })
    })
  }

  get<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: 'GET' })
  }
  post<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: 'POST' })
  }
  delete<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: 'DELETE' })
  }
  patch<T = any>(config: HYRequestConfig<T>) {
    return this.request({ ...config, method: 'PATCH' })
  }
}

export default HYRequest

  • request/type.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'

// 針對(duì)AxiosRequestConfig配置進(jìn)行擴(kuò)展
export interface HYInterceptors<T = AxiosResponse> {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestFailureFn?: (err: any) => any
  responseSuccessFn?: (res: T) => T
  responseFailureFn?: (err: any) => any
}

export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: HYInterceptors<T>
}

  • config/index.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'

// 針對(duì)AxiosRequestConfig配置進(jìn)行擴(kuò)展
export interface HYInterceptors<T = AxiosResponse> {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestFailureFn?: (err: any) => any
  responseSuccessFn?: (res: T) => T
  responseFailureFn?: (err: any) => any
}

export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: HYInterceptors<T>
}

環(huán)境變量也可以通過配置文件 但是前面需要加上REACT_APP_…

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css

  • service/index.ts
import { BASE_URL, TIME_OUT } from './config'
import HYRequest from './request'

const hyRequest = new HYRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
  interceptors: {
    requestSuccessFn: (config: any) => {
      return config
    }
  }
})

export default hyRequest

react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux,react,react.js,javascript,前端,css文章來源地址http://www.zghlxwxcb.cn/news/detail-826319.html

  • 使用的頁面
import React, { memo, useEffect, useState } from 'react'
import type { FC, ReactNode } from 'react'
import hyRequest from '@/service'

interface IProps {
  children?: ReactNode
}

export interface IBannerData {
  imageUrl: string
  targetId: number
  targetType: number
  titleColor: string
  typeTitle: string
  url: string
  exclusive: boolean
  scm: string
  bannerBizType: string
}

const Recommend: FC<IProps> = () => {
  const [banners, setBanners] = useState<IBannerData[]>([])

  // 測(cè)試網(wǎng)絡(luò)請(qǐng)求
  useEffect(() => {
    hyRequest
      .get({
        url: '/banner'
      })
      .then((res) => {
        setBanners(res.banners)
      })
  }, [])

  return (
    <div>
      {banners.map((item, index) => {
        return <div key={index}>{item.imageUrl}</div>
      })}
    </div>
  )
}

export default memo(Recommend)

  • 可以在這個(gè)頁面自動(dòng)生成類型定義
    https://transform.tools/json-to-typescript

1.11 類組件和TS的結(jié)合

import React, { PureComponent } from 'react'
/**
 * state:
 * props:
 */

interface IProps {
  name: string
  age?: number
}

interface IState {
  message: string
  counter: number
}

class Demo02 extends PureComponent<IProps, IState> {
  name = 'aaaa'
  state = {
    message: 'Hello World',
    counter: 99
  }

  // getSnapshotBeforeUpdate() {
  //   return { address: '廬山' }
  // }

  // componentDidUpdate(
  //   prevProps: Readonly<IProps>,
  //   prevState: Readonly<IState>,
  //   snapshot?: ISnapshot | undefined
  // ): void {}

  // constructor(props: IProps) {
  //   super(props)

  //   // this.state = {
  //   //   message: 'Hello World',
  //   //   counter: 100
  //   // }
  // }

  render(): React.ReactNode {
    return (
      <div>
        name: {this.props.name}
        age: {this.props.age}
        message: {this.state.message}
        counter: {this.state.counter}
      </div>
    )
  }
}

export default Demo02


1.12 redux和ts的結(jié)合

import { createSlice, PayloadAction } from '@reduxjs/toolkit'

interface IState {
  count: number
  message: string
  address: string
  height: number
  direction: 'left' | 'right' | 'up' | 'down'
  names: string[]
}

const initialState: IState = {
  count: 100,
  message: 'Hello Redux',
  address: '廣州市',
  height: 1.88,
  direction: 'left',
  names: []
}

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    changeMessageAction(state, { payload }: PayloadAction<string>) {
      state.message = payload
    }
  }
})

export const { changeMessageAction } = counterSlice.actions
export default counterSlice.reducer

到了這里,關(guān)于react+ts【項(xiàng)目實(shí)戰(zhàn)一】配置項(xiàng)目/路由/redux的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包