项目新建从0-1,react+ts完整版

百里疏珂
2023-12-01

文章目录


前言

总结了从0创建项目的一系列命令以及安装步骤


提示:以下是本篇文章正文内容,下面案例可供参考

一、环境的搭建

1、安装node.js

        官网地址:Node.js

2、安装yarn 

安装yarn的前提是需要先安装好npm,且环境变量都配置好了,才可以使用。

下载全局安装
npm install yarn -g  
安装完检查版本 
yarn --version 、
配置镜像源
yarn config set registry https://registry.npm.taobao.org -g
yarn config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/ -g
检查镜像源
yarn config get registry // https://registry.npm.taobao.org
yarn config get sass_binary_site // https://npm.taobao.org/mirrors/node-sass/


一些常用的yarn 的命令
yarn init // 生成package.json文件
yarn install // 安装yarn.lock的所有依赖
yarn install --force // 重新安装依赖
yarn remove moduleName // 删除依赖
yarn add moduleName // 安装某个依赖
yarn add moduleName --dev/-D // 安装到开发环境
yarn run scriptName // 执行package.json命名的脚本命令

二、安装react项目

1.安装react+ts

        npx create-react-app my-app --template typescript
        npx create-react-app <项目名> --template typescript
        npm init @vitejs/app <项目名称>--template react-ts

 以下选择性安装

2.安装eslint代码检测

yarn add eslint
npx eslint --init

eslint初始化后会出现三个项目,根据项目而定

1、使用什么样的eslint?(这里我选择3)

To check syntax only // 只检测语法性错误
To check syntax and find problems // 检查语法错误并发现问题代码
To check syntax, find problems, and enforce code style // 检查语法错误,发现问题代码,校验代码风格

2、项目使用什么类型的模块?(这里我选择1)

JavaScript modules (import/export) // 允许import/export
CommonJS (require/exports) // 允许require/exports
None of these // 没有任何模块化

3、项目使用哪个框架?(选择1)

React
Vue.js
None of these

4、项目使用Ts?(Yes)

Does your project use TypeScript? › No / Yes

5、代码运行环境?(浏览器)

Browser // 浏览器
Node // node环境

6、如何定义项目定义样式?(使用流行的风格指南)

Use a popular style guide // 使用流行的风格指南
Answer questions about your style // 问答定义形成一个风格

7、遵循哪一种流行风格?(可以根据自己项目所需定义,我选Airbnb)

Airbnb: https://github.com/airbnb/javascript
Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google
XO: https://github.com/xojs/eslint-config-xo

8、你希望你的配置文件是什么格式的?(JavaScript,其它的可以自行百度)

JavaScript
YAML
JSON

9、你现在就安装他们吗?(Yes,yarn)

9、你现在就安装他们吗?(Yes,yarn)

安装完成后会在项目根目录生成.eslitrc.js文件,然后改一下规则(可以根据自己需求增减规则)

module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'plugin:react/recommended',
    'airbnb',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      tsx: true,
    },
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: [
    'react',
    '@typescript-eslint',
  ],
  rules: {
    'react/jsx-filename-extension': [
      2,
      { 'extensions': ['ts', 'tsx'] }
    ],
    'import/no-unresolved': 0,
    'import/extensions': 0,
    
    // 最后一个对象属性要有逗号
    'comma-dangle': 1,
    
    // 定义但从未使用的变量
    'no-unused-vars': 1,
    
    // 赋值但从未使用
    'react/jsx-no-bind': 1,
    
    // 空标签
    'react/self-closing-comp': 0,
    
    // 具有单击处理程序的可见非交互元素必须至少有一个键盘侦听器
    'jsx-a11y/click-events-have-key-events': 0,
    
    // 具有“按钮”交互作用的元素必须是可聚焦的
    'jsx-a11y/interactive-supports-focus': 0,
    
    // 带有事件处理程序的静态HTML元素需要一个角色
    'jsx-a11y/no-static-element-interactions': 0,
    
    // return ;
    'semi-spacing': 0,
    
    // <>只包含不能只包含一个标签
    'react/jsx-no-useless-fragment': 0,
    
    // 值对于布尔属性必须省略
    'react/jsx-boolean-value': 0,
    
    // 必须默认导出
    'import/prefer-default-export': 0,
    
    // 默认变量放到最后一个
    'default-param-last': 0,
    
    // 对参数进行赋值
    'no-param-reassign': 0,
    
    // 使用未声明的函数
    'no-use-before-define': 0,
    
    'no-new': 0,
    
    // 不能使用自增
    'no-plusplus': 0,
    
    // button必须是静态type
    'react/button-has-type': 0,
  },
};

        添加@别名指向src目录(React默认没有像vue有@别名)修改config-overrides.js文件,代码如下

