React17+TS+Antd+ReactRouteDom6踩坑指南,超详细

2,171 阅读4分钟

一直用的Vue,有个项目要用react,负责人说跟Vue3.0很像,问多久能上手?信心满满,说2天吧,于是开启了打脸心酸历程。本篇记录了用 react17+ts+antd+ReactRouteDom6 搭建项目的历程,可谓处处碰壁!

1.安装和初始化

1.创建项目

antd官网地址>>

请确保电脑上已经安装了最新版的 yarn 或者 npm。 我电脑yarn、npm版本如下: image.png

使用 yarn 创建 cra-template-typescript 项目。

  npx create-react-app antd-demo-ts --template typescript
  or use yarn...
  yarn create react-app antd-demo-ts --template typescript

然后我们进入项目并启动。

 cd antd-demo-ts
 yarn start

此时浏览器会访问 http://localhost:3000/ ,看到 Welcome to React 的界面就算成功了。

image.png

2.引入 antd

项目目录下运行:

$ yarn add antd

修改 src/App.tsx,引入 antd 的按钮组件。

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

const App: FC = () => (
  <div className="App">
    <Button type="primary">Button</Button>
  </div>
);

export default App;

修改 src/App.css,在文件顶部引入 antd 的样式。

@import '~antd/dist/antd.css';

重新启动yarn startnpm run start,出现以下界面为引入成功:

image.png

2.配置样式文件

React脚手架自身支持.css, .Scss, .Sass,如果想使用less,需要自己添加对less的配置。以下介绍styled-components css modules Sass less四种编写样式的使用。

1.styled-components的使用

styled-components官方文档地址: www.styled-components.com/

styled-components中文文档地址:github.com/hengg/style…

styled-components是一个React的第三方库,是CSS in JS的优秀实践。 styled-components是在js中去编写css,创建后就是一个正常的React 组件,并且可以附加样式给当前组件,这种设计跟react相当的般配。

1.安装styled-components

npm install --save styled-components

2.vscode语法提示高亮

vscode扩展商店搜索 vscode-styled-components ,直接安装。

image.png

image.png

上面这个这段代码就可以把WrapStyle当做一个正常的组件导入react组件中使用。

2.引入并使用css modules

关于css模块化,想了解更多的话可以看下梳理 CSS 模块化,通过与sass,less的对比,比较清晰的认知css modules是什么。阮一峰的网络日志 CSS Modules 用法教程中讲述了css modules使用规则。

css modules的好处是:

  • 样式私有化
  • 避免被其他样式文件污染
  • 可复用
  1. src目录下新建 style/common.module.css 文件,请注意,必须是*.module.css!!! 输入import ""vscode提示可以看出配置了哪些后缀文件。 image.png
.red{color: red;}
  1. 打开APP.tsx 文件新增以下代码 : 
import React, { FC } from "react";
import "./App.css";
import styles from "./style/common.module.css";

const App: FC = () => (
  <div className="App">
    <h1 className={styles.red}>red</h1>
  </div>
);

export default App;

image.png

3.安装Sass和node-sass并使用Sass

  1. 安装Sass和node-sass
npm install node-sass
...or use yarn
yarn add node-sass
  1. 报错node-sass为 7.0.0版本,与^4.0.0 || ^5.0.0不兼容:
./src/style/common.module.scss (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-7-1!./node_modules/postcss-loader/src??postcss!./node_modules/resolve-url-loader??ref--5-oneOf-7-3!./node_modules/sass-loader/dist/cjs.js??ref--5-oneOf-7-4!./src/style/common.module.scss)
Node Sass version 7.0.0 is incompatible with ^4.0.0 || ^5.0.0.
  1. 首先先卸载node-sass为5.0.0版本
npm uninstall node-sass
...or use yarn
yarn remove node-sass
  1. 卸载后安装4.0.0版本(5.0.0版本之前的)
npm install node-sass@4.14.1
...or use yarn
yarn add node-sass@4.14.1

将上述 style/common.module.css文件重命名为common.module.scss,App.tsx引入改为import styles from "./style/common.module.scss";重启后依然有效。

4.安装 Less 和 Less-loader并使用Less

1. 安装 Less 和 Less-loader

npm i less less-loader
...or use yarn
yarn add less less-loader

2. 打开隐藏的webpack配置文件

  1. 打开隐藏的webpack配置文件
npm run eject
...or use yarn
yarn eject
  1. 报错,问题是脚手架添加.gitgnore文件,但是却没有本地仓库
