写给前端新人:从 0到1 搭建一个前端项目,都需要做什么?

不使用脚手架搭建 React 项目:https://github.com/zhuyuanmin/react-0-1-build。读者可根据提交的分支顺序一步步搭建,所以库都使用了最新版本,让我们在踩坑中成长!【master 分支:完整版,不包含 typescript ;typescript-react 分支: 包含 typescript 的完整版本】

一、项目启动

  1. 了解需求背景
  2. 了解业务流程

二、项目搭建初始化

本案例使用脚手架 create-react-app 初始化了项目。此脚手架有利有弊吧,项目目录结构简洁,不需要太关心 webpack 令人头疼的配置;弊端在于,脚手架确实有些庞大,构建时间在 4mins 左右。各位看官择优选择吧,也可以完全自己搭建一个项目。

  1. 设置淘宝镜像仓库
$ yarn config set registry registry.npm.taobao.org/ -g $ yarn config set sass_binary_site cdn.npm.taobao.org/dist/node-sass -g
  1. 工程目录 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.json
  1. yarn build 试试
$ yarn build & tree -I "node_modules"
.
|-- 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
  1. 连接 git 远程仓库
$ git remote add origin yuanmin.zhu%40wetax.com.cn:wd246800mm@gitlab.yunpiaoer.com/front/qpj-web-pc.git
  1. 添加 .gitignore
$ echo -e " yarn.lock package-lock.json /dist .idea" >> .gitignore
  1. 添加 eslint 代码及提交评论校验
$ 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"
    项目根目录新建 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],
  },
}
    Commit message 格式说明
<type>: <subject>
    type 值枚举如下: feat: 添加新特性 fix: 修复 bug docs: 仅仅修改了文档 style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑 refactor: 代码重构,没有加新功能或者修复 bug perf: 增加代码进行性能测试 test: 增加测试用例 chore: 改变构建流程、或者增加依赖库、工具等 revert: 当前 commit 用于撤销以前的 commit subject 是 commit 目的的简短描述,不超过 50 个字符,且结尾不加句号(.) package.json 新加入如下配置:
{
    ...,
    "lint-staged": {
        "src/**/*.{jsx,txs,ts,js,json,css,md}": [
            "eslint --quiet"
        ]
    },
}
    可执行 npx eslint [filePath] --fix 进行格式修复,无法修复的需手动解决

三、项目配置一(功能配置)

  1. 安装项目常用依赖库
$ yarn add antd axios dayjs qs -S # UI 库 及工具库 $ yarn add react-router-dom redux react-redux redux-logger redux-thunk -S # 路由及状态管理
  1. webpack 配置拓展很有必要
    根目录新建 config-overrides.js,详细使用可访问:简书:React 之 config-overrides文件配置 安装 $ yarn add react-app-rewired customize-cra -D 修改 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)
  1. 配置按需加载
// config-overrides.js
...
module.exports = override(
    fixBabelImports(import, {
        libraryName: antd,
        libraryDirectory: es, // library 目录
        style: true, // 自动打包相关的样式
    }),
)
  1. 更改主题色
// config-overrides.js
...
module.exports = override(
    addLessLoader({
        lessOptions: {
            javascriptEnabled: true,
            modifyVars: {
                @primary-color: #1890ff,
            },
        }
    }),
)
  1. 别名配置(typescript 项目这里有坑)
// config-overrides.js
const path = require(path)
...
module.exports = override(
    addWebpackAlias({
        @: path.resolve(__dirname, src),
    }),
)
  1. 去除注释、多进程打包压缩
// 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
})
  1. 解决埋下的两个坑
    修改打包出的文件夹名为 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": {
            "@/*": ["*"]
        }
    }
}
  1. 配置装饰器写法
{
    "compilerOptions": {
        "experimentalDecorators": true,
        ...
    }
}
  1. 配置开发代理
    在 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": ""
            // }
        })
    )
}
  1. 加入 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)
)
  1. CSS Modules
create-react-app 自带支持以 xxx.module.(c|le|sa)ss 的样式表文件,使用上 typescript 项目中要注意:
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>
)
// 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 { // 这个标识之后,其子代元素可以不需要使用 `styles[type-check-box]` 的方式,直接写 `className`
        .type-check-box {
            .ant-checkbox-wrapper + .ant-checkbox-wrapper{
                margin-left: 0;
            }
        }
    }
}
  1. 【新】配置 React jsx 指令式属性 r-if、r-for、r-model、r-show,提升开发效率:
    安装依赖