const {
  override,
  addWebpackAlias,
  addWebpackModuleRule,
} = require('customize-cra');
const path = require('path');

module.exports = override(
  // @别名
  addWebpackAlias({
    '@': path.resolve('./src'),
  }),

  // scss全局变量
  addWebpackModuleRule({
    test: /\.scss$/,
    use: [
      'style-loader',
      'css-loader',
      'sass-loader',
      {
        loader: 'sass-resources-loader',
        options: {
          resources: ['./src/assets/scss/varable.scss'],
        },
      },
    ],
  }),
);

然后在根目录下tsconfig.json文件更改代码如下

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

三、项目运行

因为直接开启eslist的缘故,所以直接运行会报eslist,默认的项目这里需要更改两个地方

安装 antd 

yarn add antd 

主入口引入样式
import “antd/dist/antd.css”   
这种情况会有警告
出现这个问题是因为 react-script 升级到5.0.0.
通过npx create-react-app 创建的项目,
引入antd.css 之后会看到这个警告。


解决方法就是
import “antd/dist/antd.css”
替换为
import “antd/dist/antd.min.css”

1、App.tsx文件更改为

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

2.先改package.json文件(找到scripts改为如下代码)

"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-scripts eject"
},

3、之后直接yarn start运行项目即可

四、项目框架结构

|- node_modules // 项目依赖包
|- public // 一般用于存放静态文件,打包时会被直接复制到输出目录(./buidle)
|- src // 项目源代码
  |  |- assets // 静态资源文件夹,用于存放静态资源,打包时会经过 webpack 处理
  |  |- components // 组件文件夹,存放 React 组件,一般是该项目公用的无状态组件
          |  |- MLayout
                  |  |- MLayout.js
                  |  |- MLayout.module.less
  |  |- containers // 页面视图文件夹
          |  |- Index
                  |  |- Index.js
                  |  |- Index.module.less
  |  |- redux // redux文件夹
          |  |- actions
                  |  |- actions.js
          |  |- reducers
                  |  |- reducers.js
          |  |- store
                  |  |- store.js
          |  |- state.js
  |  |- router // 路由配置文件夹
          |  |- router.js // 路由文件配置
  |  |- service // 服务请求文件夹
          |  |- http.js // 请求接口文件
          |  |- request.js // 请求配置文件
  |  |- App.js // 入口文件
  |  |- App.less
  |  |- index.js //注册路由与服务
  |  |- index.css
  |  |- serviceWorker //开发配置
|- .env // 环境配置文件
|- .env.development // dev环境配置文件
|- .env.test // test环境配置文件
|- .env.production // prd环境配置文件
|- craco.config.js // craco配置文件
|- package.json // 包管理代码
|- .gitignore // Git忽略文件

五、常用的集成和配置

1、React Router(路由配置管理)

React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。

1.1、安装react-router

npm install react-router@4.3.1 --save
或者用 yarn add 

Router下面只能包含一个盒子标签,类似这里的div。 
Link代表一个链接,在html界面中会解析成a标签。作为一个链接,必须有一个to属性,代表链接地址。这个链接地址是一个相对路径。 
Route,有一个path属性和一个组件属性(可以是component、render等等)。

基本使用

render () {
    return (
          <Router>
             <div>
                <ul>
                   <li><Link to="/index">首页</Link></li>
                   <li><Link to="/other">其他页</Link></li>
                 </ul>
                 <Route path="/index" component={Index}/>
                 <Route path="/other" component={Other}/>
             </div>
          </Router>
    )
}

路由嵌套使用

