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

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


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

npx create-react-app react-template --template typescript

在vscode中打开项目,可以看到顺利生成了react项目且组件的后缀为tsx,此时说明成功创建了react+typescript项目的雏形

在项目根目录下,运行npm run start,成功启动项目

npm run start

步骤2:引入antd

npm install antd -S

安装完成之后,再次运行npm run start启动项目,发现报错了,提示Could not find a declaration file for module 'react'.

这实际上是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';
  1. 删除node_modules文件夹,并重新执行npm install
  2. 重新执行npm run start,项目成功运行

接下来我们继续引入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;

可以看到效果:

此时antd就引入完成了

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

  1. 安装cross-env
npm i cross-env -D
  1. 配置package.json命令
"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"
  },
  1. 创建.env ,.env.development ,.env.production ,.env.uat ,.env.sit,
    .env
REACT_APP_ENV=development
  1. .env.development
REACT_APP_ENV=development
  1. .env.production
REACT_APP_ENV=production
  1. .env.uat
REACT_APP_ENV=uat
  1. .env.sit
REACT_APP_ENV=sit
  1. 项目中获取当前环境变量
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
  1. src目录下创建views文件夹,views内创建页面组件,如Index

  2. 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;

  1. index.tsx引入所有的功能模块路由配置,做统一暴露
import CommonRouter from "./CommonRouter";
const routerConfig: any = [...CommonRouter]
export default routerConfig;
  1. 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;
  1. 根目录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();

  1. 至此,路由配置就完成了

步骤5:配置less

  1. 暴露配置
npm run eject
  1. 此时项目多出了config文件夹
  2. 安装lessless-loader@5.0.0 (less-loader必须安装指定版本5.0.0)
npm install less less-loader@5.0.0 -S
  1. 仿照sass修改config目录下的webpack.config.js
    ①搜索 cssRegex ,找到后添加两行代码,添加less相关正则
// 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$/;
  1. ②修改 getStyleLoaders 函数,添加代码
{
  loader: require.resolve('less-loader'),
  options: lessOptions,
},
  1. ③搜索 cssRegex ,在 css 配置下添加 less 配置
// 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'
  ),
},
  1. 重新启动项目,创建less文件并引入


    样式生效,说明less配置成功

步骤6:配置sass

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

样式生效,说明sass配置成功

步骤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
  1. 创建redux核心模块
    如以下示例:
├── src
│   └── redux
│       ├── action
│       │   ├── TestAction.tsx
│       └── asyncAction
│       │   ├── AsyncTestAction.tsx
│       └── reducer
│       │   ├── reducer.tsx
│       │   └── TestReducer.tsx
│       └── store.tsx

  1. 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;
  1. reducer~reducer.tsx // 创建管理所有reducer的配置
import { combineReducers } from 'redux';
// 引入其他reducer
import TestReducer from './TestReducer';
// 将所有引入的reducer合并成一个reducers,暴露出去
export default combineReducers({
  TestReducer,
});
  1. 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
    }
  }
}
  1. 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);
  }
}
  1. store.tsx // 配置store
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer/reducer';
export default createStore(reducer, applyMiddleware(thunk));
  1. 入口文件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();
  1. 组件中使用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;

步骤8:自适应

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

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

文章参考

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

目录
相关文章
|
24天前
|
设计模式 监控 JavaScript
TypeScript 在大型项目内网管理监控软件中的结构优化
本文探讨了 TypeScript 在大型项目内网管理监控软件中的结构优化,包括模块划分与组织、接口与抽象类的使用以及依赖注入与控制反转的设计模式,通过具体代码示例展示了这些技术的应用,提高了代码的可读性、可维护性和灵活性。
32 3
|
30天前
|
存储 JavaScript 前端开发
掌握现代Web开发的基石:深入理解React与Redux
【10月更文挑战第14天】掌握现代Web开发的基石:深入理解React与Redux
31 0
|
22天前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
29天前
|
存储 JavaScript 前端开发
React中使用redux
【10月更文挑战第15天】
30 3
|
29天前
|
JavaScript 前端开发 测试技术
JavaScript与TypeScript:为何TypeScript成为大型项目的首选
JavaScript与TypeScript:为何TypeScript成为大型项目的首选
28 1
|
1月前
|
存储 JavaScript 前端开发
如何使用React和Redux构建现代化Web应用程序
【10月更文挑战第4天】如何使用React和Redux构建现代化Web应用程序
|
1月前
|
JavaScript 前端开发
使用 React 和 Redux 构建动态图表应用
【10月更文挑战第3天】使用 React 和 Redux 构建动态图表应用
|
1月前
|
JavaScript 前端开发
使用 React 和 Redux 构建一个计数器应用
【10月更文挑战第3天】使用 React 和 Redux 构建一个计数器应用
|
1月前
|
存储 JavaScript 前端开发
|
1月前
|
前端开发 JavaScript 网络架构
实现动态路由与状态管理的SPA——使用React Router与Redux
【10月更文挑战第1天】实现动态路由与状态管理的SPA——使用React Router与Redux
32 1