I. Project foundation construction
-
1. Create a file and initialize it
npm init -y tsc --init touch .gitignore Copy the code
-
2. Install dependency packages
#Basic package npm install react react-dom @types/react @types/react-dom react-router-dom @types/react-router-dom #Install the package about WebPack npm install webpack webpack-cli webpack-dev-server html-webpack-plugin -D #Installed to handle TS npm i typescript ts-loader source-map-loader -D #Install redux related npm install redux react-redux @types/react-redux redux-thunk redux-logger @types/redux-logger redux-promise @types/redux-promise #Install libraries that route to redux connections npm install connected-react-router #Process-style npm install style-loader css-loader less-loader less -D #prefixed npm install autoprefixer postcss-loader -D #Processing picture address class npm install url-loader file-loader lib-flexible -D #Rem px conversion (see need to install, only for mobile sites) npm install px2rem-loader #The ajax request library npm install axios #React Multicast map component library npm install react-swipe @types/react-swipe #React Animation library npm install react-transition-group @types/react-transition-group Copy the code
-
Modify the tsconfig.json file
{ "compilerOptions": { "outDir": "./dist"./* Specifies the output directory */ "sourceMap": true./* When compiling ts files into js files, generate the corresponding sourceMap file */ "noImplicitAny": true./* If true, the ts compiler will still compile to js files when it cannot infer the type, but will */ "module": "commonjs"./ * * / "target": "es5"./* to es5*/ "jsx": "react"."esModuleInterop": true."baseUrl": ".".// Find the starting position of modules that are not relative paths "paths": { // Define an alias "@ / *": [ "src/*"]}},"include": [ /* The file to compile */ "./src/**/*"]}Copy the code
-
4. Configure the WebPack file
Create a webpack.config.js file under the webpack.config.js project
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'.entry: './src/index.tsx'.output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js',},devtool: 'source-map'.resolve: { // Define an alias alias: { The '@': path.resolve(__dirname, 'src'), '~': path.resolve(__dirname, 'node_modules')},// When you load a file with no specified extension, it automatically looks for which extension extensions: ['.ts'.'.tsx'.'.js'.'.json']},module: { rules: [{test: /\.(j|t)sx? /, loader: 'ts-loader'.options: { transpileOnly: true.// Only compile without checking compilerOptions: { module: 'es2015'}}}, {test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader'.options: { importLoaders: 0}},/ / { // loader: 'postcss-loader', // options: { // plugins: [require('autoprefixer')] / /} / /}] {},test: /\.less$/, use: [ 'style-loader', { loader: 'css-loader'.options: { importLoaders: 0}},/ / { // loader: 'postcss-loader', // options: { // plugins: [require('autoprefixer')] / /} / /}, // If it is a mobile phone, configure it / / { // loader: 'px2remote-loader', // options: { // remUnit: 75, // Base size // Precesion: 8 // accurate to how many digits / /} // }, 'less-loader',]},// Handle image classes { test: /\.(jpg|png|gif|svg|jpeg)$/, use: ['url-loader']]}},plugins: [ new HtmlWebpackPlugin({ template: './src/index.html',})./ / hot update new webpack.HotModuleReplacementPlugin(), ], devServer: { hot: true.contentBase: path.join(__dirname, 'dist'), open: false.port: 3000.historyApiFallback: { // When browserHistory is refreshed, 404 is automatically redirected to index.html index: './index.html'}}}Copy the code
-
Create a SRC directory and create two files index. HTML and index. TSX
-
6. Configure the startup command in package.json
{..."scripts": { "dev": "webpack-dev-server"."build": "webpack"}},Copy the code
-
7. Write the react code in the SRC /index.tsx file
import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <div>I created it using React</div>.document.getElementById('root'))Copy the code
-
8. Write a basic container in SRC /index.html
<! DOCTYPEhtml> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="Width = device - width, initial - scale = 1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Manually set up the React project</title> </head> <body> <div id="root"></div> </body> </html> Copy the code
-
9, if you are a mobile website to add adaptation (see the project needs to write)
<! DOCTYPEhtml> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="Width = device - width, initial - scale = 1.0"> <title>Manually set up the React project</title> </head> <body> <script> const docEl = document.documentElement; function setRemUtils() { docEl.style.fontSize = docEl.clientWidth / 10 + 'px'; } setRemUtils(); window.addEventListener('resize', setRemUtils, false); </script> <div id="root"></div> </body> </html> Copy the code
-
10. Start the project
Second,react
In combination withredux
Used together
-
Create a store folder under SRC
-
2. General directory structure
➜ store tree. ├ ─ ─ action - types. Ts # define constants ├ ─ ─ the actions # a component corresponding to an action ├ ─ ─ index. The ts └ ─ ─ reducers # a component corresponding to a reducer └ ─ ─ index.ts 2 directories, 3 filesCopy the code
-
3. In the store/index.ts file
import { createStore, applyMiddleware } from 'redux'; import logger from 'redux-logger'; import promise from 'redux-promise'; import thunk from 'redux-thunk'; import rootReducer from './reducers'; const store = applyMiddleware( promise, thunk, logger)(createStore)(rootReducer); // Mount to window for easy viewing (<any>window).store = store; export default store; Copy the code
-
4. The Store /reducers/index.ts file is used to integrate reducer in all components
import { ReducersMapObject, Reducer, combineReducers, AnyAction } from 'redux' The reducer state type of each component can be put into a separate reducer file export interface CombinedState { } const reducers: ReducersMapObject<CombinedState, AnyAction> = { Reducer of each component }; const rootReducers: Reducer<CombinedState, any> = combineReducers(reducers); export default rootReducers; Copy the code
-
5, use store in SRC /index.tsx
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; ReactDOM.render( <Provider store={store}> <div>I created it using React</div> </Provider> , document.getElementById('root'))Copy the code
Three, test,store
Check whether the configuration is successful
-
1. Define two constants in action-types.ts
export const ADD_COUNT = 'ADD_COUNT'; export const MINUS_COUNT = 'MINUS_COUNT'; Copy the code
-
Create a reducer file from SRC /store.counter. Ts
import { AnyAction } from 'redux'; import * as types from '. /.. /action-types'; export interface Counter { count: number } const initState: Counter = { count: 0};export default function (state: Counter = initState, action: AnyAction) { switch (action.type) { case types.ADD_COUNT: return { ...state, count: state.count + 1 }; case types.MINUS_COUNT: return { ...state, count: state.count - 1 }; default: returnstate; }}Copy the code
-
3, in the SRC/store/reducers/index. The ts reducer for the introduction of the component
.import counter, { Counter } from './counter'; // State type of reducer of each component export interface CombinedState { counter: Counter, } const reducers: ReducersMapObject<CombinedState, AnyAction> = { Reducer of each componentcounter, }; .Copy the code
-
4. Define actions files
import * as types from '. /.. /action-types'; export default { // Add method // Payload Specifies the parameter that triggers the function addCount(payload: number) { console.log('I'm the payload that comes in.', payload); return { type: types.ADD_COUNT, payload, } }, // The reduction method minusCount(payload: number) { return { type: types.MINUS_COUNT, payload, } } } Copy the code
-
Create a component components/ counter.tsx
import React, { PropsWithChildren } from 'react'; import { connect } from 'react-redux'; import action from '@/store/actions/counter'; type Props = PropsWithChildren<ReturnType<typeof mapStateToProps> & typeof action>; // Define an arbitrary one type State = { [propsName: string] :any } class Counter extends React.Component<Props.State> { render() { console.log(this.props); return ( <> <button onClick={this.props.minusCount}>-</button>{this.props. Count} {/* If you want to pass the payload, you need to write */}<button onClick={()= > this.props.addCount(10)}>+</button> </>)}}const mapStateToProps = (state: any) = > (state.counter) export default connect( mapStateToProps, action, )(Counter); Copy the code
-
6. Use this component in SRC /index.tsx and test it successfully
4. Browser configurationRedux DevTools
Observe changes in the state
-
1. Install Redux DevTools for Google Chrome.
-
2. Redux-devtools-extension
-
SRC /store/index.ts
- Direct use of
.import { composeWithDevTools } from 'redux-devtools-extension'; // Write it the other way conststore = createStore(rootReducer, composeWithDevTools(applyMiddleware(promise, thunk, logger))); .Copy the code
- Distinguish between development environment and production environment
// The development environment uses tools and logging middleware const enhancers = process.env.NODE_ENV === "development" ? composeWithDevTools( applyMiddleware(promise, thunk, logger) ) : applyMiddleware(promise, thunk); const store = createStore(rootReducer, enhancers); Copy the code
-
4. Operation effect drawing
Optimize code, definitionstore
Type constraint of
-
1. Create a folder SRC /typings in your project
-
2. Create a file typings/counter. Ts to constrain the counter
export interface CounterState { count: number } Copy the code
-
3, create a typings/state.ts file
import { CounterState } from '. '; // State type of reducer of each component export interface CombinedState { counter: CounterState, } export interface CounterPayload { count: number,}Copy the code
-
4, streamline store/reducers/counter. Ts code
import { AnyAction } from 'redux'; import * as types from '. /.. /action-types'; import { CounterState } from '@/typings'; const initState: CounterState = { count: 0};export default function (state: CounterState = initState, action: AnyAction) {... }Copy the code
-
5. Simplify store/reducers/index.ts code
import { ReducersMapObject, Reducer, combineReducers, AnyAction } from 'redux' import counter from './counter'; import { CombinedState } from '@/typings'; const reducers: ReducersMapObject<CombinedState, AnyAction> = { Reducer of each component counter, }; const rootReducers: Reducer<CombinedState, any> = combineReducers(reducers); export default rootReducers; Copy the code
-
6. Optimize store/actions/counter
import * as types from '. /.. /action-types'; import { CounterPayload } from '@/typings'; export default { // Add method // Payload Specifies the parameter that triggers the function addCount(payload: CounterPayload) { console.log('I'm the payload that comes in.', payload); return { type: types.ADD_COUNT, payload, } }, // The reduction method minusCount() { // If you need to pass parameters, add payload return { type: types.MINUS_COUNT, } } } Copy the code
-
You can also use extracted type constraints in components
import React, { PropsWithChildren } from 'react'; import { connect } from 'react-redux'; import action from '@/store/actions/counter'; import { CombinedState } from '@/typings'; type Props = PropsWithChildren<ReturnType<typeof mapStateToProps> & typeof action>; // Define an arbitrary one type State = { [propsName: string] :any } class Counter extends React.Component<Props.State> { render(){... }}// Use type constraints const mapStateToProps = (state: CombinedState) = > (state.counter) export default connect( mapStateToProps, action, )(Counter) Copy the code
Six, the use ofredux-thunk
andaxios
Use of requests
-
1. General process
- Page click button, send one
action
- in
action
Is initiated after receiving the eventajax
request - in
ajax
In the request data, determine whether to send it according to the conditiondispatch
(Note that this returns a function that uses immediate execution functions.) - in
reducer
Middle processing justaction
distributedtypes
Save the data tostate
In the - Use just in components
state
The data in the
- Page click button, send one
-
2. Create an utils folder in the SRC folder of your project
// In real business, it may be more concrete, but now it is just a simple encapsulation import axios, { AxiosRequestConfig } from 'axios'; axios.defaults.baseURL = 'http://test.dancebox.cn/api/v1'; axios.defaults.headers.post['Content-Type'] = 'application/json; charset=UTF-8'; axios.interceptors.request.use((config: AxiosRequestConfig) = > { let access_token = sessionStorage.getItem('access_token'); if (access_token) config.headers['Authorization'] = `Bearer ${access_token}`; return config; }, (error: any) = > Promise.reject(error)); // Response interceptor puts AxiosResponse=> axiosResponse.data axios.interceptors.response.use(response= > response.data, error= > Promise.reject(error)); export { axios }; Copy the code
-
Create an API in the project’s SRC directory to hold the data request methods
import https from '@/utils/https'; // Define the request interface export const activityList = () = > { return https.get('/front/activity'); } Copy the code
-
4. Define two data types in reducers/index.ts
.export type StoreDispatch = Dispatch; export type StoreGetState = () = > CombinedState; export default rootReducers; Copy the code
-
5. Request activity data in actions/counter. Ts
import * as types from '. /.. /action-types'; import { CounterPayload } from '@/typings'; import { activityList } from '@/api'; import { StoreDispatch, StoreGetState } from '.. /reducers'; export default{...// Return data using Ajax requests, because we use redux-thunk, reducer can return a function activityList() { return function (dispatch: StoreDispatch, getState: StoreGetState) { // To use async, use the immediate function (async function () { const response: { [propsName: string] :any } = await activityList(); const { code, message, result } = response; if (Object.is(code, 0)) { dispatch({ type: types.GET_ACTIVITY_DATA, payload: result, }) } else { console.log('Failed to get data', message); }}) (); }}}Copy the code
-
6, add active SRC /typings/counter
export interface CounterState { count: number.activityListData: any[].// Add data to the active list } Copy the code
-
7. Handle reducers/ Counter. Ts and save the data of Dispath in actions into state
import { AnyAction } from 'redux'; import * as types from '. /.. /action-types'; import { CounterState } from '@/typings'; const initState: CounterState = { count: 0.activityListData: [],};export default function (state: CounterState = initState, action: AnyAction) { switch (action.type) { ... case types.GET_ACTIVITY_DATA: // Get the requested data and put it in store console.log(action.payload); return { ...state, activityListData: action.payload.data } default: returnstate; }}Copy the code
-
8. Use in components
import React, { PropsWithChildren } from 'react'; import { connect } from 'react-redux'; import action from '@/store/actions/counter'; import { CombinedState } from '@/typings'; type Props = PropsWithChildren<ReturnType<typeof mapStateToProps> & typeof action>; // Define an arbitrary one type State = { [propsName: string] :any } class Counter extends React.Component<Props.State> { render() { console.log(this.props); return ( <> <button onClick={this.props.activityList}>Get active data</button> <ul> { this.props.activityListData.map(item => { return ( <li key={item.id}>{item.title}</li>)})}</ul> </>)}}const mapStateToProps = (state: CombinedState) = > (state.counter) export default connect( mapStateToProps, action, )(Counter) Copy the code
Seven, configuration,antd
The front endUI
library
-
1. Install dependency packages
npm install antd npm install ts-import-plugin Copy the code
-
2. Modify the webpack.config.js configuration
const tsImportPluginFactory = require('ts-import-plugin'); module.exports = { ... module: { rules: [{test: /\.(j|t)sx? /, loader: 'ts-loader'.options: { transpileOnly: true.// Only compile without checking getCustomTransformers: () = > ({ // Get or define a custom converter before: [tsImportPluginFactory({ 'libraryName': 'antd'.// Which module to load on demand 'libraryDirectory': 'es'.// The on-demand module, if implemented on demand, must be ES Modules 'style': 'css' // Automatically import its corresponding CSS}})]),compilerOptions: { module: 'es2015'}}},]},... }Copy the code
-
3. Use in components
import React, { PropsWithChildren } from 'react'; import { connect } from 'react-redux'; import { Button } from 'antd'; import action from '@/store/actions/counter'; import { CombinedState } from '@/typings'; type Props = PropsWithChildren<ReturnType<typeof mapStateToProps> & typeof action>; // Define an arbitrary one type State = { [propsName: string] :any } class Counter extends React.Component<Props.State> { render() { console.log(this.props); return ( <>.<Button type="primary" onClick={this.props.activityList}>Get active data</Button> <ul> { this.props.activityListData.map(item => { return ( <li key={item.id}>{item.title}</li>)})}</ul> </>)}}const mapStateToProps = (state: CombinedState) = > (state.counter) export default connect( mapStateToProps, action, )(Counter) Copy the code
-
SRC /index.tsx
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { ConfigProvider } from 'antd' import zh_CN from 'antd/lib/locale-provider/zh_CN'; import store from './store'; import Counter from '@/components/Counter'; // Global style import './assets/style/common.less'; ReactDOM.render( <Provider store={store}> <ConfigProvider locale={zh_CN}> <Counter /> </ConfigProvider> </Provider>.document.getElementById('root'))Copy the code