const Test = () => (
   <div>
      <Switch>
         <Route
            path="/"
            render={props => (
               <App>
                  <Switch>
                     <Route path="/" exact component={Index} />
                     <Route path="/index" component={Index} />
                     <Route path="/other" component={Other} />
                     <Route path="/message/:id/:TestId" component={Message} />
                      {/*路由不正确时,默认跳回home页面*/}
                     <Route render={() => <Redirect to="/" />} />
                  </Switch>
               </App>
            )}
         />
      </Switch>
   </div>
);
 
export default Test;

把路由路径写在一个配置文件router.js里,示例代码如下

import Login from '../containers/Login/Login.js';
import Index from '../containers/Index/Index.js';
import Home from '../containers/Home.js';
import New from '../containers/New.js';
import NewOne from '../containers/NewOne.js';
 
let routes = [
  {
    path: "/",
    component: Home,
    exact: true
  },
  {
    path: "/login",
    component: Login,
    exact: true
  },
  {
    path: "/index",
    component: Index,
    exact: true
  },
  {
    path: "/news",
    component: New,
    routes: [  // 嵌套路由
      {
        path: "/news/newsone",
        component: NewOne,
        exact: true
      }
    ]
  }
];
 
export default routes;

在App.js里导入使用 

import './App.less';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import routes from './router/router';
import Home from './containers/Home.js';
 
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
import moment from 'moment';
import 'moment/locale/zh-cn';
import locale from 'antd/lib/locale/zh_CN';
import { ConfigProvider } from 'antd';
 
function App() {
  return (
    <ConfigProvider locale={locale}>
      <div className="App">
      <Router>
        <Switch>
        {
          routes.map((route, key) => {
            console.log(route)
            if (route.exact) {
              return <Route key={key} exact path={route.path} component={route.component}/>
            } else {
              return <Route key={key} path={route.path}
                render={props => <route.component {...props} routes={route.routes} />
              }/>
            }
          })
        }
        <Route component={Home}/>
        </Switch>
      </Router>
      </div>
    </ConfigProvider>
  );
}
 
export default App;

new.js(父页面)

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
 
function New(props) {
  return <div>
    new
    {
      props.routes.map((route, key) => {
        console.log(route.path)
        return <Route key={key} exact path={route.path} component={route.component}/>
      })
    }  
  </div>
}
 
export default New

newOne.js(嵌套的子页面)

function NewOne() {
  return <div>new one</div>
}
 
export default NewOne

2、redux(数据管理) 

redux 的项目使用详细

React是单向数据流,所以有些情况下单单依赖React自身无法实现组件之间的通信,故此需要redux

需要安装Redux相关依赖,由于不同版本之间可能存在兼容性问题,所以安装的时候最好制定版本,安装好之后,就可以在项目中引入redux了

npm install redux@3.7.2 --save
 
npm install redux-thunk@2.1.0 --save
 
npm install react-redux@5.0.6 --save

state.js(声明变量默认值)

// state的默认值统一放置在state.js文件
 
// state.js
// 声明默认值
// 这里我们列举两个示例
// 同步数据:pageTitle
// 异步数据:infoList(将来用异步接口获取)
 
export default {
  pageTitle: '首页',
  infoList: []
}

actions.js(定义触发事件)

// actions.js
// action也是函数
 
export function increaseAction (data) {
  return (dispatch, state) => {
    dispatch({ type: 'increase', data: data })
  }
}
 
export function setInfoList (data) {
  return (dispatch, getState) => {
    // // 使用fetch实现异步请求
    // window.fetch('/api/getInfoList', {
    //     method: 'GET',
    //     headers: {
    //         'Content-Type': 'application/json'
    //     }
    // }).then(res => {
    //     return res.json()
    // }).then(data => {
    //     let { code, data } = data
    //     if (code === 0) {
    //         dispatch({ type: 'SET_INFO_LIST', data: data })
    //     }
    // })
  }
}

 reducers.js(获取和处理数据)

// reducers.js
// 工具函数,用于组织多个reducer,并返回reducer集合
import { combineReducers } from 'redux'
// 默认值
import defaultState from '../state.js'
 