$ yarn add babel-react-rif babel-react-rfor babel-react-rmodel babel-react-rshow -D
    配置 .babelrc :
// .babelrc
{
    ...,
    "plugins": [
        "babel-react-rif",
        "babel-react-rfor",
        "babel-react-rmodel",
        "babel-react-rshow",
    ]
}
    使用示例: r-if
<div>
  <h1 r-if={height < 170}>good</h1>
  <h1 r-else-if={height > 180}>best</h1>
  <h1 r-else>other</h1>
</div>
    r-for
{/* eslint-disable-next-line no-undef */}
<div r-for={(item, index) in [1, 2, 3, 4]} key={index}>
  内容 {item + - + index}
</div>
    r-model
<input onChange={this.callback} type="text" r-model={inputVale} />
    r-show
<div r-show={true}>内容</div> # 注意:这是 `r-if` 的效果,不会渲染节点

四、项目配置二(优化配置)

  1. 实现组件懒加载 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)
  1. 处理 axios 拦截响应
const service = axios.create({
    baseURL: /,
    timeout: 15000,
})

service.interceptors.request.use(function (config) {
    return config
})

service.interceptors.response.use(function (config) {
    return config
})
  1. 处理 React router 的嵌套配置
我们知道 React 中不支持类似 Vue Router 路由配置方式,React 中一切皆组件,路由也是组件,需要用到路由要临时加上路由组件,写起来就很繁琐,但我们可以自己实现路由配置表方式。
// 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>
  1. 处理 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
为了方便使用,避免每个组件都需要 connect ,这边实现了 redux store 的全局注入,但是如果项目庞大的话就会损耗些性能。
// App.tsx
import { dispatchActions } from @/store/reducer

export default connect((state: any) => ({ store: state }), dispatchActions)(App)

五、总结

自此项目搭建就全部完成了,剩下的就是业务代码了。相信你可以得到如下收获: ① 项目构建在宏观上有个极大的能力提升; ② 项目整体功能了解清晰; ③ 排查问题不慌乱; ④ 封装能力有加强; ⑤ 业务功能很清楚。

六、题外话

基于 create-react-app 创建的 React 项目,本人实现了一个脚手架,以上配置默认已经全部加入实现,欢迎 Github 试用并 star 。链接:https://github.com/zhuyuanmin/zym-cli

关于本文

作者:前端小猿_zym

https://juejin.cn/post/6953807616082460702

The End

欢迎自荐投稿到《前端技术江湖》,如果你觉得这篇内容对你挺有启发,记得点个 「在看」哦

点个『在看』支持下

