React18+TS
一、项目搭建
1.1.创建项目
-
由于用create-react-app脚手架创建react项目时不会询问是否使用ts,所以需要为其添加一个后缀
create-react-app react_ts_music --template typescript
1.2.配置项目别名
-
例如:src -> @
-
但是由于脚手架创建出来的项目其已经对webpack的配置进行了封装,所以无法直接进行修改
-
方式一:运行代码npm run eject 使webpack的配置暴露出来,从而修改其配置(不推荐)
-
方式二:使用craco:create-react-app.config(帮助我们对webpack配置进行更改)
-
安装craco
由于使用的react-scripts是5以上的版本所以直接安装不行,需要安装alpha版本
npm install @craco/craco@alpha -D -
使用craco
1、创建craco.config.js文件
const path = require('path') const resolve = (dir) => path.resolve(__dirname, dir) module.exports = { webpack: { alias: { '@': resolve('src') } } }
-
-
-
除此之外还需要配置一下tsconfig.json文件,要不然TS检测会出现问题
"baseUrl": ".", "paths": { "@/*": [ "src/*" ] } -
最后需要carco去启动项目
在package.json里更改scripts
"scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "react-scripts eject" },
1.3.代码规范
Ⅰ、集成editorconfig配置
-
Editorconfig有助于为不同IDE编辑器上处理同一项目的多个开发人员维护一致的编码风格。
-
//.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 -
VSCode需要安装一个插件:Editorconfig for VS Code
Ⅱ、使用prettier工具
-
Prettier是一款强大的代码格式化工具,支持js,ts,css,scss,jsx,angular,vue,graphql, json,md等语言,基本上前端能用到的文件格式它都能搞定,是当前最流行的代码格式化工具。
-
1.安装prettier
npm install prettier -D2.配置.prettier文件
-
useTabs: 使用tab缩进还是空格缩进,选择false;
-
tabWidth:tab是空格的情况下是几个空格,选择2个;
-
printWidth:当前字符的长度,推荐80,也可以100或者120;
-
singleQuote:使用单引号还是双引号,选择true,使用单引号;
-
traillingComma:在多行输入的尾逗号是否添加,设置为none,比如对象类型的最后一个属性是否添加一个逗号;
-
semi:语句末尾是否要加分号,默认值true,选择false表示不加;
-
//.prettierrc { "useTabs": false, "tabWidth": 2, "printWidth": 100, "singleQuote": true, "trailingComma": "none", "bracketSpacing": true, "semi": false }
-
-
创建.prettierignore忽略文件
/build/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
安装VS Code插件Prettier - Code formatter,并且在设置中搜索Editor: Default Formatter选择Prettier,format on save=>勾选
配置脚本:
//package.json
"prettier": "prettier --write ."
npm run prettier
作用:用prettier对所有文件进行格式化
Ⅲ、使用ESlint
1.安装ESlint的插件
2.解决ESlint和prettier冲突的问题
安装插件:
npm install eslint -D
//由于配置过多需要默认去配置
npx eslint --init
由于之前在配置项目别名时使用到了require所以要忽略这个报错
rules: {
'@typescript-eslint/no-var-requires': 'off'
}
3.在设置中找到setting.json文件配置一些eslint(目前vscode不必要)
"eslint.alwaysShowStatus": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typrscriptreact"
]
4.保持prettier和eslint代码风格保持一致
npm install eslint-plugin-prettier -D eslint -config-prettier -D
//.eslintrc.js
extends: [
'plugin:prettier/recommended'
]
1.4.样式重置
1.引入normalize.css文件
npm install normalize.css
2.在index.tsx中进行导入
import 'normalize.css'
3.自己手写一些重置样式
1.5.less配置
npm install craco-less@2.1.0-alpha.0
配置:
//carco.config.js
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')
}
}
}
1.6.路由的配置
-
在单独的router文件夹中配置自己的路由并导出
const routes: RouteObject[] = [] -
在App中使用routes
<div className="main">{useRoutes(routes)}</div> -
千万不可忘记用Router包裹App组件
root.render( <HashRouter> <App /> </HashRouter> ) -
性能优化:路由懒加载
const Discover = lazy(() => import('@/views/discover')) -
当点击切换页面时会发现报错,原因是懒加载进行了分包处理,当时组件还未下载下来,所以出现了报错
解决办法:包裹suspense,在下载过程中展示
<Suspense fallback="">
<div className="main">{useRoutes(routes)}</div>
</Suspense>
-
配置二级路由
children:[ .... ] -
因为为二级路由需要一个占位,同样需要包裹一个suspense,防止二级路由出现闪烁
<Suspense fallback=""> <Outlet /> </Suspense>
1.7.生成模板
生成对应函数式组件的代码模板
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.8.状态管理
-
安装redux
npm install @reduxjs/toolkit react-redux -
创建store
import { configureStore } from '@reduxjs/toolkit' import counterReducer from './modules/counter' const store = configureStore({ reducer: { counter: counterReducer } }) export default store //index.tsx root.render( <Provider store={store}> <HashRouter> <App /> </HashRouter> </Provider> ) -
创建store片段
import { createSlice } from '@reduxjs/toolkit' const counterSlice = createSlice({ name: 'counter', initialState: { }, reducers: { changeMessageAction(state, { payload }) { state.message = payload } } }) export const { changeMessageAction } = counterSlice.actions export default counterSlice.reducer -
定义state的类型
自己封装一个useAppSelector
type GetStateFnType = typeof store.getState type IRootState = ReturnType<GetStateFnType> type DispatchType = typeof store.dispatch // useAppSelector的hook export const useAppSelector: TypedUseSelectorHook<IRootState> = useSelector export const useAppDispatch: () => DispatchType = useDispatch export const shallowEqualApp = shallowEqual
1.9.axios二次封装
- 复用之前的vue3+ts的封装
1.10.切换开发环境和生产环境
由于在开发阶段需要测试一些数据,而这些数据不应该在生产环境下暴露出去
-
手动切换:不可取
-
依赖当前环境development/production
这个环境变量由node注入,可在react-app-env.d.ts中查看
let BASE_URL = '' if (process.env.NODE_ENV === 'development') { BASE_URL = 'http://codercba.dev:9002' } else { BASE_URL = 'http://codercba.prod:9002' } -
从定义的环境变量的配置文件中, 加载变量
//.env.development REACT_APP_BASE_URL=http://codercba.dev:9002 //.env.production REACT_APP_BASE_URL=http://codercba.prod:9002 //注意:在给变量命名时一定要以REACT_APP开头
1.11.类组件的约束
由于类组件有自己的状态需要管理,所以需要约束类组件的state和props,以便有更好的代码提示
- PureComponent接受两个参数,第一个为props的约束类型,第二个为state的约束类型,第三个为getSnapshotBeforeUpdate函数的返回值类型(基本不用)
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
}
render(){}
}
由于组件会自动调用super方法,并且可以直接定义数据,所以可以省略掉constructor函数,简化代码
1.12.使用styled-components
-
安装styled-conponents
npm install styled-component -D -
引入styled-components时,出现模块未声明的报错
安装类型声明
npm install --save-dev @types/styled-components
二、注意事项
2.1.给link组件设置选中样式
这个时候可以使用NavLink,其内部会自动去比较hash值看是否匹配路径,
由此来动态添加active,并且刷新的时候也不会回到首页
当然如果不想添加active,想添加自己的类可以写如下代码
<NavLink
to={item.link}
className={({ isActive }) => {
return isActive ? 'active' : undefined
}}
>
{item.title}
<i className="icon sprite_01"></i>
</NavLink>
可以参考官方文档:NavLink v6.10.0 | React Router
2.2.图片资源
- 网易云的图片资源支持高斯模糊和缩放
- 高斯模糊:url+?imageView&blur= 参数x参数
- 缩放:url+?param= width x height
2.3.ref绑定组件
-
当我们去使用ref绑定组件的时候,由于未声明其类型,所以无法获取其的current属性
解决:react为我们提供了ElementRef
import type { ElementRef } from 'react' const bannerRef = useRef<ElementRef<typeof 组件名>>(null)
2.4.图片的获取
-
如果直接去获取原图片,由于图片过大消耗性能,所以可以封装一个getImageSize的工具函数
export function getImageSize( imageUrl: string, width: number, height: number = width ) { return imageUrl + `?param=${width}x${height}` }
这样在获取图片时就能获取一张缩放了的图片,优化了性能
2.5.同时发送多个网络请求,并保持返回顺序
-
在开发中有些数据放在一起会比较好处理,但是它们会有一定顺序,而服务器返回的数据顺序与发送的顺序无关,所以就要自定义一个promis数组来接收,并且调用它的all方法,既保证了都拿到了数据,也保证了顺序
const promises: Promise<any>[] = [] for (const id of rankingIds) { promises.push(getPlaylistDetail(id)) } Promise.all(promises).then((res) => { const playlists = res.map((item) => item.playlist) console.log(playlists) dispatch(changeRankingsAction(playlists)) })Promise后面接收的泛型是用来约束resolve(‘.....’),reject(‘....’)中的参数的