// 一个reducer就是一个函数
function pageTitle (state = defaultState.pageTitle, action) {
  // 不同的action有不同的处理逻辑
  switch (action.type) {
    case 'SET_PAGE_TITLE':
      return action.data
    default:
      return state
  }
}
 
function infoList (state = defaultState.infoList, action) {
  switch (action.type) {
    case 'SET_INFO_LIST':
      return action.data
    default:
      return state
  }
}
 
// 导出所有reducer
export default combineReducers({
    pageTitle,
    infoList
})

store.js(创建store实例)

// applyMiddleware: redux通过该函数来使用中间件
// createStore: 用于创建store实例
 
// 中间件,作用:如果不使用该中间件,当我们dispatch一个action时,
// 需要给dispatch函数传入action对象;但如果我们使用了这个中间件,那么就可
// 以传入一个函数,这个函数接收两个参数:dispatch和getState。这个dispatch可
// 以在将来的异步请求完成后使用,对于异步action很有用
 
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
// 引入reducer
import reducers from '../reducers/reducers.js'
 
 
// 创建store实例
let store = createStore(
  reducers,
  applyMiddleware(thunk)
)
 
export default store

在根文件index.js里使用,向目标元素渲染容器组件App

这里的组件Provider是一个react-redux中特殊的组件

1. Provider中有且只有一个子组件(这里就是App容器组件,不一定是容器组件,根据自己的业务需求自己操作)

2. 使用Provider组件的好处是,只需要给Provider组件设置属性,那么其子组件和其子组件中的子组件都可以直接使用其对应的属性

3. 避免了组件嵌套之后一个一个传递的复杂操作
 

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
 
// Provider是react-redux两个核心工具之一,作用:将store传递到每个项目中的组件中
// 第二个工具是connect,稍后会作介绍
import { Provider } from 'react-redux'
// 引入创建好的store实例
import store from './redux/store/store.js'
 