This git repository has untracked files or uncommitted changes:

package.json
D public/logo192.png
D public/logo512.png
D public/manifest.json
D public/robots.txt
M src/App.css
D src/App.test.tsx
M src/App.tsx
D src/index.css
M src/index.tsx
D src/logo.svg
D src/reportWebVitals.ts
D src/setupTests.ts
M yarn.lock
package-lock.json
src/style/

Remove untracked files, stash or commit any changes, and try again.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
  1. 解决方法,依次执行以下命令:
git init

git add .

git commit -m "Saving before ejecting"

yarn eject
  1. 项目目录下显示了webpack配置文件config文件夹: image.png

3. webpack中配置Less

打开 config 文件夹,修改 webpack.config.js,可以搜索下SasslessSass配置基本一致,可以仿照sass配置去理解。 添加后缀匹配:

  1. 搜索 cssRegex ,找到后添加两行代码:
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

image.png

  1. 修改 getStyleLoaders 函数,添加代码
{
    loader: require.resolve("less-loader"),
    options: lessOptions,
  },

image.png

  1. 添加以下代码在test: sassModuleRegex,下面:
 {
  test: lessRegex,
  exclude: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
    },
    "less-loader"
  ),
  sideEffects: true,
},
{
  test: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction
        ? shouldUseSourceMap
        : isEnvDevelopment,
      modules: {
        getLocalIdent: getCSSModuleLocalIdent,
      },
    },
    "less-loader"
  ),
},

image.png

4. 重启后报错:less-loader安装的版本过高到导致,需要安装6以下版本

Failed to compile.

./src/App.css (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!./node_modules/less-loader/dist/cjs.js!./node_modules/postcss-loader/src??postcss!./src/App.css)
TypeError: this.getOptions is not a function

解决方法,依次执行命令:

npm uninstall less-loader
npm install less-loader@5.0.0

...or use yarn

yarn remove less-loader
yarn add less-loader@5.0.0

4. 在*.d.ts中添加第三方库的声明

react-app-env.d.ts中添加第三方库的声明,不然在.tsx文件中引入less文件会报错:

declare module "*.module.less" {
  const classes: { readonly [key: string]: string };
  export default classes;
}

将上述 style/common.module.css文件重命名为common.module.less,App.tsx引入改为import styles from "./style/common.module.less";重启后依然有效。

5.react引入less全局样式文件

  1. 安装style-resources-loader插件,插件地址>>

style-resources-loader 是一个用于 webpack 的 CSS 处理器资源加载器,它将你的样式资源(例如变量,mixins)注入到多个导入的CSS, sass, scss, less, stylus模块中。 运行命令如下:

npm install --save-dev style-resources-loader 
...or use yarn
yarn add --save-dev style-resources-loader

2.在webpack中配置less全局样式文件

修改 webpack.config.jstest: lessRegex,test: lessModuleRegex部分如下:

请注意,path.resolve(__dirname,"../src/style/globle.module.less"改为你放置公共样式文件的地址

 {
  test: lessRegex,
  exclude: cssModuleRegex,
  use: [
    ...getStyleLoaders(
      {
        importLoaders: 3,
        sourceMap: isEnvProduction
          ? shouldUseSourceMap
          : isEnvDevelopment,
      },
      "less-loader"
    ),
    {
      loader: "style-resources-loader",
      options: {
        patterns: path.resolve(
          __dirname,
          "../src/style/globle.module.less"
        ),
      },
    },
  ],
  sideEffects: true,
},
{
  test: lessModuleRegex,
  use: [
    ...getStyleLoaders(
      {
        importLoaders: 3,
        sourceMap: isEnvProduction
          ? shouldUseSourceMap
          : isEnvDevelopment,
        modules: {
          getLocalIdent: getCSSModuleLocalIdent,
        },
      },
      "less-loader"
    ),
    {
      loader: "style-resources-loader",
      options: {
        patterns: path.resolve(
          __dirname,
          "../src/style/globle.module.less"
        ),
      },
    },
  ],
},

重启项目,现在 "../src/style/globle.module.less"中定义的less就是全局的了。

image.png

3.引入路由并使用

1. 安装react-router-dom

React RouterReact-Router domReact-Router native提供了所有的核心功能。React-Router-dom包包含了在web应用中使用React Router的绑定,所以我们只安装react-router-dom即可:

npm install react-router-dom
...or use yarn
yarn add react-router-dom

image.png

2.引入路由并使,心酸百度路:

reactrouter官网文档地址:

此处要说一下,这里百度的要哭了,看了所有的文档,都写的<Route path="" component=""></Route>,然而一直报错,但都没有找到我需要的。。。。最后问了一个同事,他说现在的写法是<Route path="/" element={<Compoent/>}></Route>。鬼知道我为什么要用一天时间百度路由这个东西。

  1. 错误写法!!!image.png
TypeScript error in ....
Type '{ path: string; component: Element; }' is not assignable to type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'.
  Property 'component' does not exist on type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'. 
  1. 正确写法!!!App.tsx
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PageA from "./pages/pageA";
import PageB from "./pages/pageB";
const Root = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<PageA />}></Route>
        <Route path="/pageA" element={<PageA />}></Route>
        <Route path="/pageB" element={<PageB />}></Route>
      </Routes>
    </BrowserRouter>
  );
};

