React+Redux+Ant Design+TypeScript 电子商务实战-客户端应用 01 基础配置

本文主要是介绍React+Redux+Ant Design+TypeScript 电子商务实战-客户端应用 01 基础配置,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

页面预览

该实战项目是(不怎么严谨的)电子商务网站。

首页

在这里插入图片描述

商品列表页面

在这里插入图片描述

登录页面

在这里插入图片描述

注册页面

在这里插入图片描述

购物车列表

在这里插入图片描述

支付完成页面

在这里插入图片描述

Dashboard 页面

普通用户页面

购买历史页面

在这里插入图片描述

资料更新页面

在这里插入图片描述

管理员页面

在这里插入图片描述

创建分类页面

在这里插入图片描述

创建商品页面

在这里插入图片描述

订单列表页面

显示所有用户的订单

在这里插入图片描述

客户端技术栈介绍

  • 脚本:TypeScript
  • 前端框架:React
  • 路由管理:react-router-dom
  • 用户界面:Ant Design
  • 全局状态管理:Redux
  • 一部状态更新:redux-saga
  • 路由状态同步:connected-react-router
  • 网络请求:Axios
  • 日期处理工具:Moment
  • 调试工具:redux-devtools-extension

创建客户端项目

创建使用 TypeScript 的项目

npx create-react-app ecommerce-front --template typescript
cd ecommerce-front
# 启动项目
npm start

安装项目依赖

npm i @types/react react-router-dom @types/react-router-dom antd redux react-redux @types/react-redux redux-saga connected-react-router axios redux-devtools-extension moment

引入 Antd 样式表

可以引入项目本地模块中的样式表文件,也可以从 CDN 平台引入。

本地文件方式

src/index.tsx 中引入

import 'antd/dist/antd.css'

CDN 方式

以 cdnjs 平台为例,修改 public/index.html,添加 link 标签:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/4.16.8/antd.min.css"  />

删除不需要的文件

├─ src
│   ├─ App.css
│   ├─ App.test.tsx
│   ├─ index.css
│   ├─ logo.svg
│   ├─ reportWebVitals.ts
│   └─ setupTests.ts

删除不需要的代码

// src\index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import '~antd/dist/antd.css'
import App from './App'ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root')
)
// src\App.tsx
function App() {return <div>App works</div>
}export default App

配置服务器端 API 请求地址

create-react-app 脚手架内置了 dotenv,允许开发者在 React 项目中配置环境变量,环境变量的名称要求以 REACT_APP_ 开头,在项目中通过 process.env.REACT_APP_<name>访问。

在项目根目录下新建 .env 文件:

# .env
# 以 `#` 开头的行被视为注释
# 生产环境的服务器端 API 地址 应该在 `npm run build` 构建项目时使用
REACT_APP_PRODUCTION_API_URL = http://xxx.com/api
# 开发环境的服务器端 API 地址 应该在 `npm start` 启动项目时使用
REACT_APP_DEVELOPMENT_API_URL = http://localhost/api

直接使用 process.env 访问 API 地址会将环境写死,为了使其根据环境决定使用哪个 API 地址,可以将 API 地址写入配置中。

新建 src/config.ts 文件:

// src\config.ts
export let API: stringif (process.env.NODE_ENV === 'development') {// 在值后添加 `!` 后缀断言值不会是 `null` 或 `undefined` 省略检查// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-API = process.env.REACT_APP_DEVELOPMENT_API_URL!
} else {API = process.env.REACT_APP_PRODUCTION_API_URL!
}

安装 chrome 扩展

安装扩展

在这里插入图片描述

  • React Developer Tools:检查 React 组件层次结构、props、hooks 等信息,在页面上显示 React 组件
  • Redux DevTools:监测 Store 中状态的变化

Chrome 网上应用店访问受限,可以使用 Microsoft Edge 浏览器,不需要翻墙。

React Developer Tools

在这里插入图片描述

Redux DevTools

在这里插入图片描述

配置 Redux DevTools

安装完扩展,还需要修改代码,在创建 store 时用 Redux DevTools 的 composeWithDevTools 包裹下 applyMiddleware,该方法来自安装的 redux-devtools-extension 模块。

import { composeWithDevTools } from 'redux-devtools-extension'export const store = createStore(rootReducer,composeWithDevTools(applyMiddleware(...middlewares))
)

页面组件初始化和路由初始化

创建文件和文件夹

文件命名规范建议(参考 Taro):

  • 普通 TS 文件以 .ts 作为后缀
  • 组件文件以 .tsx 作为后缀

src 目录下添加:

