在上一篇文章中,我介绍了如何使用express+mongodb构建后端项目 ,如今我再来跟你们介绍如何使用react+typescript+antd来构建前端项目css
react
typescript
antd
react-router-dom
react-redux
canvas
ES6
cookiehtml
assets——存放静态资源
components——存放公共组件
pages——存放页面
store——存放状态管理数据流文件
utils——存放函数工具
api.ts——定义api接口文件
App.css——定义全局css
App.ts——定义项目入口组件
global.d.ts——定义全局的声明文件
index.tsx——入口文件
router.ts——路由配置表前端
npm install -g create-react-app
npx create-react-app antd-demo-ts --typescript
cd react-antd-ts
npm start
此时浏览器会访问 http://localhost:3000/ ,看到 Welcome to React 的界面就算成功了react
npm install antd --save
此时咱们须要对 create-react-app 的默认配置进行自定义,这里咱们使用 react-app-rewired (一个对 create-react-app 进行自定义配置的社区解决方案,而不须要eject暴露react的webpack配置文件)。webpack
引入 react-app-rewired 并修改 package.json 里的启动配置。因为新的 react-app-rewired@2.x 版本的关系,你还须要安装 customize-cra。ios
npm install react-app-rewired customize-cra --save
修改package.jsongit
"scripts": { - "start": "react-scripts start", + "start": "react-app-rewired start", - "build": "react-scripts build", + "build": "react-app-rewired build", - "test": "react-scripts test", + "test": "react-app-rewired test", }
babel-plugin-import 是一个用于按需加载组件代码和样式的 babel 插件(原理),如今咱们尝试安装它并修改 config-overrides.js 文件。github
npm install babel-plugin-import --save
而后在项目根目录建立一个 config-overrides.js 用于修改默认配置
config-overrides.jsweb
const { override, fixBabelImports } = require('customize-cra'); module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css', }), );
若是你试一下在现有项目上修改代码,发现整个页面都会刷新,这并非咱们想要的,若是想局部更新,咱们可使用react-hot-loader来实现mongodb
npm install --save react-hot-loader
修改App.tsx文件:
App.tsx
import React from 'react'; import logo from './logo.svg'; import { hot } from 'react-hot-loader/root' import './App.css'; const App: React.FC = () => { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default process.env.NODE_ENV === "development" ? hot(App) : App
npm install --save less-loader less
修改config-overrides.js:
const { override, fixBabelImports,addLessLoader,addPostcssPlugins } = require('customize-cra'); const rewireReactHotLoader = require('react-app-rewire-hot-loader-for-customize-cra') module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css', }), addLessLoader({ strictMath: true, noIeCompat: true, localIdentName: '[local]--[hash:base64:5]' // if you use CSS Modules, and custom `localIdentName`, default is '[local]--[hash:base64:5]'. }), addPostcssPlugins([require('autoprefixer')]), rewireReactHotLoader() );
致此,能够引用antd的组件了;注意:若是在构建的时候出现 import React from 'react' 的报错,能够npm install @types/react进行解决;这里给你们科普一下typescript里面的声明文件知识,若是引入的模块提示没有找到声明文件,通常能够用npm install @types/xxxx(模块名称)来解决。关于引入模块声明文件的相关知识,能够看看如下两篇文章:
https://www.tslang.cn/docs/ha...
https://www.tslang.cn/docs/ha...
(为节省篇幅,如下只讲一些重点以及一些注意事项,因此会将一些已经写好的组件和页面放到项目中,再也不叙述这些页面跟组件的构建,最后会附上项目的git地址,能够自行参考)
单页应用中的重要部分,就是路由系统。因为不一样普通的页面跳转刷新,所以单页应用会有一套本身的路由系统须要维护。
咱们固然能够手写一个路由系统,可是,为了快速有效地建立于管理咱们的应用,咱们能够选择一个好用的路由系统。本文选择了react-router 4。这里须要注意,在v4版本里,react-router将WEB部分的路由系统拆分至了react-router-dom,所以须要npmreact-router-dom
npm i --save react-router-dom
本例中咱们使用react-router中的BrowserRouter组件包裹整个App应用,在其中使用Route组件用于匹配不一样的路由时加载不一样的页面组件。(也可使用HashRouter,顾名思义,是使用hash来做为路径)react-router推荐使用BrowserRouter,BrowserRouter须要history相关的API支持。
首先,须要在App.tsx中添加BrowserRouter组件,并将Route组件放在BrowserRouter内。其中Route组件接收两个属性:path和component,分别是匹配的路径与加载渲染的组件,把App.tsx修改以下:
App.tsx
import React from 'react'; import {BrowserRouter,Route,Switch} from 'react-router-dom'; import {store} from './store'; import {Provider} from 'react-redux'; import Index from './components/Index'; import Login from './pages/login'; import Err from './pages/err'; import './App.css'; import { hot } from 'react-hot-loader/root' import moment from 'moment'; import 'moment/locale/zh-cn'; moment.locale('zh-cn'); const App: React.FC = () => { return ( <Provider store={store()}> <BrowserRouter> <Switch> <Route path="/login" exact component={Login}></Route> <Route path="/err" exact component={Err}></Route> <Route path='/' component={Index}/> </Switch> </BrowserRouter> </Provider> ); } export default process.env.NODE_ENV === "development" ? hot(App) : App
redux是flux架构的一种实现,redux并非彻底依附于react的框架,实际上redux是能够和任何UI层框架相结合的。所以,为了更好得结合redux与react,对redux-flow中的store有一个更好的全局性管理,咱们还须要使用react-redux
npm install --save redux
npm install --save react-redux
同时,为了更好地建立action和reducer,咱们还会在项目中引入redux-actions:一个针对redux的一个FSA工具箱,能够相应简化与标准化action与reducer部分。固然,这是可选的
npm install --save redux-actions
注意:若是出现如下状况,上面有提到,须要 npm install @types/react-redux
来解决
一、建立对应的action。
action是一个object类型,对于action的结构有Flux有相关的标准化建议FSA
一个action必需要包含type属性,同时它还有三个可选属性error、payload和meta。
(1) type属性至关因而action的标识,经过它能够区分不一样的action,其类型只能是字符串常量或Symbol。
(2) payload属性是可选的,可使任何类型。payload能够用来装载数据;在error为true的时候,payload通常是用来装载错误信息。
(3) error属性是可选的,通常当出现错误时其值为true;若是是其余值,不被理解为出现错误。
(4) meta属性可使任何类型,它通常会包括一些不适合在payload中放置的数据。
使用redux-actions对actions进行建立与管理
(1) createAction(type, payloadCreator = Identity, ?metaCreator)
(2) createAction至关于对action建立器的一个包装,会返回一个FSA,使用这个返回的FSA能够建立具体的action。
(3) payloadCreator是一个function,处理并返回须要的payload;若是空缺,会使用默认方法。若是传入一个Error对象则会自动将action的error属性设为true
如下以全局控制侧边栏的收开为例,建立action和reducer
在根目录下新建store文件夹,建立types.ts文件,用于存放action 的惟一标识符
store/types.ts
export const TOGGLE_SIDE_BAR = 'TOGGLE_SIDE_BAR';
建立models.ts,用于存放store中初始状态的接口声明
store/models.ts
export interface AppModel { collapsed:boolean; }
建立actions.ts,用于对全部action进行管理
store/actions.ts
import {createAction} from 'redux-actions'; import {AppModel} from './models'; import {TOGGLE_SIDE_BAR} from './types'; const toggleSideBar = createAction<AppModel, boolean>( TOGGLE_SIDE_BAR, (collapsed: boolean) => {return {collapsed}} ); export { toggleSideBar }
建立reducers.ts
store/reducers.ts
import {handleActions} from 'redux-actions'; import {TOGGLE_SIDE_BAR} from './types'; import {AppModel} from './models' // 初始的状态,就像react中组件内的初始状态,只不过这个是全局的。 const initialState: AppModel = { collapsed:false }; export const toggleSideBarReducer = handleActions<AppModel>({ [TOGGLE_SIDE_BAR]: (state:any, action:any) => { return { ...state, collapsed: !state.collapsed } } }, initialState);
建立index.ts,用于组合store
store/index.ts
import { AppModel } from './models'; import { combineReducers } from 'redux'; import { toggleSideBarReducer } from './reducers'; import { Store, createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; // import {History} from 'history'; interface RootState { app: AppModel } const rootReducer = combineReducers({ app: toggleSideBarReducer }); export function store(initialState?:any): Store<RootState> { // store 中间件,根据我的需求添加 const middleware = applyMiddleware(thunkMiddleware) return createStore( rootReducer, initialState, middleware ) as Store<RootState>; }
一、在package.json里面加入
"proxy": "http://localhost:3001",
二、建立api.ts,对全局接口请求进行定义
import http from './utils/http'; const getUserList=()=>{ return http.get('/getUserList') } const register=(params:{name:string,password:string,phone:number,type:number})=>{ return http.post('/register',params) } const login=(params:{password:string,phone:number})=>{ return http.post('/login',params) } const logout=()=>{ return http.post('/logout') } const userInfo=()=>{ return http.post('/userInfo') } export { getUserList, register, login, logout, userInfo }
三、建立utils文件夹,新建http.ts文件,对axios进行全局设置
npm install --save axios
utils/http.ts
import qs from 'qs' import { message } from 'antd'; import axios,{ AxiosResponse, AxiosRequestConfig } from 'axios' import { Modal } from 'antd'; import {createBrowserHistory} from 'history'; const axiosConfig: AxiosRequestConfig = { // 请求后的数据处理 transformResponse: [(data: AxiosResponse) => { return data }], transformRequest: [(data: any) => { return qs.stringify(data) }], // 超时设置s timeout: 30000, // 跨域是否带Token withCredentials: true, responseType: 'json', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } // 取消重复请求 const pending: Array<{ url: string, cancel: any }> = [] const cancelToken = axios.CancelToken const removePending = (config: any) => { // tslint:disable-next-line:forin for (const p in pending) { const item: any = p const list: any = pending[p] // 当前请求在数组中存在时执行函数体 if (list.url === config.url + '&request_type=' + config.method) { // 执行取消操做 list.cancel() // 从数组中移除记录 pending.splice(item, 1) } } } const service = axios.create(axiosConfig) // 添加请求拦截器 service.interceptors.request.use( (config: any) => { removePending(config) config.cancelToken = new cancelToken((c: any) => { pending.push({ url: config.url + '&request_type=' + config.method, cancel: c }) }) return config }, (error: any) => { return Promise.reject(error) } ) // 返回状态判断(添加响应拦截器) service.interceptors.response.use( (res: any) => { removePending(res.config) if (res.data) { // LoadingInterface.close(); if (res.status === 200) { if (res.data.status === 200) { return res.data.data } else if (res.data.status === 403) { // 未登陆或者token过时,重定向到登陆页面 Modal.info({ title: '通知', content:'登陆信息已过时,请从新登陆', onOk(){ const history = createBrowserHistory() history.push('/login') window.location.reload() } }) } else { message.error(res.data.message) return Promise.reject(res.data.message) } } else { message.error( res.statusText ) return Promise.reject(res.statusText) } } }, (error: any) => { message.error('请求失败,请稍后再试') return Promise.reject(error) } ) export default service
好嘞,基本的东西就介绍到这里了,完整的项目能够到个人git上去下载;
后端git地址:https://github.com/SuperMrBea...
前端git地址:https://github.com/SuperMrBea...
项目在线预览地址:http://www.wxdriver.com (帐号:15900000000 密码:123456)
上一篇文章的地址:https://segmentfault.com/a/11...