从零开始搭建react+typescript+antd+redux+less+vw自适应项目

从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤1:通过create-react-app脚手架创建项目

npx create-react-app ai_video_project_frontend --template typescript

从零开始搭建react+typescript+antd+redux+less+vw自适应项目
在vscode中打开项目,可以看到顺利生成了react项目且组件的后缀为tsx,此时说明成功创建了react+typescript项目的雏形
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
在项目根目录下,运行npm run start,成功启动项目

npm run start

从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤2:引入antd

npm install antd -S

安装完成之后,再次运行npm run start启动项目,发现报错了,提示Could not find a declaration file for module 'react'.
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
这实际上是create-react-app 4.x版本的bug
解决方案如下:

  1. 在项目根目录创建react-app-env.d.ts文件

    /// <reference types="react-scripts" />
    /// <reference types="node" />
    /// <reference types="react" />
    /// <reference types="react-dom" />
    
    declare namespace NodeJS {
      interface ProcessEnv {
        readonly NODE_ENV: 'development' | 'production' | 'test';
        readonly PUBLIC_URL: string;
      }
    }
    
    declare module '*.bmp' {
      const src: string;
      export default src;
    }
    
    declare module '*.gif' {
      const src: string;
      export default src;
    }
    
    declare module '*.jpg' {
      const src: string;
      export default src;
    }
    
    declare module '*.jpeg' {
      const src: string;
      export default src;
    }
    
    declare module '*.png' {
      const src: string;
      export default src;
    }
    
    declare module '*.webp' {
        const src: string;
        export default src;
    }
    
    declare module '*.svg' {
      import * as React from 'react';
    
      export const ReactComponent: React.FunctionComponent<React.SVGProps<
        SVGSVGElement
      > & { title?: string }>;
    
      const src: string;
      export default src;
    }
    
    declare module '*.module.css' {
      const classes: { readonly [key: string]: string };
      export default classes;
    }
    
    declare module '*.module.scss' {
      const classes: { readonly [key: string]: string };
      export default classes;
    }
    
    declare module '*.module.sass' {
      const classes: { readonly [key: string]: string };
      export default classes;
    }
    
    // declare module 'react-imageview';
    
    
  2. 删除node_modules文件夹,并重新执行npm install

  3. 重新执行npm run start,项目成功运行
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

接下来我们继续引入antd
在App.tsx中添加import 'antd/dist/antd.css';,同时引入所需要的antd组件,如Button

import React from 'react';
import './App.css';
import 'antd/dist/antd.css';
import { Button } from 'antd';

function App() {
  return (
    <div className="App">
      <Button type="primary">Button</Button>
    </div>
  );
}

export default App;

可以看到效果:
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
此时antd就引入完成了

步骤3:配置不同环境的打包命令,如测试环境、生产环境

  1. 安装cross-env

    npm i cross-env -D
    
  2. 配置package.json命令
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    "scripts": {
        "serve": "cross-env REACT_APP_ENV=development node scripts/start.js",
        "build": "cross-env REACT_APP_ENV=production node scripts/build.js",
        "uat": "cross-env REACT_APP_ENV=uat node scripts/build.js",
        "sit": "cross-env REACT_APP_ENV=sit node scripts/build.js"
      },
    
  3. 创建.env ,.env.development ,.env.production ,.env.uat ,.env.sit,
    .env

    REACT_APP_ENV=development
    

    .env.development

    REACT_APP_ENV=development
    

    .env.production

    REACT_APP_ENV=production
    

    .env.uat

    REACT_APP_ENV=uat
    

    .env.sit

    REACT_APP_ENV=sit
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

  4. 项目中获取当前环境变量

    console.log(process.env.REACT_APP_ENV)
    