├─ components
│   ├─ admin # 存放登录后访问页面的文件夹
│   └─ core # 存放前台页面组件的文件夹
│       ├─ Layout.tsx # 布局组件
│       ├─ Home.tsx # 首页
│       └─ Shop.tsx # 商品列表页
└─ Routes.tsx # 路由组件

布局组件

// src\components\core\Layout.tsx
import React, { FC } from 'react'// 定义 Layout 组件参数类型的接口
interface Props {children: React.ReactNode
}// FC 表示函数型组件类型
const Layout: FC<Props> = ({ children }) => {return <div>Layout {children}</div>
}export default Layout

首页

// src\components\core\Home.tsx
import Layout from './Layout'const Home = () => {return <Layout>Home</Layout>
}export default Home

商品列表页

// src\components\core\Shop.tsx
import Layout from './Layout'const Shop = () => {return <Layout>Shop</Layout>
}export default Shop

路由组件

// src\Routes.tsx
import { HashRouter, Route, Switch } from 'react-router-dom'
import Home from './components/core/Home'
import Shop from './components/core/Shop'const Routes = () => {return (<HashRouter><Switch><Route path="/" component={Home} exact /><Route path="/shop" component={Shop} /></Switch></HashRouter>)
}export default Routes

修改入口文件

// src\index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import 'antd/dist/antd.css'
import Routes from './Routes'ReactDOM.render(<React.StrictMode><Routes /></React.StrictMode>,document.getElementById('root')
)

访问页面

npm start 运行,访问:

  • http://localhost:3000/
  • http://localhost:3000/#/shop

全局 Store 初始化

创建文件和文件夹

src 下添加:

├─ store
│   ├─ reducers
│   │   ├─ index.ts
│   │   └─ test.reducer.ts # 测试 reducer
│   └─ index.ts
// src\store\reducers\test.reducer.ts
export default function testReducer(state: number = 0) {return state
}
// src\store\reducers\index.ts
import { combineReducers } from 'redux'
import testReducer from './test.reducer'const rootReducer = combineReducers({test: testReducer
})export default rootReducer
// src\store\index.ts
import { createStore } from 'redux'
import rootReducer from './reducers'const store = createStore(rootReducer)export default store

注入全局

// src\index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import 'antd/dist/antd.css'
import Routes from './Routes'
import { Provider } from 'react-redux'
import store from './store'ReactDOM.render(<React.StrictMode><Provider store={store}><Routes /></Provider></React.StrictMode>,document.getElementById('root')
)

测试

// src\components\core\Home.tsx
import { useSelector } from 'react-redux'
import Layout from './Layout'const Home = () => {const state = useSelector(state => state)return <Layout>Home {JSON.stringify(state)}</Layout>
}export default Home

访问首页显示:Layout Home {"test":0}

将路由状态同步到全局 Store

connected-react-router 文档

connected-react-router 用于将路由状态同步到 Store。

第一步

在 Root Reducer 文件中,

  • 将 rootReducer 更改为创建 rootReducer 的函数(createRootReducer),将历史记录(history)作为参数接收。
  • 内部通过向 connectRouter 函数传递 history 实例对象创建 routerReducer ,并添加到返回的 rootReducer 中。
  • key 必须是 router
// reducers.js
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'// 导出的是一个创建 rootReducer 的函数
const createRootReducer = (history) => combineReducers({// connectRouter 返回一个 routerReducerrouter: connectRouter(history),// 其余的 reducers... // rest of your reducers
})
export default createRootReducer

第二步

当创建 Store 时,

  • 创建一个 history 实例对象,并导出
    • 通过调用 createBrowserHistory/createHashHistory 方法创建 history 实例对象
    • createBrowserHistory/createHashHistory 是 history 模块提供的 API。
    • history 模块是 react-router-dom (除了 React 本身)仅有的两个主要依赖项之一
      • 它提供了用于在 JavaScript 中各种环境下管理 history 的实现。
      • 官方文档:ReactTraining/history
      • React Router 文档:history
  • createRootReducer 函数传递 history 对象,创建的 rootReducer 传递给 createStore 方法
  • 配置用于派发 history actions 的中间件 routerMiddleware()
    • 该中间件通过调用 routerMiddleware 生成,方法来自 connected-react-router
    • 传递 history 对象
    • 中间件的作用是监听路由状态,当路由状态更改的时候 dispatch 一个 action