不使用脚手架搭建 React 项目:https://github.com/zhuyuanmin/react-0-1-build。读者可根据提交的分支顺序一步步搭建,所以库都使用了最新版本,让我们在踩坑中成长!【master 分支:完整版,不包含 typescript ;typescript-react 分支: 包含 typescript 的完整版本】 一、项目启动 了解需求背景 了解业务流程 二、项目搭建初始化 本案例使用脚手架 create-react-app 初始化了项目。此脚手架有利有弊吧,项目目录结构简洁,不需要太关心 webpack 令人头疼的配置;弊端在于,脚手架确实有些庞大,构建时间在 4mins 左右。各位看官择优选择吧,也可以完全自己搭建一个项目。 设置淘宝镜像仓库 $ yarn config set registry registry.npm.taobao.org/ -g $ yarn config set sass_binary_site cdn.npm.taobao.org/dist/node-sass -g 工程目录 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.json yarn build 试试 $ yarn build & tree -I "node_modules" . |-- 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 远程仓库 $ git remote add origin yuanmin.zhu%40wetax.com.cn:wd246800mm@gitlab.yunpiaoer.com/front/qpj-web-pc.git 添加 .gitignore $ echo -e " yarn.lock package-lock.json /dist .idea" >> .gitignore 添加 eslint 代码及提交评论校验 $ 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" 项目根目录新建 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],   }, } Commit message 格式说明 : type 值枚举如下: feat: 添加新特性 fix: 修复 bug docs: 仅仅修改了文档 style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑 refactor: 代码重构,没有加新功能或者修复 bug perf: 增加代码进行性能测试 test: 增加测试用例 chore: 改变构建流程、或者增加依赖库、工具等 revert: 当前 commit 用于撤销以前的 commit subject 是 commit 目的的简短描述,不超过 50 个字符,且结尾不加句号(.) package.json 新加入如下配置: {     ...,     "lint-staged": {         "src/**/*.{jsx,txs,ts,js,json,css,md}": [             "eslint --quiet"         ]     }, } 可执行 npx eslint [filePath] --fix 进行格式修复,无法修复的需手动解决 三、项目配置一(功能配置) 安装项目常用依赖库 $ yarn add antd axios dayjs qs -S # UI 库 及工具库 $ yarn add react-router-dom redux react-redux redux-logger redux-thunk -S # 路由及状态管理 webpack 配置拓展很有必要 根目录新建 config-overrides.js,详细使用可访问:简书:React 之 config-overrides文件配置 安装 $ yarn add react-app-rewired customize-cra -D 修改 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(                                     ,     document.getElementById(root) ) CSS Modules create-react-app 自带支持以 xxx.module.(c|le|sa)ss 的样式表文件,使用上 typescript 项目中要注意: const styles = require(./index.module.less) retrun (                    ) // 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 { // 这个标识之后,其子代元素可以不需要使用 `styles[type-check-box]` 的方式,直接写 `className` .type-check-box { .ant-checkbox-wrapper + .ant-checkbox-wrapper{ margin-left: 0; } } } } 【新】配置 React jsx 指令式属性 r-if、r-for、r-model、r-show,提升开发效率: 安装依赖 $ yarn add babel-react-rif babel-react-rfor babel-react-rmodel babel-react-rshow -D 配置 .babelrc : // .babelrc {     ...,     "plugins": [         "babel-react-rif",         "babel-react-rfor",         "babel-react-rmodel",         "babel-react-rshow",     ] } 使用示例: r-if
   good     180}>best    other
r-for {/* eslint-disable-next-line no-undef */}   内容 {item + - + index} r-model r-show 内容 # 注意:这是 `r-if` 的效果,不会渲染节点 四、项目配置二(优化配置) 实现组件懒加载 react-loadable import Loadable from react-loadable const Loading = (props: any) => {     if (props.error) {         console.error(props.error)         return 
Error!  Retry
    } else if (props.timedOut) {         return 
Timeout!  Retry
    } else if (props.pastDelay) {         return 
Loading...
    } 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 的嵌套配置 我们知道 React 中不支持类似 Vue Router 路由配置方式,React 中一切皆组件,路由也是组件,需要用到路由要临时加上路由组件,写起来就很繁琐,但我们可以自己实现路由配置表方式。 // 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 (               {                 const NewComp = item.component                 Object.assign(props, {                     redirect: item.redirect || null,                     permission: item.permission || [],                     ...store                 })                 if (item.routes) {                     return  { mapRoutes(item.routes, store) }                 } else {                     return                  }             }} />         )     }) } const Routes = (props: any) => {     return (                                       { mapRoutes(routes, props.store) }                   (
404 Page not Found!
)} />             
        
    ) } export default Routes 子路由承载页面需要加上如下代码: import { Redirect, Route, Switch } from react-router-dom     {props.children}       (
404 Page not Found!
)} />     {props.redirect &&  }
处理 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 为了方便使用,避免每个组件都需要 connect ,这边实现了 redux store 的全局注入,但是如果项目庞大的话就会损耗些性能。 // App.tsx import { dispatchActions } from @/store/reducer export default connect((state: any) => ({ store: state }), dispatchActions)(App) 五、总结 自此项目搭建就全部完成了,剩下的就是业务代码了。相信你可以得到如下收获: ① 项目构建在宏观上有个极大的能力提升; ② 项目整体功能了解清晰; ③ 排查问题不慌乱; ④ 封装能力有加强; ⑤ 业务功能很清楚。 六、题外话 基于 create-react-app 创建的 React 项目,本人实现了一个脚手架,以上配置默认已经全部加入实现,欢迎 Github 试用并 star 。链接:https://github.com/zhuyuanmin/zym-cli 关于本文 作者:前端小猿_zym https://juejin.cn/post/6953807616082460702 The End 欢迎自荐投稿到《前端技术江湖》,如果你觉得这篇内容对你挺有启发,记得点个 「在看」哦 点个『在看』支持下
 record.id}             pagination={false}         />         
经验分享 程序员 微信小程序 职场和发展