export default Root;

PageA.tsx:

import React from "react";
import { Link } from "react-router-dom";
class PageA extends React.Component {
  render() {
    return (
      <div>
        <div>PageA</div>
        <Link to="/pageB">跳转pageB</Link>
      </div>
    );
  }
}

export default PageA;

PageB.tsx:

import React from "react";
import { Link } from "react-router-dom";
class PageB extends React.Component {
  render() {
    return (
      <div>
        <div>pageB</div>
        <Link to="/pageA">跳转pageA</Link>
      </div>
    );
  }
}

export default PageB;

效果如下:

GIF 2021-12-16 9-33-28.gif

4.axios封装:

1. 安装axios

npm install axios
or use yarn...
yarn add axios

2.封装axios:

之前写vue3+ts时有写过,直接拿过来修改下Ui组件就可以,再贴一次代码:

import axios, {
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse,
} from "axios";
import { message } from "antd";

//返回数据规则
export interface IResponseData<T> {
  status?: number;
  msg?: string;
  img?: string;
  data?: T;
  code: number;
  captchaOnOff?: boolean;
  uuid?: string;
  token?: string;
}


const state = {
  ok: 200, //请求成功状态码
};

//请求默认配置规则
type TOption = {
  baseURL: string;
  timeout: number;
};

//默认配置
const config = {
  baseURL: "/api",
  timeout: 30 * 1000,
  withCredentials: true,
};

const whiteUrl = ["/captchaImage", "/login"];

class Http {
  service: any;
  constructor(config: TOption) {
    //实例化请求配置
    this.service = axios.create(config);

    //请求拦截
    this.service.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        let stateToken = localStorage.getItem("token") as string;
        if (config.url && !whiteUrl.includes(config.url) && stateToken) {
          (config.headers as AxiosRequestHeaders).authorization = stateToken;
        }

        return config;
      },
      (error: any) => {
        return Promise.reject(error);
      }
    );

    //响应拦截
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const data = response.data;
        const { code } = data;

        if (!code) {
          //如果没有返回状态码,直接返回数据,针对于返回数据为blob类型
          return response;
        } else if (code !== state["ok"]) {
          message.error(data.msg || "请求异常");
          return Promise.reject(data);
        }
        return response.data;
      },
      (error: any) => {
        message.error("请求失败");
        return Promise.reject(error);
      }
    );
  }

  get<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
    return this.service.get(url, { params, ...data });
  }

  post<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
    return this.service.post(url, params, data);
  }

  put<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
    return this.service.put(url, params, data);
  }

  delete<T>(
    url: string,
    params?: object,
    data = {}
  ): Promise<IResponseData<T>> {
    return this.service.delete(url, { params, ...data });
  }
}

export default new Http(config);

3.使用:

随便找了个掘金的请求地址,在App.tsx中测试下:

import React from "react";
import http from "./utils/http";

const Root = () => {
  http.post("https://mcs.snssdk.com/v1/list",{ /*请求的参数*/},{/*添加默认请求配置如header,baseUrl等*/}).then((res: any) => {
      console.log(res);
    }).catch((err: any) => {
      console.log(err);
    });
  
  return <h1>主页</h1>;
};

export default Root;

image.png

其他

之前有写从零搭建后台系统(Vue3.0+ElementPlus+TS+Vite),想把后续过程记录下来,暂时还没时间继续写完。

以前喜欢写笔记,一本一本,写完就扔了,掘金给我提供了一种新的记笔记的方式,O(∩_∩)O哈哈~