Don’t use scaffolding structures, the React project: https://github.com/zhuyuanmin/react-0-1-build. Readers can build step by step according to the submitted branch order, all libraries are using the latest version, let us grow in the pit! The Typescript-React branch contains the full version of typescript.
I. Project launch
-
Understand the background of requirements
-
Understand the business process
Ii. Initialization of project construction
This example uses the scaffolding create-react-app to initialize the project. This scaffold has advantages and disadvantages, the project directory structure is simple, do not need to care too much about the troublesome configuration of Webpack; The downside is that scaffolding can be a bit bulky, taking about 4 minutes to build. You can choose, or you can build your own project.
-
Set up taobao mirror warehouse
$ yarn config set registry registry.npm.taobao.org/ -g
$yarn config set sass_binary_site cdn.npm.taobao.org/dist/node-s… -g
-
Project directory init
$ create-react-app qpj-web-pc –typescript
$ tree -I “node_modules”
. |-- README.md |-- package.json |-- public | |-- favicon.ico | |-- index.html | |-- logo192.png | |-- logo512.png | |-- manifest.json | `-- robots.txt |-- src | |-- App.css | |-- App.test.tsx | |-- App.tsx | |-- index.css | |-- index.tsx | |-- logo.svg | |-- react-app-env.d.ts | |-- reportWebVitals.ts | `-- setupTests.ts `-- tsconfig.jsonCopy the code
-
Yarn build try
$ yarn build & tree -I “node_modules”
. | - README. | - build/md # transformation point (due to ` Jenkins ` pack build script may have written dead ` dist ` package name) | -- package. Json | - public | | -- the favicon. Ico | |-- index.html | |-- logo192.png | |-- logo512.png | |-- manifest.json | `-- robots.txt |-- src | |-- App.css | |-- App.test.tsx | |-- App.tsx | |-- index.css | |-- index.tsx | |-- logo.svg | |-- react-app-env.d.ts | |-- reportWebVitals.ts | `-- setupTests.ts `-- tsconfig.jsonCopy the code
-
Connect to a Git remote repository
$git remote add origin zhu%40wetax.com:[email protected]/front/qpj-w…
-
Add the gitignore
$ echo -e ” yarn.lock \n package-lock.json \n /dist \n .idea” >> .gitignore
-
Add ESLint code and submit comments for validation
$ yarn add husky lint-staged @commitlint/cli @commitlint/config-conventional -D
$ npx husky install
$ npx husky add .husky/pre-commit “npx lint-staged”
$ npx husky add .husky/prepare-commit-msg “npx commitlint -e”
-
Create new commitlint.config.js in the project root directory
// commitlint.config.js module.exports = { extends: ['@commitlint/config-conventional'].rules: { 'type-enum': [ 2.'always'['feat'.'fix'.'docs'.'style'.'refactor'.'test'.'chore'.'revert']],'subject-full-stop': [0.'never'].'subject-case': [0.'never',}}Copy the code
-
Search ESLint vscode extension and installation, project root directory. The new eslintrc. Js, content may refer to article configuration: zhuanlan.zhihu.com/p/84329603 the fifth
-
Commit Message format description
<type>: <subject>
-
The type values are enumerated as follows:
- Feat: Add a new feature
- Fix: fix the bug
- Docs: Only modified documents
- Style: Just changed Spaces, formatting indentation, all good, etc., without changing the code logic
- Refactor: Code refactored without adding new features or fixing bugs
- Perf: Add code for performance testing
- Test: Adds test cases
- Chore: Change the build process, or add dependency libraries, tools, etc
- Revert: Current COMMIT Used to undo a previous COMMIT
-
Subject is a short description of the commit purpose, no more than 50 characters long, and without a period (.).
-
-
Package. json adds the following configuration:
{... ."lint-staged": { "src/**/*.{jsx,txs,ts,js,json,css,md}": [ "eslint --quiet"]}}Copy the code
-
NPX esLint [filePath] –fix NPX eslint [filePath] –fix NPX eslint [filePath] –fix
-
Iii. Project Configuration I (Function Configuration)
-
Install the project’s common dependency libraries
$YARN add ANTD axios dayjs QS -s # UI library
$yarn add react-router-dom redux redux redux-logger redux-thunk-s # Route and state management
-
Webpack configuration extensions are necessary
- Root Directory Creation
config-overrides.js
For detailed use, please visit:React config-overrides file configuration - The installation
-
$ yarn add react-app-rewired customize-cra -D
-
- Modify the
package.json
In the startup// package.json "scripts": { "start": "react-app-rewired start"."build": "react-app-rewired build",}Copy the code
- use
// config-overrides.js const { override, / / the main function fixBabelImports, // The configuration is loaded on demand addWebpackExternals, // Do not configure packaging processing addWebpackAlias, // Configure the alias addLessLoader // lessLoader configuration, you can change the theme color, etc } = require('customize-cra') module.exports = override(/ *... * /.config= > config) Copy the code
- Root Directory Creation
-
The configuration is loaded on demand
// config-overrides.js.module.exports = override( fixBabelImports('import', { libraryName: 'antd'.libraryDirectory: 'es'./ / the library catalog style: true.// Automatically package related styles}),)Copy the code
-
Change the theme color
// config-overrides.js.module.exports = override( addLessLoader({ lessOptions: { javascriptEnabled: true.modifyVars: { '@primary-color': '#1890ff',},}}),)Copy the code
-
Alias configuration (typescript projects have pits here)
// config-overrides.js const path = require('path')...module.exports = override( addWebpackAlias({ The '@': path.resolve(__dirname, 'src'),}),)Copy the code
-
Remove comments, multi-process packaging compression
// config-overrides.js const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')...module.exports = override(/ *... * /.config= > { config.plugins = [...config.plugins, { new UglifyJsPlugin({ uglifyOptions: { warnings: false.compress: { drop_debugger: true.drop_console: true,}}}),new HardSourceWebpackPlugin() }] return config }) Copy the code
-
Solve the two holes
- Change the name of the packaged folder
dist
// Change the package path as well as output const paths = require('react-scripts/config/paths') paths.appBuild = path.join(path.dirname(paths.appBuild), 'dist') module.exports = override(/ *... * /.config= > { config.output.path = path.resolve(__dirname, 'dist') return config }) Copy the code
- To solve
typescript
The alias configuration- For relevant information, please visit
tsconfig.json
To add a configuration{..."extends": "./paths.json" } Copy the code
- The new file
paths.json
{ "compilerOptions": { "baseUrl": "src"."paths": { "@ / *": ["*"]}}}Copy the code
- For relevant information, please visit
- Change the name of the packaged folder
-
Configure the decorator writing method
{ "compilerOptions": { "experimentalDecorators": true. }}Copy the code
-
Configuring the development agent
- in
src
New directorysetupProxy.js
// src/setupProxy.js const proxy = require('http-proxy-middleware').createProxyMiddleware module.exports = function(app) { // App is an instance of Express where Mock data can be written app.use( proxy('/api', { "target": "https://qpj-test.fapiaoer.cn"."changeOrigin": true."secure": false.// "pathRewrite": { // "^/api": "" // }}}))Copy the code
- in
-
Add polyfill and ANTD component internationalization processing
// src/index.tsx import React from 'react' import ReactDOM from 'react-dom' / / into the store import { Provider } from 'react-redux' import store from '@/store/store' import { ConfigProvider, Empty } from 'antd' import App from './App' import zhCN from 'antd/es/locale/zh_CN' import 'moment/locale/zh-cn' // polyfill import 'core-js/stable' import 'regenerator-runtime/runtime' ReactDOM.render( <Provider store={store}> <ConfigProvider locale={zhCN} renderEmpty={Empty}> <App /> </ConfigProvider> </Provider>.document.getElementById('root'))Copy the code
-
CSS Modules
The create – react – app bring support to XXX. The module. (c | le | sa) ss stylesheet file, use typescript on projects should pay attention to:
const styles = require('./index.module.less') retrun ( <div className={` ${styles.container} `} > <Table columns={columns} className={` ${styles['border-setting` ']}}dataSource={props.store.check.items} rowKey={record= > record.id} pagination={false} /> <div className="type-check-box"></div> </div> ) Copy the code
// index.module.less .container { padding: 24px; background-color: #fff; height: 100%; overflow: auto; .border-setting { tr { td:nth-child(3) { border-left: 1px solid #F0F0F0; border-right: 1px solid #F0F0F0; }}td { text-align: left ! important; }} :global { // Descendant elements can write 'className' without using 'styles['type-check-box']' .type-check-box { .ant-checkbox-wrapper + .ant-checkbox-wrapper{ margin-left: 0; }}}}Copy the code
-
React JSX {r-if, r-for, r-model, r-show}
- Install dependencies
$ yarn add babel-react-rif babel-react-rfor babel-react-rmodel babel-react-rshow -D
- configuration
.babelrc
:// .babelrc{... ."plugins": [ "babel-react-rif"."babel-react-rfor"."babel-react-rmodel"."babel-react-rshow"]},Copy the code
- Example:
r-if
<div> <h1 r-if={height < 170} >good</h1> <h1 r-else-if={height > 180}>best</h1> <h1 r-else>other</h1> </div> Copy the code
r-for
{/* eslint-disable-next-line no-undef */} <div r-for={(item, index) in [1.2.3.4} key={index}> contents {item +The '-' + index} </div> Copy the code
r-model
<input onChange={this.callback} type="text" r-model={inputVale} /> Copy the code
r-show
<div r-show={true}> contents </div> # Note: This is`r-if`The effect does not render nodesCopy the code
- Install dependencies
Iv. Project Configuration II (Optimized Configuration)
- Implement lazy loading of components
react-loadable
import Loadable from 'react-loadable' const Loading = (props: any) = > { if (props.error) { console.error(props.error) return <div>Error! <Button type="link" onClick={props.retry}>Retry</Button></div> } else if (props.timedOut) { return <div>Timeout! <Button onClick={props.retry}>Retry</Button></div> } else if (props.pastDelay) { return <div>Loading...</div> } else { return null}}const loadable = (path: any) = > { return Loadable({ loader: () = > import(`@/pages${path}`), loading: Loading, delay: 200.timeout: 10000})},const Home = loadable('/homePage/Home') Copy the code
- To deal with
axios
Intercept the responseconst service = axios.create({ baseURL: '/'.timeout: 15000, }) service.interceptors.request.use(function (config) { return config }) service.interceptors.response.use(function (config) { return config }) Copy the code
- To deal with
React router
The nested configuration of
We know that React does not support routing tables like Vue Router. In React, everything is a component, and routing is also a component. When routing is needed, you need to add routing components temporarily.
// router/router.config.ts const routes = [ { path: '/home'.component: loadable('components/Index'), exact: true}, {path: '/new'.component: loadable('components/New'), redirect: '/new/list'.// exact: true, routes: [{path: '/new/list'.component: loadable('components/NewList'), exact: true}, {path: '/new/content'.component: loadable('components/NewContent'), exact: true,},],},]export default routes Copy the code
// router/router.ts import React from 'react' import { Switch, BrowserRouter as Router, Route } from 'react-router-dom' import routes from './index' function mapRoutes(routes: any[], store: object) :any { return routes.map((item: any, index: number) = > { return ( <Route exact={item.exact || false} path={item.path} key={index} render={props= >{ const NewComp = item.component Object.assign(props, { redirect: item.redirect || null, permission: item.permission || [], ... store }) if (item.routes) { return<NewComp {. props} >{ mapRoutes(item.routes, store) }</NewComp> } else { return <NewComp {. props} / > } }} />)})}const Routes = (props: any) = > { return ( <Router> <Switch> { mapRoutes(routes, props.store) } <Route component={()= > (<div>404 Page not Found!</div>)} / ></Switch> </Router>)}export default Routes Copy the code
The child routing bearer page needs to add the following code:
import { Redirect, Route, Switch } from 'react-router-dom' <Switch> {props.children} <Route component={() = > (<div>404 Page not Found!</div>)} /> {props.redirect && <Redirect to={props.redirect} />} </Switch> Copy the code
- To deal with
React store
// store/store.ts import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' import logger from 'redux-logger' import reducers from './reducer' const store = process.env.NODE_ENV === 'development' ? createStore(reducers, applyMiddleware(thunk, logger)) : createStore(reducers, applyMiddleware(thunk)) export default store Copy the code
For ease of use and avoiding the need for connect for each component, global injection of redux Store is implemented, but performance is compromised if the project is large.
// App.tsx import { dispatchActions } from '@/store/reducer' export default connect((state: any) = > ({ store: state }), dispatchActions)(App) Copy the code
Five, the summary
The project setup is complete, and all that remains is the business code. Believe that you can get the following harvest: (1) the project construction in the macro has a great ability to improve; (2) Clear understanding of the overall function of the project; (3) troubleshooting problems do not panic; (4) Packaging capacity has been strengthened; ⑤ The business functions are clear.
Six, digression
Create a react project based on create-react-app. Create a react project based on create-react-app. Create a react project based on create-react app. Link: github.com/zhuyuanmin/…