// configureStore.js
...
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { routerMiddleware } from 'connected-react-router'
import createRootReducer from './reducers'
...
export const history = createBrowserHistory()export default function configureStore(preloadedState) {const store = createStore(// 第一步编写的追加了 routerReducer 的 rootReducercreateRootReducer(history), // root reducer with router statepreloadedState,compose(applyMiddleware(// 用于派发历史记录操作的中间件routerMiddleware(history), // for dispatching history actions// ... 其它中间件 ...),),)return store
}

第三步

  • ConnectedRouter 组件包裹根组件,并将第二步创建的 history 对象传递给组件
    • 该组件用于让内部组件可以获取路由状态
  • 记得删除 BrowserRouterHashRouterNativeRouter
  • ConnectedRouter 组件作为 react-redux 的 Provider 子级放置
  • 注意:如果进行服务器端渲染,仍然应该使用 StaticRouter
// index.js
...
import { Provider } from 'react-redux'
import { Route, Switch } from 'react-router' // react-router v4/v5
import { ConnectedRouter } from 'connected-react-router'
import configureStore, { history } from './configureStore'
...
const store = configureStore(/* provide initial state if any */)ReactDOM.render(<Provider store={store}><ConnectedRouter history={history}> { /* place ConnectedRouter under Provider */ }<> { /* your usual react-router v4/v5 routing */ }<Switch><Route exact path="/" render={() => (<div>Match</div>)} /><Route render={() => (<div>Miss</div>)} /></Switch></></ConnectedRouter></Provider>,document.getElementById('react-root')
)

注意:提供给 routerReducer、routerMiddleware 和 ConnectedRouter 组件的 history 对象必须是同一个对象。

修改代码

第一步

// src\store\reducers\index.ts
import { connectRouter } from 'connected-react-router'
import { History } from 'history'
import { combineReducers } from 'redux'
// import testReducer from './test.reducer'// 定义一个包含 router 的 store 类型接口 供外部使用
export interface AppState {router: RouterState
}const createRootReducer = (history: History) =>combineReducers({// test: testReducer,router: connectRouter(history)})export default createRootReducer

第二步

// src\store\index.ts
import { applyMiddleware, createStore } from 'redux'
import createRootReducer from './reducers'
import { createHashHistory } from 'history'
import { routerMiddleware } from 'connected-react-router'export const history = createHashHistory()const store = createStore(createRootReducer(history), applyMiddleware(routerMiddleware(history)))export default store

第三步

// src\index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import 'antd/dist/antd.css'
import Routes from './Routes'
import { Provider } from 'react-redux'
import store, { history } from './store'
import { ConnectedRouter } from 'connected-react-router'ReactDOM.render(<React.StrictMode><Provider store={store}><ConnectedRouter history={history}><Routes /></ConnectedRouter></Provider></React.StrictMode>,document.getElementById('root')
)

测试

在 Shop 页面也输入 Redux 状态:

// src\components\core\Shop.tsx
import { useSelector } from 'react-redux'
import Layout from './Layout'const Shop = () => {const state = useSelector(state => state)return <Layout>Shop {JSON.stringify(state)}</Layout>
}export default Shop
# 访问 `http://localhost:3000/` 输出:
Layout Home {"test":0,"router":{"location":{"pathname":"/","search":"","hash":"","query":{}},"action":"POP"}}# 访问 `http://localhost:3000/#/shop?id=1` 输出:
Layout Shop {"test":0,"router":{"location":{"pathname":"/shop","search":"?id=1","hash":"","query":{"id":"1"}},"action":"POP"}}

配置 Redux DevTools 插件

Redux DevTools 插件需要使用composeWithDevTools 包裹 applyMiddleware

// src\store\index.ts
import { applyMiddleware, createStore } from 'redux'
import createRootReducer from './reducers'
import { createHashHistory } from 'history'
import { routerMiddleware } from 'connected-react-router'
import { composeWithDevTools } from 'redux-devtools-extension'export const history = createHashHistory()const store = createStore(createRootReducer(history), composeWithDevTools(applyMiddleware(routerMiddleware(history))))export default store

这篇关于React+Redux+Ant Design+TypeScript 电子商务实战-客户端应用 01 基础配置的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1097934

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

vite搭建vue3项目的搭建步骤

《vite搭建vue3项目的搭建步骤》本文主要介绍了vite搭建vue3项目的搭建步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1.确保Nodejs环境2.使用vite-cli工具3.进入项目安装依赖1.确保Nodejs环境

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

Nginx搭建前端本地预览环境的完整步骤教学

《Nginx搭建前端本地预览环境的完整步骤教学》这篇文章主要为大家详细介绍了Nginx搭建前端本地预览环境的完整步骤教学,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录项目目录结构核心配置文件:nginx.conf脚本化操作:nginx.shnpm 脚本集成总结:对前端的意义很多

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

mysql8.0.43使用InnoDB Cluster配置主从复制

《mysql8.0.43使用InnoDBCluster配置主从复制》本文主要介绍了mysql8.0.43使用InnoDBCluster配置主从复制,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录1、配置Hosts解析(所有服务器都要执行)2、安装mysql shell(所有服务器都要执行)3、

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v