JS版:React初始化项目及基础配置

1,638 阅读5分钟

前置条件

node >= 14,安装了yarn

一. 初始化项目及基础配置

1. create-react-app初始化项目

npx create-react-app my-app

注意:  第一行的 npx 不是拼写错误 —— 它是 [npm 5.2+ 附带的 package 运行工具]

你也可以基于以下模板初始化项目:

结合redux初始化项目 npx create-react-app my-app --template redux

结合typescript初始化项目 npx create-react-app my-app --template typescript

2. 目录结构调整

我们将目录结构调整为以下结构,方便项目管理:

└── /src
  ├── /pages // 页面存放处,相当于vue2的views
    ├── /src index.js
    └── App.js
  ├── /components // 公共组件
  ├── /api  // 后台接口地址
  ├── /mock  // 模拟数据
  ├── /store // 状态管理,同vue2功能一致
    ├── /features 状态配置
    └── index.js
  ├── /router // 路由配置
  ├── /utils // 常量、工具函数等
  └── /assets // 静态资源,如图片

调整后的文件目录结构如下显示:

image.png

更新./index.jsAppstore的引入路径

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
// 更改store的引入路径
import { store } from './store';
// 更改App的引入路径
import App from './pages/App';
import reportWebVitals from './reportWebVitals';
import './index.css';

更新完成后,启动一下服务,验证配置是否成功。

3. 安装Ant Design

安装Ant Design,简单写几个页面,方便后面添加路由测试页面

yarn add antd

无任何报错信息提示,表示安装成功:

image.png

4. 配置路由

接下来我们配置项目路由,相当于vue技术栈的vue-router,不同的是vue的生态vue-cli已将这些内置好,普通用户只需要使用即可,而react需要自行配置~~

官网文档react-router已更新至6.3.0,本文将参照官方文档配置。新项目路由配置参考: github.com/remix-run/r…

安装react-router 6至项目中

npm install react-router-dom@6

出现以下提示,表示安装成功:

image.png

修改./index.js文件如下所示:

import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import { store } from './store/store';
import App from './pages/App';
import reportWebVitals from './reportWebVitals';
import './index.css';

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>    
  </React.StrictMode>
);

// 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();


完成以上配置后,运行yarn start验证项目正常运行,代表路由加载成功。

配置简单的路由

接下来我们简单配两个路由,熟悉一下react-router6的新用法,在src/pages/login/Index.js中写入如下代码:

import { Link } from "react-router-dom";
import { Button } from 'antd';

function Login() {
  return (
    <div className="login">
      <h1>Welcome to React Router!</h1>
      <Button type="primary">登录</Button>      
      <br/>
      <Link to="/">首页</Link>
    </div>
  );
}

export default Login;

./pages/App.js改写如下:

import React from 'react';
import { Routes, Route, Link } from "react-router-dom";
import Login from './login/Index';
import './App.css';

function Home() {
  return (
    <>
      <main>
        <h2>Welcome to the homepage!</h2>
        <p>You can do this, I believe in you.</p>
      </main>
      <nav>
        <Link to="/login">登录</Link>
      </nav>
    </>
  );
}

function App() {
  return (
    <div className="App">
      <header className="App-header">        
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="login" element={<Login />} />
        </Routes>
      </header>
    </div>
  );
}

export default App;

修改后,启动服务,验证是否配置成功:

image.png

5. 设置路径别名

我们需要对 create-react-app 的默认配置进行自定义,create-react-app官方脚手架默认将webpack配置隐藏起来了,你也可以使用 create-react-app 提供的 yarn run eject 命令将所有内建的配置暴露出来,只是这个操作不可逆。

在这里我们使用 craco。 对create-react-app 的默认配置进行自定义,安装craco

yarn add  @craco/craco --save

在项目根目录创建craco.config.js

/* craco.config.js */
const path = require('path');

module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src') // 路径别名
    }
  }
};

修改 package.json 里的 scripts 属性将package.json的启动改为craco启动:

"start": "craco start",
"build": "craco build",
"test": "craco test",

配置完成后,启动一下服务,验证配置是否正确。

接下来我们继续尝试别名是否配置成功,增加了craco.config.js别名配置后,启动服务明显慢一点了

const path = require('path');

