一、项目启动
-
了解需求背景
-
了解业务流程
二、项目搭建初始化
本案例使用脚手架 create-react-app
初始化了项目。此脚手架有利有弊吧,项目目录结构简洁,不需要太关心 webpack
令人头疼的配置;弊端在于,脚手架确实有些庞大,构建时间在 4mins 左右。各位看官择优选择吧,也可以完全自己搭建一个项目。
-
设置淘宝镜像仓库
-
工程目录 init
. |-- 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.json
-
yarn build
试试. |-- README.md |-- build/ # 改造点(由于 `Jenkins` 构建打包脚本有可能已经写死了 `dist` 包名) |-- 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.json
-
连接
git
远程仓库 -
添加
.gitignore
-
添加
eslint
代码及提交评论校验-
项目根目录新建
commitlint.config.js
// 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'], }, }
-
vscode
扩展中搜索ESLint
并安装,项目根目录新建.eslintrc.js
,内容可参考文章配置:zhuanlan.zhihu.com/p/84329603 看第五点 -
Commit message 格式说明
-
type
值枚举如下:- feat: 添加新特性
- fix: 修复 bug
- docs: 仅仅修改了文档
- style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑
- refactor: 代码重构,没有加新功能或者修复 bug
- perf: 增加代码进行性能测试
- test: 增加测试用例
- chore: 改变构建流程、或者增加依赖库、工具等
- revert: 当前 commit 用于撤销以前的 commit
-
subject
是 commit 目的的简短描述,不超过 50 个字符,且结尾不加句号(.)
-
-
package.json
新加入如下配置:{ ..., "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "src/**/*.{jsx,txs,ts,js,json,css,md}": [ "eslint" ] }, }
-
可执行
npx eslint [filePath] --fix
进行格式修复,无法修复的需手动解决
-
三、项目配置一(功能配置)
-
安装项目常用依赖库
-
webpack
配置拓展很有必要- 根目录新建
config-overrides.js
,详细使用可访问:简书:React 之 config-overrides文件配置 - 安装
- 修改
package.json
中启动项// package.json "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", }
- 使用
// config-overrides.js const { override, // 主函数 fixBabelImports, // 配置按需加载 addWebpackExternals, // 不做打包处理配置 addWebpackAlias, // 配置别名 addLessLoader // lessLoader 配置,可更改主题色等 } = require('customize-cra') module.exports = override(/* ... */, config => config)
- 根目录新建
-
配置按需加载
// config-overrides.js ... module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', // library 目录 style: true, // 自动打包相关的样式 }), )
-
更改主题色
// config-overrides.js ... module.exports = override( addLessLoader({ lessOptions: { javascriptEnabled: true, modifyVars: { '@primary-color': '#1890ff', }, } }), )
-
别名配置(
typescript
项目这里有坑)// config-overrides.js const path = require('path') ... module.exports = override( addWebpackAlias({ '@': path.resolve(__dirname, 'src'), }), )
-
去除注释、多进程打包压缩
// 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 })
-
解决埋下的两个坑
- 修改打包出的文件夹名为
dist
// 修改打包路径除了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 })
- 解决
typescript
别名配置- 查阅相关资料,需要在
tsconfig.json
中添加一项配置{ ... "extends": "./paths.json" }
- 新建文件
paths.json
{ "compilerOptions": { "baseUrl": "src", "paths": { "@/*": ["*"] } } }
- 查阅相关资料,需要在
- 修改打包出的文件夹名为
-
配置装饰器写法
{ "compilerOptions": { "experimentalDecorators": true, ... } }
-
配置开发代理
- 在
src
目录新建setupProxy.js
// src/setupProxy.js const proxy = require('http-proxy-middleware').createProxyMiddleware module.exports = function(app) { // app 为 Express 实例,此处可以写 Mock 数据 app.use( proxy('/api', { "target": "https://qpj-test.fapiaoer.cn", "changeOrigin": true, "secure": false, // "pathRewrite": { // "^/api": "" // } }) ) }
- 在
-
加入
polyfill
和antd
组件国际化处理// src/index.tsx import React from 'react' import ReactDOM from 'react-dom' // 注入 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') )
四、项目配置二(优化配置)
- 实现组件懒加载
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')
- 处理
axios
拦截响应const service = axios.create({ baseURL: '/', timeout: 15000, }) service.interceptors.request.use(function (config) { return config }) service.interceptors.response.use(function (config) { return config })
- 处理
React router
的嵌套配置// 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
// 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
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>
- 处理
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
// App.tsx import { dispatchActions } from '@/store/reducer' export default connect((state: any) => ({ store: state }), dispatchActions)(App)
五、总结
六、题外话
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!