步骤4:配置路由

  1. 安装react-router-dom@types/react-router-dom

    npm i react-router-dom @types/react-router-dom -S
    
  2. src目录下创建views文件夹,views内创建页面组件,如Index
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

  3. src目录下创建router文件夹,文件夹下配置各个功能模块的路由,同时创建index.tsx,对各个功能模块的路由做统一的引入和暴露
    功能模块CommonRouter.tsx:

    import { lazy } from "react";
    
    const Index = lazy(() => import("../views/Index/Index"));
    
    const CommonRouter: any = [
      {
        path: "/",
        component: Index,
        exact: true,
        title: "首页",
      },
    ];
    
    export default CommonRouter;
    
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目
    index.tsx引入所有的功能模块路由配置,做统一暴露

    import CommonRouter from "./CommonRouter";
    
    const routerConfig: any = [...CommonRouter]
    
    export default routerConfig;
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

  4. App.tsx中引入Route和自定义的路由配置

    import React, { Suspense, Component, Fragment } from "react";
    import { Route, Switch, Redirect, withRouter as realWithRouter } from "react-router-dom";
    // 这里的是路由配置文件
    import routes from "./router/index";
    import 'antd/dist/antd.css';
    
    type StateType = {
      [propName: string]: any;
    };
    type PropType = {
      [propName: string]: any;
    };
    interface App {
      state: StateType;
      props: PropType;
    }
    
    // 获取路由信息
    const withRouter: any = realWithRouter;
    @withRouter
    class App extends Component {
      render() {
        return (
          <Fragment>
            <Suspense fallback={<div></div>}>
              <Switch>
                {routes.length > 0 &&
                  routes.map((route: any) => {
                    //遍历路由数组
                    const { path, component: C, exact } = route;
                    return (
                      <Route
                        exact={exact}
                        key={path}
                        path={path}
                        render={(props: any) => {
                          return <C {...props} />;
                        }}
                      />
                    );
                  })}
                {/* 默认进入/时自动匹配到/ */}
                <Redirect exact from="/" to={"/"} />
                {/* 默认无效路径时自动匹配到首页 */}
                <Redirect to="/" />
              </Switch>
            </Suspense>
          </Fragment>
        );
      }
    }
    
    export default App;
    
    
  5. 根目录index.tsx中这样定义

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { BrowserRouter as Router } from "react-router-dom";
    
    
    ReactDOM.render(
      <React.StrictMode>
        <Router>
          <App />
        </Router>
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();
    
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    至此,路由配置就完成了
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤5:配置less

  1. 暴露配置

    npm run eject
    

    此时项目多出了config文件夹
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

  2. 安装lessless-loader@5.0.0 (less-loader必须安装指定版本5.0.0)

    npm install less less-loader@5.0.0 -S
    
  3. 仿照sass修改config目录下的webpack.config.js

    ①搜索 cssRegex ,找到后添加两行代码,添加less相关正则
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    // style files regexes
    const cssRegex = /\.css$/;
    const cssModuleRegex = /\.module\.css$/;
    const sassRegex = /\.(scss|sass)$/;
    const sassModuleRegex = /\.module\.(scss|sass)$/;
    const lessRegex = /\.less$/;
    const lessModuleRegex = /\.module\.less$/;
    

    ②修改 getStyleLoaders 函数,添加代码
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    {
      loader: require.resolve('less-loader'),
      options: lessOptions,
    },
    

    ③搜索 cssRegex ,在 css 配置下添加 less 配置从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    // Opt-in support for LESS (using .less extensions).
    // By default we support LESS Modules with the
    // extensions .module.less
    {
      test: lessRegex,
      exclude: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 1,
          sourceMap: isEnvProduction
            ? shouldUseSourceMap
            : isEnvDevelopment,
        },
        'less-loader'
      ),
      // Don't consider CSS imports dead code even if the
      // containing package claims to have no side effects.
      // Remove this when webpack adds a warning or an error for this.
      // See https://github.com/webpack/webpack/issues/6571
      sideEffects: true,
    },
    // Adds support for CSS Modules, but using LESS
    // using the extension .module.less
    {
      test: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 1,
          sourceMap: isEnvProduction
            ? shouldUseSourceMap
            : isEnvDevelopment,
          modules: {
            getLocalIdent: getCSSModuleLocalIdent,
          },
        },
        'less-loader'
      ),
    },
    
  4. 重新启动项目,创建less文件并引入
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目
    样式生效,说明less配置成功
    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤6:配置sass

通过create-react-app创建的react项目,其实是默认已经配置好sass的,所以我们先尝试在项目中引入sass文件

