国内优秀企业网站欣赏,装修采购网,国产99做视频网站,wordpress前台发文积分文章目录 1、项目搭建1、创建项目1.2 配置项目1.2.1 更换icon1.2.2 更换项目名称1.2.1 配置项目别名 1.3 代码规范1.3.1 集成editorconfig配置1.3.2 使用prettier工具 1.4 项目结构1.5 对css进行重置1.6 注入router1.7 定义TS组件的规范1.8 创建代码片段1.9 二级路由和懒加载1.… 文章目录 1、项目搭建1、创建项目1.2 配置项目1.2.1 更换icon1.2.2 更换项目名称1.2.1 配置项目别名 1.3 代码规范1.3.1 集成editorconfig配置1.3.2 使用prettier工具 1.4 项目结构1.5 对css进行重置1.6 注入router1.7 定义TS组件的规范1.8 创建代码片段1.9 二级路由和懒加载1.10 redux-reduxtk1.10 axios的封装 1.11 类组件和TS的结合1.12 redux和ts的结合 1、项目搭建
1、创建项目
1、该项目使用的是ts创建的 所以需要加上--template typescript create-react-app kiki_ts_react_music --template typescript 2、整理项目结构 删除一些自己用不到的文件
1.2 配置项目
1.2.1 更换icon 1.2.2 更换项目名称
在index.html文件里面 1.2.1 配置项目别名
1、npm i -D craco/craco2、在根文件创建 craco.config.ts
const path require(path);
const CracoLessPlugin require(craco-less);// path.resolve返回当前文件的绝对路径 拼接dir
const resolve (dir) path.resolve(__dirname, dir);
module.exports {plugins: [{ plugin: CracoLessPlugin }],webpack: {alias: {: resolve(src),},},
};3、修改tsconfig.json baseUrl: .,paths: {/*: [src/*]}4、修改 package.json scripts: {start: craco start,build: craco build,test: craco test,eject: react-scripts eject},1.3 代码规范
1.3.1 集成editorconfig配置
EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。
1、在根目录下创建.editorconfig文件
# http://editorconfig.orgroot 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
**同时需要安装插件 **EditorConfig for VS Code 1.3.2 使用prettier工具
Prettier 是一款强大的代码格式化工具支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言基本上前端能用到的文件格式它都可以搞定是当下最流行的代码格式化工具。 1.安装prettier npm install prettier -D 2、配置.prettierrc文件在根目录下创建该文件
{useTabs: false,tabWidth: 2,printWidth: 80,singleQuote: true,trailingComma: none,semi: false
}
3、创建.prettierignore忽略文件 在根目录下
/dist/*
.local
.output.js
/node_modules/****/*.svg
**/*.sh/public/*
4、在package.json中配置一个scripts prettier: prettier --write .执行 npm run prettier就会将项目全部按照prettier的配置进行格式化
1.4 项目结构 1.5 对css进行重置 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)}}
} 3、配置自定义的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/mimeconst 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.lessconst root ReactDOM.createRoot(document.getElementById(root) as HTMLElement)
root.render(BrowserRouterApp //BrowserRouter
)
index.tsx
import React, { Suspense } from react
import { Link, useRoutes } from react-router-dom
import routes from ./routerfunction App() {return (div classNameAppheader classNameApp-headerh1hahah/h1Link to/discover发现音乐/LinkSuspense fallback正在加载{useRoutes(routes)}/Suspense/header/div)
}export default App
1.7 定义TS组件的规范
import React, { memo } from react
import type { ReactNode } from react// 定义传进来的props类型
interface IProps {// 在之前的版本props默认会有children是插槽 在后来取消了得自己写children?: ReactNodename?: stringage?: number
}const Download: React.FCIProps (props) {return (div{props.children}h1{props.age}/h1h1{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/downloadfunction App() {return (div classNameAppheader classNameApp-headerh1hahah/h1Link to/discover发现音乐/LinkDownload namekikih1我是downLoad的插槽/h1/DownloadSuspense fallback正在加载{useRoutes(routes)}/Suspense/header/div)
}export default App
1.8 创建代码片段
首选项设置代码片段react-ts
生成代码片段的网站 https://snippet-generator.app/?descriptiontabtriggersnippetmodevscode
import React, { memo } from react
import type { FC, ReactNode } from reactinterface IProps {children?: ReactNode
}const Template: FCIProps () {return divTemplate/div
}export default memo(Template)
1.9 二级路由和懒加载
discover页面
import React, { memo, Suspense } from react
import type { FC, ReactNode } from react
import { Outlet, Link } from react-router-dominterface IProps {children?: ReactNode
}const Discover: FCIProps () {return (divdivLink to/discover/recommend推荐/LinkLink to/discover/ranking排行榜/LinkLink to/discover/songs歌单/LinkLink to/discover/djradio主播电台/LinkLink to/discover/artist歌手/LinkLink to/discover/album新碟上架/Link/div{/* 二级路由也可以用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/downloadfunction App() {return (div classNameAppdiv classNamenavLink to/discover发现音乐/LinkLink to/mine我的音乐/LinkLink to/focus关注/LinkLink to/download下载客户端/Link/divSuspense fallback正在加载{useRoutes(routes)}/Suspensediv classNamemain/div/div)
}export default App 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 ./storeconst root ReactDOM.createRoot(document.getElementById(root) as HTMLElement)
root.render(Provider store{store}BrowserRouterApp //BrowserRouter/Provider
)
store/index.ts
import { configureStore } from reduxjs/toolkit
import { useSelector, useDispatch, TypedUseSelectorHook } from react-redux
import counterReducer from ./modules/counterconst store configureStore({reducer: {counter: counterReducer}
})// 获取函数的返回类型
type GetStateFnType typeof store.getState
// 获取函数返回类型的类型
type IRootState ReturnTypeGetStateFnType
type DispatchType typeof store.dispatchexport const useAppSelector: TypedUseSelectorHookIRootState useSelector
export const useAppDisPatch: () DispatchType useDispatchexport default store
store/count.ts
import { createSlice } from reduxjs/toolkitconst 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/counterinterface IProps {children?: ReactNode
}const Discover: FCIProps () {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 (divdiv{count}{message}button onClick{() changeMessage(修改message)}修改message/buttonLink to/discover/recommend推荐/LinkLink to/discover/ranking排行榜/LinkLink to/discover/songs歌单/LinkLink to/discover/djradio主播电台/LinkLink to/discover/artist歌手/LinkLink to/discover/album新碟上架/Link/div{/* 二级路由也可以用suspense */}Suspense fallback正在加载Outlet //Suspense/div)
}export default memo(Discover)
1.10 axios的封装 request/index.ts
import axios from axios
import type { AxiosInstance } from axios
import type { HYRequestConfig } from ./type// 拦截器: 蒙版Loading/token/修改配置/*** 两个难点:* 1.拦截器进行精细控制* 全局拦截器* 实例拦截器* 单次请求拦截器** 2.响应结果的类型处理(泛型)*/class HYRequest {instance: AxiosInstance// request实例 axios的实例constructor(config: any) {this.instance axios.create(config)// 每个instance实例都添加拦截器this.instance.interceptors.request.use((config) {// loading/tokenreturn config},(err) {return err})this.instance.interceptors.response.use((res) {return res.data},(err) {return err})// 针对特定的hyRequest实例添加拦截器this.instance.interceptors.request.use(config.interceptors?.requestSuccessFn,config.interceptors?.requestFailureFn)this.instance.interceptors.response.use(config.interceptors?.responseSuccessFn,config.interceptors?.responseFailureFn)}// 封装网络请求的方法// T IHomeDatarequestT any(config: HYRequestConfigT) {// 单次请求的成功拦截处理if (config.interceptors?.requestSuccessFn) {config config.interceptors.requestSuccessFn(config)}// 返回Promisereturn new PromiseT((resolve, reject) {this.instance.requestany, T(config).then((res) {// 单词响应的成功拦截处理if (config.interceptors?.responseSuccessFn) {res config.interceptors.responseSuccessFn(res)}resolve(res)}).catch((err) {reject(err)})})}getT any(config: HYRequestConfigT) {return this.request({ ...config, method: GET })}postT any(config: HYRequestConfigT) {return this.request({ ...config, method: POST })}deleteT any(config: HYRequestConfigT) {return this.request({ ...config, method: DELETE })}patchT any(config: HYRequestConfigT) {return this.request({ ...config, method: PATCH })}
}export default HYRequest
request/type.ts
import type { AxiosRequestConfig, AxiosResponse } from axios// 针对AxiosRequestConfig配置进行扩展
export interface HYInterceptorsT AxiosResponse {requestSuccessFn?: (config: AxiosRequestConfig) AxiosRequestConfigrequestFailureFn?: (err: any) anyresponseSuccessFn?: (res: T) TresponseFailureFn?: (err: any) any
}export interface HYRequestConfigT AxiosResponse extends AxiosRequestConfig {interceptors?: HYInterceptorsT
}
config/index.ts
import type { AxiosRequestConfig, AxiosResponse } from axios// 针对AxiosRequestConfig配置进行扩展
export interface HYInterceptorsT AxiosResponse {requestSuccessFn?: (config: AxiosRequestConfig) AxiosRequestConfigrequestFailureFn?: (err: any) anyresponseSuccessFn?: (res: T) TresponseFailureFn?: (err: any) any
}export interface HYRequestConfigT AxiosResponse extends AxiosRequestConfig {interceptors?: HYInterceptorsT
}
环境变量也可以通过配置文件 但是前面需要加上REACT_APP_… service/index.ts
import { BASE_URL, TIME_OUT } from ./config
import HYRequest from ./requestconst hyRequest new HYRequest({baseURL: BASE_URL,timeout: TIME_OUT,interceptors: {requestSuccessFn: (config: any) {return config}}
})export default hyRequest 使用的页面
import React, { memo, useEffect, useState } from react
import type { FC, ReactNode } from react
import hyRequest from /serviceinterface IProps {children?: ReactNode
}export interface IBannerData {imageUrl: stringtargetId: numbertargetType: numbertitleColor: stringtypeTitle: stringurl: stringexclusive: booleanscm: stringbannerBizType: string
}const Recommend: FCIProps () {const [banners, setBanners] useStateIBannerData[]([])// 测试网络请求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)
可以在这个页面自动生成类型定义 https://transform.tools/json-to-typescript
1.11 类组件和TS的结合
import React, { PureComponent } from react
/*** state:* props:*/interface IProps {name: stringage?: number
}interface IState {message: stringcounter: number
}class Demo02 extends PureComponentIProps, IState {name aaaastate {message: Hello World,counter: 99}// getSnapshotBeforeUpdate() {// return { address: 庐山 }// }// componentDidUpdate(// prevProps: ReadonlyIProps,// prevState: ReadonlyIState,// snapshot?: ISnapshot | undefined// ): void {}// constructor(props: IProps) {// super(props)// // this.state {// // message: Hello World,// // counter: 100// // }// }render(): React.ReactNode {return (divname: {this.props.name}age: {this.props.age}message: {this.state.message}counter: {this.state.counter}/div)}
}export default Demo02
1.12 redux和ts的结合
import { createSlice, PayloadAction } from reduxjs/toolkitinterface IState {count: numbermessage: stringaddress: stringheight: numberdirection: left | right | up | downnames: 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 }: PayloadActionstring) {state.message payload}}
})export const { changeMessageAction } counterSlice.actions
export default counterSlice.reducer