从零开始搭建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

目录
相关文章
|
2月前
|
JavaScript
react+typescript通过window.xxx挂载属性报错的解决方案
react+typescript通过window.xxx挂载属性报错的解决方案
93 0
|
25天前
|
前端开发 JavaScript 开发者
React和TypeScript各自以其独特的优势赢得了广大开发者的青睐
【6月更文挑战第12天】React和TypeScript是前端开发的强强联合。TypeScript提供静态类型检查和面向对象特性,增强代码健壮性和团队协作效率;React凭借组件化、高性能和丰富生态系统引领UI构建。两者结合,能精确定义React组件类型,提升代码组织和维护性,通过安装TypeScript、配置、编写及构建步骤,可在React项目中实现这一优势。这种结合为前端开发带来进步,未来应用将更加广泛。
23 1
|
2月前
|
JavaScript 前端开发 开发者
【TypeScript技术专栏】TypeScript与React的完美结合
【4月更文挑战第30天】React和TypeScript在前端开发中备受推崇。React以其组件化、高性能和灵活的生态系统引领UI构建,而TypeScript通过静态类型检查和面向对象特性增强了代码的健壮性和可维护性。两者结合,能提升开发效率,降低错误,使React组件结构更清晰。通过安装TypeScript,配置tsconfig.json,然后用TypeScript编写和打包代码,可实现两者的无缝集成。这种结合为前端开发带来更强的代码质量和团队协作效果,随着技术发展,其应用将更加广泛。
|
2月前
|
前端开发 JavaScript 安全
【亮剑】探讨了在React TypeScript应用中如何通过道具(props)传递CSS样式,以实现模块化、主题化和动态样式
【4月更文挑战第30天】本文探讨了在React TypeScript应用中如何通过道具(props)传递CSS样式,以实现模块化、主题化和动态样式。文章分为三部分:首先解释了样式传递的必要性,包括模块化、主题化和动态样式以及TypeScript集成。接着介绍了内联样式的基本用法和最佳实践,展示了一个使用内联样式自定义按钮颜色的例子。最后,讨论了使用CSS模块和TypeScript接口处理复杂样式的方案,强调了它们在组织和重用样式方面的优势。结合TypeScript,确保了样式的正确性和可维护性,为开发者提供了灵活的样式管理策略。
|
2月前
|
前端开发
【专栏】在 create-react-app 中集成 less/sass 预处理器和 react-css-modules 的方法
【4月更文挑战第29天】本文介绍了在 create-react-app 中集成 less/sass 预处理器和 react-css-modules 的方法。首先,通过 `npm` 安装 less 或 sass 依赖,然后修改 `config-overrides.js` 配置文件以支持 less/sass 编译。接着,详细阐述如何使用 less/sass 编写样式。再者,安装 react-css-modules 并配置 webpack,使能样式模块化。最后,展示了如何结合使用 less/sass 和 react-css-modules,以提升前端开发的效率和代码质量。
|
2月前
|
运维 JavaScript 前端开发
发现了一款宝藏学习项目,包含了Web全栈的知识体系,JS、Vue、React知识就靠它了!
发现了一款宝藏学习项目,包含了Web全栈的知识体系,JS、Vue、React知识就靠它了!
|
2月前
|
存储 JSON 前端开发
react保姆级搭建新项目
此文主要以ts+vite+router6+antd 快速搭建一个react项目,适用于初学者,用于学习交流
|
2月前
|
前端开发 JavaScript 安全
使用React、TypeScript和Ant Design构建现代化前端应用
使用React、TypeScript和Ant Design构建现代化前端应用
75 0
|
2月前
|
移动开发 JavaScript 前端开发
vue/react项目刷新页面出现404的原因以及解决办法
vue/react项目刷新页面出现404的原因以及解决办法
340 0
React-41:antd自定义主题
React-41:antd自定义主题
146 0