module.exports = {
  webpack: { // webpack配置
    // 配置别名
    alias: { // 设置别名是为了让后续引用的地方减少路径的复杂度
      "@": path.resolve("src"),
      "@utils": path.resolve("src/utils"),
    }
  }  
}

再次启动服务,验证别名配置是否成功。

6. 配置后端接口请求axios

接下来我们写个简单的页面,配置后端请求服务, 编写登录页面如下:

import React from 'react';
import { Form, Input, Button } from 'antd';

export default function LoginForm(){  
  const onFinish = (values) => {
    console.log({values})
  };

  const onFinishFailed = (errorInfo) => {
    console.log('Failed:', errorInfo);
  };

  return (
    <Form
      name="basic"
      labelCol={{
        span: 4,
      }}
      wrapperCol={{
        span: 20,
      }}
      initialValues={{
        remember: true,
      }}
      onFinish={onFinish}
      onFinishFailed={onFinishFailed}
      autoComplete="off"
      requiredMark={false}
      colon={false}
    >
      
      <Form.Item
        label="用户名"
        name="username"
        rules={[
          {
            required: true,
            message: '请输入用户名!',
          },
        ]}
      >
        <Input className='login-input' size='large'/>
      </Form.Item>

      <Form.Item
        label="密码"
        name="password"
        rules={[
          {
            required: true,
            message: '请输入密码!',
          },
        ]}
      >
        <Input.Password className='login-input' size='large'/>
      </Form.Item>

      <Form.Item
        label=" "
      >
        <Button type="primary" htmlType="submit" className='btn-login' block>
          登录
        </Button>
      </Form.Item>
    </Form>
  );    
}

index.css文件引入如下语句:

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

启动服务,刷新显示页面如下:

image.png

接下来我们需要向后端发送请求,安装axios:

yarn add axios --save-dev

安装成功后,在utils下新建request.js

import axios from 'axios';

const service = axios.create({
  // baseURL: '',
  timeout: 1000,
  headers: {'Content-Type': 'application/json;charset=utf-8'}
});

// 添加请求拦截器
service.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});

// 添加响应拦截器
service.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response;
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error);
});

export default service;

设置api/user.js文件:

/**
 * 用户
 */
import request from "@/utils/request";

// 登录
export const fetchLogin = (params) => {
  return request({
    url: "/sys/manager/login",
    method: "post",
    data: params,
  });
};

7. 配置反向代理,发送请求

设置craco.config.js如下:

const path = require('path');

module.exports = {
  webpack: { // webpack配置
    alias: { // 设置别名方便文件引用
      "@": path.resolve("src"),
      "@utils": path.resolve("src/utils"),
    }
  },
  devServer: {
    // 配置代理解决跨域问题
    proxy: {
      '/sys': {
        target: 'http://1xx.xx.xx.xx:8080', // 要请求的服务端目标IP地址
        changeOrigin: true, // 默认值:false 是否需要跨域
        // logLevel: 'debug', // 查看代理请求的真实地址
      }
    }
  }
}

修改login/index.js文件

import React from 'react';
import { Form, Input, Button } from 'antd';
import { fetchLogin } from '@/api/user'

export default function LoginForm(){  
  const onFinish = (values) => {
    console.log({values})
    fetchLogin(values).then(response => {
      console.log({response})
    })
  };

  const onFinishFailed = (errorInfo) => {
    console.log('Failed:', errorInfo);
  };

  return (
    <Form
      name="basic"
      labelCol={{
        span: 4,
      }}
      wrapperCol={{
        span: 20,
      }}
      initialValues={{
        remember: true,
      }}
      onFinish={onFinish}
      onFinishFailed={onFinishFailed}
      autoComplete="off"
      requiredMark={false}
      colon={false}
    >
      
      <Form.Item
        label="用户名"
        name="username"
        rules={[
          {
            required: true,
            message: '请输入用户名!',
          },
        ]}
      >
        <Input className='login-input' size='large'/>
      </Form.Item>

      <Form.Item
        label="密码"
        name="password"
        rules={[
          {
            required: true,
            message: '请输入密码!',
          },
        ]}
      >
        <Input.Password className='login-input' size='large'/>
      </Form.Item>

      <Form.Item
        label=" "
      >
        <Button type="primary" htmlType="submit" className='btn-login' block>
          登录
        </Button>
      </Form.Item>
    </Form>
  );    
}