ReactDOM.render(
  <div>
    {/* 将store作为prop传入,即可使应用中的所有组件使用store */}
    <Provider store = {store}>
      <App />
    </Provider>
  </div>,
  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();

 在UI页面里使用

import { Layout, Button, Form, Input } from 'antd';
import style from './Login.module.less';
import { useRef } from 'react'
 
// connect方法的作用:将额外的props传递给组件,并返回新的组件,组件在该过程中不会受到影响
import { connect } from 'react-redux';
// 引入action
import { increaseAction, setInfoList } from '../../redux/actions/actions.js'
 
const { Header, Footer, Content } = Layout;
 
function Login(props) {
 
  const formRef = useRef(null);
 
  const onReset = () => {
    formRef.current.resetFields();
  };
 
  const onFinish = (values) => {
    console.log('Success:', values);
    props.history.push('/index')
  };
 
  const onFinishFailed = (errorInfo) => {
    console.log('Failed:', errorInfo);
  };
  
 
  return <Layout style={{height: '100vh'}}>
            ...省略代码
          </Layout>
}
 
// mapStateToProps:将state映射到组件的props中
const mapStateToProps = (state) => {
  return {
    pageTitle: state.pageTitle,
    infoList: state.infoList
  }
}
// mapDispatchToProps:将dispatch映射到组件的props中
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    increaseAction (data) {
        // 如果不懂这里的逻辑可查看前面对redux-thunk的介绍
        dispatch(increaseAction(data))
        // 执行increaseAction会返回一个函数
        // 这正是redux-thunk的所用之处:异步action
        // 上行代码相当于
        /*dispatch((dispatch, getState) => {
            dispatch({ type: 'SET_PAGE_TITLE', data: data })
        )*/
    },
    setInfoList (data) {
        dispatch(setInfoList(data))
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)

3、axios配置

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

安装

npm install axios

示例代码

request.js

import axios from 'axios'
 
const BASE_URL = process.env.BASE_URL;
const TIMEOUT = 5000;
 
const instance = axios.create({    // 创建axios实例,在这里可以设置请求的默认配置
  timeout: TIMEOUT, // 设置超时时间
  baseURL: BASE_URL // 根据自己配置的反向代理去设置不同环境的baeUrl
})
 
// 文档中的统一设置post请求头。下面会说到post请求的几种'Content-Type'
instance.defaults.headers.post['Content-Type'] = 'application/json'
 
let httpCode = {        //这里我简单列出一些常见的http状态码信息,可以自己去调整配置
  400: '请求参数错误',
  401: '权限不足, 请重新登录',
  403: '服务器拒绝本次访问',
  404: '请求资源未找到',
  500: '内部服务器错误',
  501: '服务器不支持该请求中使用的方法',
  502: '网关错误',
  504: '网关超时'
}
 
/** 添加请求拦截器 **/
instance.interceptors.request.use(config => {
  config.headers['token'] = sessionStorage.getItem('token') || ''
  // hide = message.loading({content: 'Loading...', duration: 0});
  // 在这里:可以根据业务需求可以在发送请求之前做些什么:例如我这个是导出文件的接口,因为返回的是二进制流,所以需要设置请求响应类型为blob,就可以在此处设置。
  if (config.url.includes('pur/contract/export')) {
    config.headers['responseType'] = 'blob'
  }
  // 我这里是文件上传,发送的是二进制流,所以需要设置请求头的'Content-Type'
  if (config.url.includes('pur/contract/upload')) {
    config.headers['Content-Type'] = 'multipart/form-data'
  }
  return config
}, error => {
  // 对请求错误做些什么
  return Promise.reject(error)
})
 
/** 添加响应拦截器  **/
instance.interceptors.response.use(response => {
  // hide()
  if (response.statusText === 'ok') {     // 响应结果里的statusText: ok是我与后台的约定,大家可以根据实际情况去做对应的判断
    return Promise.resolve(response.data)
  } else {
    message.error('响应超时')
    return Promise.reject(response.data.message)
  }
}, error => {
  // hide()
  if (error.response) {
      // 根据请求失败的http状态码去给用户相应的提示
      // let tips = error.response.status in httpCode ? httpCode[error.response.status] : error.response.data.message
      // message.error(tips)
      if (error.response.status === 401) {    // token或者登陆失效情况下跳转到登录页面,根据实际情况,在这里可以根据不同的响应错误结果,做对应的事。这里我以401判断为例
        // 针对框架跳转到登陆页面
        this.props.history.push(LOGIN);
      }
      return Promise.reject(error)
  } else {
      message.error('请求超时, 请刷新重试')
      return Promise.reject('请求超时, 请刷新重试')
  }
})
 
/* 统一封装get请求 */
export const get = (url, params, config = {}) => {
  return new Promise((resolve, reject) => {
    instance({
        method: 'get',
        url,
        params,
        ...config
    }).then(response => {
        resolve(response)
    }).catch(error => {
        reject(error)
    })
  })
}
 
/* 统一封装post请求  */
export const post = (url, data, config = {}) => {
  return new Promise((resolve, reject) => {
    instance({
      method: 'post',
      url,
      data,
      ...config
    }).then(response => {
      resolve(response)
    }).catch(error => {
      reject(error)
    })
  })
}

http.js

import {get, post} from './request'
 
export const requestIndex = () => {
    return get('api/index').then((res) => {
        // return res.data
        console.log(res)
    })
}

4、UI组件 

基于React的UI组件在这里推荐两个,一个是蚂蚁金服的Ant Design;另外一个是Material-UI

两个都很不错,这里使用的是Ant Design。

安装

npm install antd --save

 修改 src/App.js,引入 antd 的组件

import React from 'react';
import { Button } from 'antd';
import './App.css';
 
const App = () => (
  <div className="App">
    <Button type="primary">Button</Button>
  </div>
);
 
export default App;

高级配置

这个例子在实际开发中还有一些优化的空间,比如无法进行主题配置。

此时我们需要对 create-react-app 的默认配置进行自定义,这里我们使用 craco (一个对 create-react-app 进行自定义配置的社区解决方案)。

现在我们安装 craco 并修改 package.json 里的 scripts 属性。
 

npm install @craco/craco
/* package.json */
"scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "test": "react-scripts test",
+   "start": "craco start",
+   "build": "craco build",
+   "test": "craco test",
}

然后在项目根目录创建一个 craco.config.js 用于修改默认配置。

