【项目】React18 + TS 模版搭建

4,503 阅读5分钟

React18 + TS 模版搭建

配置 React18 + TS 的项目模版和代码规范, 让你的后续工作更丝滑

安装

create-react-app

Getting Started | Create React App (create-react-app.dev)

npx create-react-app git-blog --template typescript
cd git-blog
npm start

QA

  1. 如何安装指令版本?
npm install react@17.x react-dom@17.x --save
  1. Jsx 文件报错?

image.png

  • ts项目安装后 删除node_modules,重新 npm install, 不然jsx会报错;
  • tsconfig.json 文件中,启用 --jsx 标志; "jsx": "react-jsx" 修改为 "jsx": "react"
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react" // 新增jsx 编译
  },
  "include": ["src"]
}

目录结构调整

目录规范

├── src
│       ├── api     # axios 请求 
│       ├── assets      # 项目资源文件,比如,图片 等
│       ├── components      # 通用组件
│       ├── pages     # 页面
│       ├── router      # 路由
│       ├── store     # store
│       ├── style     # 全局样式
│       ├── utils     # 工具,比如,token、axios 的封装等
│       ├── App.tsx     # 根组件
│       ├── index.tsx     # 项目入口
│       ├── react-app-env.d.ts
│       ├── reportWebVitals.ts

QA

react < 18 的版本,src/index.tsx 修改为:

import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
ReactDOM.render(
    <App />,
  document.getElementById('root')
)

路由

V6

createBrowserRouter vv6.3.0 | React Router

npm i react-router-dom

V6 提供配置化路由。

  • router/index.tsc
import React from 'react'
import {createBrowserRouter, Link} from 'react-router-dom'const router = createBrowserRouter([
  {
    path: '/',
    element: (
      <div>
        <h1>Hello World</h1>
        <Link to="about">About Us</Link>
      </div>
    )
  },
  {
    path: 'about',
    element: <div>About</div>
  }
])
​
export default router
​
  • App.tsx
import React from 'react'
import './style/App.css'
import {RouterProvider} from 'react-router-dom'
import router from './router'function App() {
  return <RouterProvider router={router} />
}
​
export default App

QA

react-router V5 配置路由规则。

import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Login from './pages/Login/Login.jsx'
import Layout from './pages/Layout/Layout.jsx'
import NotFound from './pages/NotFound/NotFound.jsx'
export default function App() {
  return (
    <Router>
      <div className="app">
        <Switch>
          <Route path="/login" component={Login}></Route>
          <Route path="/layout" component={Layout}></Route>
          {/* 增加一个404 */}
          <Route component={NotFound}></Route>
        </Switch>
      </div>
    </Router>
  )
}

ArcoDesign 组件库

快速上手 | ArcoDesign

npm i @arco-design/web-react
  • App.tsx
import React from 'react'
import './style/App.css'
import {RouterProvider} from 'react-router-dom'
import router from './router'import {Button} from '@arco-design/web-react'
import '@arco-design/web-react/dist/css/arco.css'function App() {
  return (
    <div>
      <RouterProvider router={router} />
      <Button type="primary">Hello Arco</Button>
    </div>
  )
}
​
export default App

axios

axios中文网|axios API 中文文档 | axios (axios-js.com)

npm i axios
  • util/http.js
import axios from 'axios'// baseURL设置/超时时间设置
const instance = axios.create({
  baseURL: 'http://xxx.xxx.xx', // 配置请求基地址
  timeout: 5000 // 配置等待时间
})
​
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 操作请求
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})
​
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 操作响应
  return response.data
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})
​
/**
 * @param {String} - url  请求地址
 * @param {String} - method  请求类型
 * @param {Object} - submitData  对象类型,提交数据
 */
const request = (url, method, submitData) => {
  return instance({
    url,
    method,
    [method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
  })
}
​
export default request

sass

react 脚手架中已集成了sass 的配置,因此只需要安装 sass 的依赖包,就可以直接使用sass。

npm i sass -D

修改 @为src路径

npm i -D @craco/craco
  • 在项目根目录下,创建配置文件:craco.config.js
// craco.config.js
const path = require('path')
module.exports = {
  webpack: {
    alias: {
      '@': path.join(__dirname, 'src') // 允许通过@符号来表示 src目录
    }
  }
}
  • 修改 package.json 中的脚本命令
// 将 start/build/test 三个命令修改为 craco 方式
"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  "eject": "react-scripts eject"
},
  • 配置 vscode 使用 @提示

jsconfig.jsontsconfig.json 新增,我们使用的是TS环境,配置tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./", // 新增:配置 vscode  使用 @提示
    "paths": {
      "@/*": ["src/*"] // 新增:配置 vscode  使用 @提示
    },
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react" // 新增:jsx 编译
  },
  "include": ["src"]
}

样式解决方案

如果多个组件的样式中出现选择器重复,那么一个组件中的样式就会在另一个组件中也生效,从而造成组件之间样式相互覆盖的问题?

  1. 方法一 (不推荐)