点击登录,接口正常访问,登录接口调用成功。

image.png

5. 配置less编译

安装如下语句:

yarn add -D less less-loader
yarn add craco-less

index.css改为index.less,并更改如下:

@import '~antd/dist/antd.less';
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

./index.jsindex.css文件引入更改如下:

import './index.less';

craco.config.js更改如下:

const path = require('path');
const CracoLessPlugin = require('craco-less');

module.exports = {
  webpack: { // webpack配置
    alias: { // 设置别名方便文件引用
      "@": path.resolve("src"),
      "@utils": path.resolve("src/utils"),
    },
    module: {
      rules: [
        {
          test: /\.less$/i,
          use: [
            // compiles Less to CSS
            "style-loader",
            "css-loader",
            "less-loader",
          ],
        },
      ],
    },
  },
  devServer: {
    // 配置代理解决跨域问题
    proxy: {
      '/sys': {
        target: 'http://xxx:9098', // 要请求的服务端目标IP地址
        changeOrigin: true, // 默认值:false 是否需要跨域
      }
    }
  },
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

完成了基本的页面编写了,下一步我们配置状态管理,将需要统一获取的数据缓存到仓库中。

8. 安装redux

Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。它包含了 Redux 核心,并包含我们认为对于构建 Redux 应用必不可少的软件包和功能。Redux Toolkit简化了大多数 Redux 任务,防止了常见错误,使编写 Redux 应用程序更加容易。

yarn add @reduxjs/toolkit react-redux

接下来我们基于redux配置一个全局字典,./store/index.js代码如下:

import { configureStore } from '@reduxjs/toolkit';
import dictReducer from './features/platform/dictSlice';


export const store = configureStore({
  reducer: {
    dictionaryInfo: dictReducer,
  },
})

./store/features/platform/dictSlice.js代码如下:

import { createSlice } from '@reduxjs/toolkit'

import { fetchAreas } from '@/api/common/dictionary'

const initialState = {
  areasDict: [], // 字典
}

export const dictSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    initAreasDict: (state, action) => {
      state.areasDict = action.payload
    },

  },
})

// Action creators are generated for each case reducer function
export const { initAreasDict } = dictSlice.actions

export const AreasDictAsync = (params) => (dispatch) => {
  fetchAreas(params).then(response => {
    dispatch(initAreasDict(response.list))
  })
}

export default dictSlice.reducer

以上一个字典的缓存store就写好了,初始化时,在页面上调用

import { useDispatch } from 'react-redux';

const dispatch = useDispatch()
dispatch(AreasDictAsync())

在需要获取相应的值的页面:

import { useSelector } from 'react-redux';

const { areasDict } = useSelector(state => state.dictionaryInfo)

此时就能成功获取areasDict的值了。

9. 环境变量

create-react-app和vue-cli一样,内置了环境变量,当我们需要配置生产环境的访问地址时,这个时候就需要环境变量,在根目录上创建以下三个文件

  • .env: 公共环境变量
  • .env.development:本地环境
  • .env.production: 生产环境

.env.development文件配置如下:

REACT_APP_NODE_ENV = 'development'
REACT_APP_ENV = 'development'

REACT_APP_BASE_URL = ''

.env.production文件配置如下:

REACT_APP_NODE_ENV = 'production'
REACT_APP_ENV = 'production'

REACT_APP_BASE_URL = '后端服务地址'

然后我们配置下axios的baseURL,这样就可以根据环境调用不同的后端服务地址了, ./src/utils/request.js文件的baseURL设置如下:

import axios from 'axios';

const service = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
  timeout: 30000,
  headers: { 'Content-Type': 'application/json;charset=utf-8' }
});

这样,当我们运行yarn build时,取的就是.env.production的配置了。

重点:关于反向代理与环境变量的配置说明

发现有朋友使用这个文档配置反向代理和环境变量的时候,两者设置有重叠的地方,导致本地服务启动的时候代理设置不成功。其中.env.development文件中设置REACT_APP_BASE_URL = '',记住了此处为空字符串。

REACT_APP_BASE_URL = ''时,而又配置了代理,此时所有的服务端请求前端是走的代理服务器的地址,即craco.config.js文件中此处target指向的地址:

image.png

其中/sys是代理代理/sys前缀的IP地址,如果需要代理所有的地址,设置为/

检查代理是否设置成功,运行后查看向后端服务请求的IP地址:

image.png 当前缀是我们本地服务的地址时,即代理配置成功。

至此,我们已经完成了一个后台管理项目需要基本的配置,后面的就是在配置过程中的一些爬坑了。

二. 爬坑小集锦

在安装过程中遇到的报错及处理

安装antd报错

运行yarn add antd报错如下

yarn add v1.22.10
info No lockfile found.
[1/4] Resolving packages...
warning @testing-library/jest-dom > css > source-map-resolve@0.6.0: See https://github.com/lydell/source-map-resolve#deprecated
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x.
[2/4] Fetching packages...
error eslint@8.11.0: The engine "node" is incompatible with this module. Expected version "^12.22.0 || ^14.17.0 || >=16.0.0". Got "14.16.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.

这是在说我的node版本太低的,检查一下我的node版本

$ node -v
v14.16.0

我们升级一下node。

更新node版本

上官网下载最新的node包,我的是windows系统的,根据提示完成node安装后,再次查看node版本,已更新到v16.14.0

如无任何报错,表示安装成功。

安装craco报错cb() never called!

安装npm install @craco/craco --save报错如下:

$ npm install @craco/craco --save
npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it!
npm ERR! cb() never called!

npm ERR! This is an error with npm itself. Please report this error at:
npm ERR!     <https://npm.community>

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\jean\AppData\Roaming\npm-cache\_logs\2022-03-21T02_11_15_372Z-debug.log

删除package-lock.json和yarn-err文件,再次安装,可以了。

安装craco报错ERESOLVE unable to resolve dependency tree

安装craco报错 $ npm install @craco/craco --save

$ npm install @craco/craco --save
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: test-attestation@0.1.0
npm ERR! Found: react-scripts@5.0.1
npm ERR! node_modules/react-scripts
npm ERR!   react-scripts@"5.0.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react-scripts@"^4.0.0" from @craco/craco@6.4.3
npm ERR! node_modules/@craco/craco
npm ERR!   @craco/craco@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See C:\Users\Administrator\AppData\Local\npm-cache\eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Administrator\AppData\Local\npm-cache\_logs\2022-06-21T02_36_30_040Z-debug-0.log

换成yarn安装即可:

yarn add  @craco/craco --save

安装了craco,启动服务,报Can't resolve '@ant-design/icons'

感觉还没开始改什么东西,再启动服务就是又报错了,只好尝试把npm install --save @ant-design/icons再安装一遍又可以了

$ yarn start
yarn run v1.22.10
$ react-scripts start
Starting the development server...

(node:14564) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:14564) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Failed to compile.

Module not found: Error: Can't resolve '@ant-design/icons' in 'D:\homework\sports-web\src\pages\home'
assets by path static/js/*.js 4.7 MiB
  asset static/js/bundle.js 4.69 MiB [emitted] (name: main) 1 related asset
  asset static/js/node_modules_web-vitals_dist_web-vitals_js.chunk.js 6.93 KiB [emitted] 1 related asset
asset index.html 1.68 KiB [emitted]
asset asset-manifest.json 458 bytes [emitted]
cached modules 5.96 MiB (javascript) 31.5 KiB (runtime) [cached] 1074 modules

重新安装了图标包,启动服务,服务启动成功,而且能看到是caro启动的

image.png

启动服务时停留在Starting the development server...

运行yarn start后,无报错提示,一直停留在Starting the development server... 出现这种提示,请检查你的node版本是不是原服务安装的版本,前面为了兼容多项目不同的node版本依赖,引入了nvm管理node版本(具体使用请查看 Nodejs多版本管理工具nvm)。出现以下提示后,node版本切换为16.14.0即可正常启动服务。

$ yarn start
yarn run v1.22.10
$ react-scripts start
[HPM] Proxy created: /  -> http://120.xx.8.xx:8080
(node:1508) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:1508) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Starting the development server...

端口号被占用

$ yarn start
yarn run v1.22.17
$ craco start
Something is already running on port 3000.
Done in 21.22s.

启动[任务管理器],在进程中关闭git bash 和 node 进程,然后重新打开git bash,再次进入目录后 npm start发现成功。