/* craco.config.js */
module.exports = {
  // ...
};

 自定义主题

按照 配置主题 的要求,自定义主题需要用到类似 less-loader 提供的 less 变量覆盖功能。我们可以引入 craco-less 来帮助加载 less 样式和修改变量。

首先把 src/App.css 文件修改为 src/App.less,然后修改样式引用为 less 文件。
 

/* src/App.js */
- import './App.css';
+ import './App.less';
/* src/App.less */
- @import '~antd/dist/antd.css';
+ @import '~antd/dist/antd.less';

 然后安装 craco-less 并修改 craco.config.js 文件如下

npm install craco-less
const CracoLessPlugin = require('craco-less');
 
module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { '@primary-color': '#1DA57A' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

这里利用了 less-loader 的 modifyVars 来进行主题配置,变量和其他配置方式可以参考 配置主题 文档。修改后重启 npm start,如果看到一个绿色的按钮就说明配置成功了。

antd 内建了深色主题和紧凑主题,你可以参照 使用暗色主题和紧凑主题 进行接入。

eject

你也可以使用 create-react-app 提供的 yarn run eject 命令将所有内建的配置暴露出来。不过这种配置方式需要你自行探索,不在本文讨论范围内。

5、craco

安装

$ yarn add @craco/craco
 
# OR
 
$ npm install @craco/craco --save

2、在项目根目录新建craco.config.js文件

3、修改 package.json里的启动配置

/* package.json */
"scripts": {
-   "start": "react-scripts start",
+   "start": "craco start",
-   "build": "react-scripts build",
+   "build": "craco build",
-   "test": "react-scripts test",
+   "test": "craco test",
}

通过上面3步已经可以扩展webpack配置了,接下来就根据需求去配置即可。

示例代码:

/* craco.config.js */
/**
 * TODO: 区分环境 —— NODE_ENV
 * - whenDev ☞ process.env.NODE_ENV === 'development'
 * - whenTest ☞ process.env.NODE_ENV === 'test'
 * - whenProd ☞ process.env.NODE_ENV === 'production'
 */
 
const CracoLessPlugin = require('craco-less');
const CompressionPlugin = require("compression-webpack-plugin");
 
const path = require('path')
const pathResolve = pathUrl => path.join(__dirname, pathUrl)
 
console.log(process.env)
const dev = process.env.REACT_APP_ENV === 'development'
const test = process.env.REACT_APP_ENV === 'test'
const prod = process.env.REACT_APP_ENV === 'production'
const outputDirName = dev ? 'dev-dist' : (test ? 'test-dist' : 'dist')
 
 
module.exports = {
  webpack: {
    // 别名配置
    alias: {
      '@': pathResolve('src'),
    },
    /**
     * 重写 webpack 任意配置
     *  - configure 能够重写 webpack 相关的所有配置,但是,仍然推荐你优先阅读 craco 提供的快捷配置,把解决不了的配置放到 configure 里解决;
     *  - 这里选择配置为函数,与直接定义 configure 对象方式互斥;
     */
    configure: (webpackConfig, {
      env, paths
    }) => {
      // paths.appPath='public'
      paths.appBuild = outputDirName // 配合输出打包修改文件目录
        // webpackConfig中可以解构出你想要的参数比如mode、devtool、entry等等,更多信息请查看webpackConfig.json文件
        /**
         * 修改 output
         */
      webpackConfig.output = {
        ...webpackConfig.output,
          path: path.resolve(__dirname, outputDirName), // 修改输出文件目录
          publicPath: process.env.REACT_APP_PUBLICPATH // 设置的是部署应用包的基本 URL,不是项目打包出来的文件存放的位置
      }
      
      // 返回重写后的新配置
      return webpackConfig
    },
    plugins: [
      // compression-webpack-plugin 因为版本问题,2.x将 asset 改为了 filename
      new CompressionPlugin({
        filename: '[path].gz[query]', // 目标资源名称。[file] 会被替换成原资源。[path] 会被替换成原资源路径,[query] 替换成原查询字符串
        algorithm: 'gzip', // 算法       
        test: new RegExp('\\.(js|css)$'), // 压缩 js 与 css
        threshold: 10240, // 只处理比这个值大的资源。按字节计算
        minRatio: 0.8 // 只有压缩率比这个值小的资源才会被处理
      }),
    ]
  },
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { 
              // '@primary-color': '#1DA57A',
              '@heading-color': '#1890ff' // 标题色
            },
            javascriptEnabled: true,
          },
        },
      },
    }
  ],
  // devServer: {
  //   port: 9000,
  //   proxy: {
  //     '/api': {
  //       target: 'https://placeholder.com/',
  //       changeOrigin: true,
  //       secure: false,
  //       xfwd: false,
  //     }
  //   }
  // }
};