给每个组件的根标签取不一样的类名。

  1. 方法二 CSS IN JS (推荐使用)

CSS IN JS : 是使用 JavaScript 编写 CSS 的统称,用来解决 CSS 样式冲突、覆盖等问题。

具体实现方法有50种(下述为常用的两种)。

  • scoped : vue中默认使用 scoped css
  • CSS Moudles : React 中默认使用 CSS Moudles

CSS Modules 的使用

用法:

  • 在所有的css的文件名后缀前面加上 .moudle
  • 组件中, 导入 import styles from './xxx.module.scss
  • 组件中使用 : <div className={styles.css类名}>

简化用法:

  • 定义样式, 在根节点下用 :global {}包裹

  • 组件中使用方法

    • 根节点用 <div className={style.css类名}>
    • 子节点用 直接使用类名 className=css类名

注意 :

  • CSS Moudles 最好使用 驼峰命名

  • 保持原有类名的语法 :global(.类名){样式}

    • 应用场景 : 修改别人已经封装好组件的类名(组件库-antd)

QA

  1. CSS Modules 配置vscode代码提示?
  • 安装依赖
npm install typescript-plugin-css-modules -D
  • tsconfig.json中配置插件的使用
{
  "compilerOptions": {
    ...
    // 添加下面配置
    "plugins": [
      {
        "name": "typescript-plugin-css-modules"
      }
    ]
  },
  "include": [
    "src"
  ]
}
  • 在项目的根目录新建.vscode/settings.json,配置vscode的配置
// .vscode/settings.json文件中{
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}
  • 创建文件src/types/custom.d.ts
declare module "*.css" {
  const css: { [key: string]: string };
  export default css;
}

image.png

Redux配置

Redux

  • 安装
npm i redux react-redux redux-thunk redux-logger @types/redux-logger
npm install  redux-devtools-extension --save-dev
  • store 目录结构
├─store                # redux根目录
│  ├─reducers
│  │  ├─index.js  
│  │  └─student.js   
│  ├─actions  
│  │  └─student.js 
│    └─index.js          # 创建store并导出
  • action/student.js
export const actionTypes = {
  //设置学生查询结果数组和总数
  setStudentsAndTotal: 'setStudentsAndTotal'
}
​
export function setStudentsAndTotal(arr, total) {
  return {
    type: actionTypes.setStudentsAndTotal,
    payload: {
      datas: arr,
      total
    }
  }
}
​
  • store/reducers/index.js
// 创建一个唯一的reducer
import students from './student'
import {combineReducers} from 'redux'export default combineReducers({
  students
})
​
  • store/reducers/student.js
import {actionTypes} from '@/store/action/student'// 默认状态
const initialState = {
  datas: [],
  total: 0
}
​
/**
 * 控制查询结果的reducer
 * @param {*} state
 * @param {*} action
 */
export default function (state = initialState, {type, payload}) {
  switch (type) {
    case actionTypes.setStudentsAndTotal:
      return {
        ...state,
        ...payload
      }
    default:
      return state
  }
}
​
  • store/index.js
// 用于创建仓库,并导出
import {createStore, applyMiddleware} from 'redux'
import reducer from './reducer'
import logger from 'redux-logger'
import thunk from 'redux-thunk'// 安装redux-devtools-extension的可视化工具。
import {composeWithDevTools} from 'redux-devtools-extension'const store = createStore(
  reducer,
  composeWithDevTools(applyMiddleware(thunk, logger))
)
​
export default store
​
  • src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import './style/index.scss'
import App from '@/App'
import reportWebVitals from './reportWebVitals'
import {Provider} from 'react-redux'
import store from './store'const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <Provider store={store}>
       <App />
  </Provider>
)
​
reportWebVitals()
​
  • App.tsx 测试
import React, {useEffect} from 'react'
import '@/style/App.scss'
import {RouterProvider} from 'react-router-dom'
import router from './router'
import {Button} from '@arco-design/web-react'
import '@arco-design/web-react/dist/css/arco.css'import {useSelector, useDispatch} from 'react-redux'
import {setStudentsAndTotal} from '@/store/action/student'function App() {
  const data = useSelector((state) => {
    return state
  })
  const dispatch = useDispatch()
  console.log(data)
  
  useEffect(() => {
    dispatch(setStudentsAndTotal([1, 2, 3], 3))
    console.log('setStudentsAndTotal', data)
  }, [])
​
  return (
    <div>
      <RouterProvider router={router} />
      <Button type="primary">Hello Arco</Button>
    </div>
  )
}
​
export default App

image.png

QA

Redux - A predictable state container for JavaScript apps. | Redux

Getting Started with React Redux | React Redux (react-redux.js.org)

Redux DevTools 扩展的使用说明 - 掘金 (juejin.cn)

模版地址

PantherVkin/react-ts-project-template: React18 项目模版 (github.com)