从零开始搭建react+typescript+antd+redux+less+vw自适应项目
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
样式生效,说明sass配置成功
从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤7:配置react-redux

  1. 安装react-redux@types/react-reduxredux-thunk@types/redux-thunk

    npm install react-redux @types/react-redux redux-thunk @types/redux-thunk -S
    
  2. 创建redux核心模块
    如以下示例:

    ├── src
    │   └── redux
    │       ├── action
    │       │   ├── TestAction.tsx
    │       └── asyncAction
    │       │   ├── AsyncTestAction.tsx
    │       └── reducer
    │       │   ├── reducer.tsx
    │       │   └── TestReducer.tsx
    │       └── store.tsx
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

    reducer~TestReducer.tsx // 创建TestReducer

    interface StateType {
      name: string;
      age: number;
    }
    
    const commonState: StateType = {
      name: '',
      age: 0,
    };
    
    const TestReducer = (state = commonState, action: any): any => {
      switch (action.type) {
        case "CHANGE_NAME":
          return {
            ...state,
            name: action.payload.name,
          };
        case "CHANGE_AGE":
          return {
            ...state,
            age: action.payload.age,
          };
        case "ASYNC_CHANGE_NAME":
          return {
            ...state,
            addressObj: action.payload.name,
          };
        default:
          return state;
      }
    };
    
    export default TestReducer;
    
    

    reducer~reducer.tsx // 创建管理所有reducer的配置

    import { combineReducers } from 'redux';
    // 引入其他reducer
    import TestReducer from './TestReducer';
    
    // 将所有引入的reducer合并成一个reducers,暴露出去
    export default combineReducers({
      TestReducer,
    });
    

    action~TestAction.tsx // 创建同步更改state的方法

    export const changeName = (name: string) => {
      return {
        type: 'CHANGE_NAME',
        payload: {
          name
        }
      }
    }
    
    export const changeAge = (age: number) => {
      return {
        type: 'CHANGE_AGE',
        payload: {
          age
        }
      }
    }
    
    

    asyncAction~AsyncTestAction.tsx // 创建异步更改state的方法

    export const asyncChangeName = () => {
      return async (dispatch: any) => {
        const countDown: Promise<any> = new Promise((resolve: any, reject: any) => {
          setTimeout(() => {
            resolve({
              name: '模拟网络请求获取到的name'
            })
          }, 1000)
        })
        const data: any = await countDown;
        const params: any = {
          type: 'ASYNC_CHANGE_NAME',
          payload: {
            name: data.name
          }
        };
        dispatch(params);
      }
    }
    
    

    store.tsx // 配置store

    import { createStore, applyMiddleware } from 'redux';
    
    import thunk from 'redux-thunk';
    
    import reducer from './reducer/reducer';
    
    export default createStore(reducer, applyMiddleware(thunk));
    
  3. 入口文件index.tsx配置redux

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { BrowserRouter as Router } from "react-router-dom";
    
    import { Provider } from 'react-redux';
    import store from './redux/store';
    
    ReactDOM.render(
      <Provider store={store}>
        <React.StrictMode>
          <Router>
            <App />
          </Router>
        </React.StrictMode>
      </Provider>,
      document.getElementById('root')
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

  4. 组件中使用redux,如在App.tsx中使用redux:

    import React, { Suspense, Component, Fragment } from "react";
    // 这里的是路由配置文件
    import { Route, Switch, Redirect, withRouter as realWithRouter } from "react-router-dom";
    import routes from "./router/index";
    
    // 公共样式
    import 'antd/dist/antd.css';
    import './assets/style/public.less';
    
    // redux
    import { changeName, changeAge } from "./redux/action/TestAction";
    import { asyncChangeName } from "./redux/asyncAction/AsyncTestAction";
    import { connect as realConnect } from "react-redux";
    
    type StateType = {
      [propName: string]: any;
    };
    type PropType = {
      [propName: string]: any;
    };
    interface App {
      state: StateType;
      props: PropType;
    }
    
    // 获取redux
    const mapStateToProps = (state: any) => {
      return {
        state,
      };
    };
    const connect: any = realConnect;
    
    // 获取路由信息
    const withRouter: any = realWithRouter;
    @withRouter
    @connect(mapStateToProps, { changeName, changeAge, asyncChangeName })
    class App extends Component {
      componentDidMount() {
        // 调用redux中指定reducer的同步action和异步action方法
        this.props.changeName('张三');
        this.props.changeAge(25);
        this.props.asyncChangeName();
        // 获取redux中指定reducer的state
        console.log(this.props.state.TestReducer);
      }
    
      render() {
        return (
          <Fragment>
            <Suspense fallback={<div></div>}>
              <Switch>
                {routes.length > 0 &&
                  routes.map((route: any) => {
                    //遍历路由数组
                    const { path, component: C, exact } = route;
                    return (
                      <Route
                        exact={exact}
                        key={path}
                        path={path}
                        render={(props: any) => {
                          return <C {...props} />;
                        }}
                      />
                    );
                  })}
                {/* 默认进入/时自动匹配到/ */}
                <Redirect exact from="/" to={"/"} />
                {/* 默认无效路径时自动匹配到首页 */}
                <Redirect to="/" />
              </Switch>
            </Suspense>
          </Fragment>
        );
      }
    }
    
    export default App;
    
    

    从零开始搭建react+typescript+antd+redux+less+vw自适应项目

步骤8:自适应

如有配置自适应的需求,可参考这篇文章移动端自适应解决方案vw(以react为例)

至此,react项目创建和配置完成

文章参考

https://www.cnblogs.com/gqx-html/p/13219422.html

上一篇:Linux常用命令


下一篇:05-rem+less