6、多环境打包运行配置

1.1、内置环境

有一个名为 NODE_ENV 的特殊内置环境变量。你可以从 process.env.NODE_ENV 中读取它。

当你运行 npm start 时,它总是等于 'development' 

当你运行 npm test 它总是等于 'test' 

当你运行 npm run build 来生成一个生产 bundle(包) 时,它总是等于 'production' 

你无法手动覆盖NODE_ENV 这可以防止开发人员意外地将慢速开发构建部署到生产环境中。

在 .env 中添加开发环境变量:

REACT_APP_BASE_URL=123
REACT_APP_ENV=development

  

注意:你必须以 REACT_APP_ 开头创建自定义环境变量。除了 NODE_ENV 之外的任何其他变量都将被忽略,以避免意外地在可能具有相同名称的计算机上公开私钥。更改任何环境变量都需要重新启动正在运行的开发服务器。

其实查看 create-react-app 的官方文档可以发现,create-react-app 默认是支持多个环境配置文件的:

  • .env:默认。
  • .env.local:本地覆盖。除 test 之外的所有环境都加载此文件。
  • .env.development, .env.test, .env.production:设置特定环境。
  • .env.development.local, .env.test.local, .env.production.local:设置特定环境的本地覆盖。
  • 左侧的文件比右侧的文件具有更高的优先级:
  • npm start: .env.development.local, .env.development, .env.local, .env
  • npm run build: .env.production.local, .env.production, .env.local, .env
  • npm test: .env.test.local, .env.test, .env (注意没有 .env.local )

1.2、dotenv

我们可以使用 dotenv 来做环境变量的管理(dotenv 可将环境变量从 .env 文件加载到 process.env中)

安装dotenv-cli:

npm install dotenv-cli -g

在 package.json 里修改命令

"scripts": {
    "dev": "GENERATE_SOURCEMAP=false cross-env PORT=5000 craco start",
    "test": "craco test",
    "prd": "dotenv -e .env.production craco start",
    "build": "craco build",
    "build:dev": "dotenv -e .env.development craco build",
    "build:test": "dotenv -e .env.test craco build",
    "start-old": "cross-env PORT=5000 react-scripts start",
    "build-old": "react-scripts build",
    "test-old": "react-scripts test",
    "eject": "react-scripts eject"
  },

7、使用代理proxy(http-proxy-middleware)

安装

npm install http-proxy-middleware

建立 src/setupProxy.js 文件,可配置多个代理

const proxy = require("http-proxy-middleware");
 
module.exports = function(app) {
  app.use(
    proxy("/base/**", {
      target: "http://45.32.15.21:8090/",
      changeOrigin: true
    })
  );
  app.use(
    proxy("/fans/**", {
      target: "https://easy-mock.com/mock/5c0f31837214cf627b8d43f0/",
      changeOrigin: true
    })
  );
};

学习参考文档:
React 官方中文文档 – 用于构建用户界面的 JavaScript 库

Create React App 中文文档 · 通过运行一个命令来建立现代Web应用程序。

Introduction | React Router 中文文档

React Router: Declarative Routing for React.js

React Router 使用教程 - 阮一峰的网络日志

Ant Design - The world's second most popular React UI framework

Redux中文文档

Redux 入门教程(一):基本用法 - 阮一峰的网络日志

Redux 入门教程(三):React-Redux 的用法 - 阮一峰的网络日志

axios中文网|axios API 中文文档 | axios


总结

本内容供自己参考学习有修改会及时更新

 类似资料: