目录
一、环境搭建
1、安装
2、安装webpack
3、配置淘宝镜像(看个人需求)
4、安装create-react-app
5、创建项目
6、本地服务启动
二、项目框架结构
三、常用的集成和配置
1、React Router(路由配置管理)
2、redux(数据管理)
2.1 在函数组件里使用
2.2 在Class组件里使用
3、axios
4、UI组件
5、craco
6、多环境打包运行配置
1.1、内置环境
1.2、dotenv
7、使用代理proxy(http-proxy-middleware)
四、开箱即用的前端开发框架
1、UMI
2、Ant Design Pro
学习参考文档
一、环境搭建
1、安装
官网地址:
2、安装webpack
npm install -g webpack
3、配置淘宝镜像(看个人需求)
如果在本地一直装不上npm的包,可以考虑用国内的淘宝镜像,使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm
-
npm install -g cnpm --registry=
-
-
npm config set registry
4、安装create-react-app
npm install -g create-react-app
5、创建项目
-
create-react-app my-project // 创建名为my-project的项目
-
-
cd my-project // 进入项目文件夹
6、本地服务启动
npm run start // 启动本地server用于开发
二、项目框架结构
-
|- node_modules // 项目依赖包
-
|- public // 一般用于存放静态文件,打包时会被直接复制到输出目录(./buidle)
-
|- src // 项目源代码
-
| |- assets // 静态资源文件夹,用于存放静态资源,打包时会经过 webpack 处理
-
| |- components // 组件文件夹,存放 React 组件,一般是该项目公用的无状态组件
-
| |- MLayout
-
| |-
-
| |-
-
| |- containers // 页面视图文件夹
-
| |- Index
-
| |-
-
| |-
-
| |- redux // redux文件夹
-
| |- actions
-
| |-
-
| |- reducers
-
| |-
-
| |- store
-
| |-
-
| |-
-
| |- router // 路由配置文件夹
-
| |- // 路由文件配置
-
| |- service // 服务请求文件夹
-
| |- // 请求接口文件
-
| |- // 请求配置文件
-
| |- // 入口文件
-
| |-
-
| |- //注册路由与服务
-
| |-
-
| |- serviceWorker //开发配置
-
|- .env // 环境配置文件
-
|- . // dev环境配置文件
-
|- . // test环境配置文件
-
|- . // prd环境配置文件
-
|- // craco配置文件
-
|- // 包管理代码
-
|- .gitignore // Git忽略文件
三、常用的集成和配置
1、React Router(路由配置管理)
React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。
1.1、安装react-router
npm install react-router@4.3.1 --save
- Router下面只能包含一个盒子标签,类似这里的div。
- Link代表一个链接,在html界面中会解析成a标签。作为一个链接,必须有一个to属性,代表链接地址。这个链接地址是一个相对路径。
- Route,有一个path属性和一个组件属性(可以是component、render等等)。
1.2、基本使用:
-
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>
-
)
-
}
1.3 路由嵌套使用
-
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;
1.4、把路由路径写在一个配置文件里,示例代码如下
-
import Login from '../containers/Login/';
-
import Index from '../containers/Index/';
-
import Home from '../containers/';
-
import New from '../containers/';
-
import NewOne from '../containers/';
-
-
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;
在里导入使用
-
import './';
-
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
-
import routes from './router/router';
-
import Home from './containers/';
-
-
// 默认语言为 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>
-
{
-
((route, key) => {
-
(route)
-
if () {
-
return <Route key={key} exact path={} component={}/>
-
} else {
-
return <Route key={key} path={}
-
render={props => < {...props} routes={} />
-
}/>
-
}
-
})
-
}
-
<Route component={Home}/>
-
</Switch>
-
</Router>
-
</div>
-
</ConfigProvider>
-
);
-
}
-
-
export default App;
(父页面)
-
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
-
-
function New(props) {
-
return <div>
-
new
-
{
-
((route, key) => {
-
()
-
return <Route key={key} exact path={} component={}/>
-
})
-
}
-
</div>
-
}
-
-
export default New
(嵌套的子页面)
-
function NewOne() {
-
return <div>new one</div>
-
}
-
-
export default NewOne
2、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
2.1 在函数组件里使用
(声明变量默认值)
-
// state的默认值统一放置在文件
-
-
//
-
// 声明默认值
-
// 这里我们列举两个示例
-
// 同步数据:pageTitle
-
// 异步数据:infoList(将来用异步接口获取)
-
-
export default {
-
pageTitle: '首页',
-
infoList: []
-
}
(定义触发事件)
-
//
-
// action也是函数
-
-
export function increaseAction (data) {
-
return (dispatch, state) => {
-
dispatch({ type: 'increase', data: data })
-
}
-
}
-
-
export function setInfoList (data) {
-
return (dispatch, getState) => {
-
// // 使用fetch实现异步请求
-
// ('/api/getInfoList', {
-
// method: 'GET',
-
// headers: {
-
// 'Content-Type': 'application/json'
-
// }
-
// }).then(res => {
-
// return ()
-
// }).then(data => {
-
// let { code, data } = data
-
// if (code === 0) {
-
// dispatch({ type: 'SET_INFO_LIST', data: data })
-
// }
-
// })
-
}
-
}
(获取和处理数据)
-
//
-
// 工具函数,用于组织多个reducer,并返回reducer集合
-
import { combineReducers } from 'redux'
-
// 默认值
-
import defaultState from '../'
-
-
// 一个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实例)
-
// 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/'
-
-
-
// 创建store实例
-
let store = createStore(
-
reducers,
-
applyMiddleware(thunk)
-
)
-
-
export default store
在根文件里使用,向目标元素渲染容器组件App
这里的组件Provider是一个react-redux中特殊的组件
1. Provider中有且只有一个子组件(这里就是App容器组件,不一定是容器组件,根据自己的业务需求自己操作)
2. 使用Provider组件的好处是,只需要给Provider组件设置属性,那么其子组件和其子组件中的子组件都可以直接使用其对应的属性
3. 避免了组件嵌套之后一个一个传递的复杂操作
-
import React, { useEffect } from 'react';
-
import ReactDOM from 'react-dom';
-
import './';
-
import App from './App';
-
import reportWebVitals from './reportWebVitals';
-
-
// Provider是react-redux两个核心工具之一,作用:将store传递到每个项目中的组件中
-
// 第二个工具是connect,稍后会作介绍
-
import { Provider } from 'react-redux'
-
// 引入创建好的store实例
-
import store from './redux/store/'
-
-
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())
-
// or send to an analytics endpoint. Learn more: /CRA-vitals
-
reportWebVitals();
在UI页面里使用
-
import { Layout, Button, Form, Input } from 'antd';
-
import style from './';
-
import { useRef } from 'react'
-
-
// connect方法的作用:将额外的props传递给组件,并返回新的组件,组件在该过程中不会受到影响
-
import { connect } from 'react-redux';
-
// 引入action
-
import { increaseAction, setInfoList } from '../../redux/actions/'
-
-
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)
2.2 在Class组件里使用
安装react-redux
依赖包
-
npm i react-redux --save
-
npm i @types/react-redux --save-dev
src/
-
import React from 'react'
-
import ReactDOM from 'react-dom'
-
import { Provider } from 'react-redux' // react-redux 利用上下文context,提供的数据组件Provider
-
import 'antd/dist/'
-
import './'
-
import App from './App'
-
import 'i18n'
-
-
import store from 'redux/store'
-
-
ReactDOM.render(
-
<>
-
{/* 使用Provider, 并加载 数据仓库 store, 就可以在全局范围内使用store */}
-
<Provider store={store}>
-
<App />
-
</Provider>
-
</>,
-
document.getElementById('root')
-
)
Header
组件中使用
-
import { Component } from 'react'
-
import { withRouter, RouteComponentProps } from 'react-router-dom'
-
import { MenuInfo } from 'rc-menu/lib/interface'
-
import { nanoid } from 'nanoid'
-
// 使用react-redux
-
import { Dispatch } from 'redux'
-
import { connect } from 'react-redux'
-
import store, { RootState } from 'redux/store'
-
import { addLanguageActionCreator, changeLanguageActionCreator } from 'redux/language/actionCreators'
-
-
/* connect 就是一个高阶hoc,只不过它的命名没有使用withxxxx来表示,class 类组件 使用 withxxx 高阶hoc组件
-
使用了connect,就相当于, 即组件订阅了store中的数据
-
*/
-
-
-
/* mapStateToProps: 处理数据的流入。返回一个对象
-
使用connect函数,传入mapStateToProps,完成store数据与组件的props绑定
-
*/
-
// 高阶hoc包裹的组件,可以获取到一些额外的props或state属性,connect函数传入参数 mapStateToProps, 组件的props会被注入一些额外的props属性
-
const mapStateToProps = (state: RootState) => {
-
return {
-
lng: state.lng,
-
languageList: state.languageList
-
}
-
}
-
-
/* mapDispatchToProps: 处理数据的流出。返回一个对象,对象中的每一个字段都是一个dispatch处理函数
-
将dispatch绑定到props中
-
*/
-
const mapDispatchToProps = (dispatch: Dispatch) => {
-
return {
-
addLanguageDispatch: (language: { code: string, language: string }) => {
-
dispatch(addLanguageActionCreator(language))
-
}
-
changeLanguageDispatch: (lng: 'en'|'zh') => {
-
dispatch(changeLanguageActionCreator(lng))
-
}
-
}
-
}
-
-
class HeaderComponent extends Component<RouteComponentProps & ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>>{
-
-
render(){
-
const { history, lng, languageList, addLanguageDispatch, changeLanguageDispatch } = this.props
-
-
/* meun 的点击事件 */
-
const oprateLanguage = ( e: MenuInfo ) => {
-
if(e.key !== 'new'){
-
changeLanguageDispatch(e.key)
-
}else{
-
addLanguageDispatch({code: `lng${nanoid()}`, language: '新语种'})
-
}
-
}
-
-
const menu = (
-
<Menu>
-
< key='new' onClick={oprateLanguage}>
-
添加新语言
-
</>
-
{(language => (
-
< key={} onClick={oprateLanguage}>
-
{}
-
</>
-
))}
-
</Menu>
-
)
-
-
return (
-
<div>
-
< className='mr40'>让旅游更幸福</>
-
<Dropdown overlay={menu}>
-
<Button>
-
{(language => === lng)?.language}
-
<GlobalOutlined />
-
</Button>
-
</Dropdown>
-
<>
-
<Button onClick={() => ('/signIn')}>登录</Button>
-
<Button onClick={() => ('/register')}>注册</Button>
-
</>
-
</div>
-
)
-
}
-
}
-
-
export const Header = connect(mapStateToProps, mapDispatchToProps)(withRouter(HeaderComponent))
store
数据封装
- 新建目录:
src/redux
、src/redux/language
- 新建文件:
src/redux/
、src/redux/language/
,src/redux/language/
-
mkdir src/redux src/redux/language
-
touch src/redux/language/
-
touch src/redux/language/
-
import { createStore } from 'redux'
-
import languageReducer form './language/'
-
-
const store = createStore(languageReducer)
-
-
// 使用ts的条件类型 ReturnType<T>,T:函数类型。 获取函数返回值的类型
-
export type RootState = ReturnType<typeof store.getState>
-
-
export default store
工厂模式创建action-
-
/* 用常量定义,减少代码敲错 */
-
export const ADD_LANGUAGE = 'language/add'
-
export const CHANGE_LANGUAGE = 'language/change'
-
-
/* action的类型申明 */
-
const AddActionProps = {
-
type: typeof ADD_LANGUAGE,
-
payload: { code: string, language: string }
-
}
-
const ChangeActionProps = {
-
type: typeof CHANGE_LANGUAGE,
-
payload: 'zh' | 'en'
-
}
-
-
export type LanguageActionProps = AddActionProps | ChangeActionProps
-
-
/* 用工厂模式创建action */
-
export const addLanguageActionCreator = (language: {code: string, language: string}):ADD_LANGUAGE => {
-
return {
-
type: ADD_LANGUAGE,
-
payload: language
-
}
-
}
-
export const changeLanguageActionCreator = (lng: 'zh' | 'en'):CHANGE_LANGUAGE => {
-
return {
-
type: CHANGE_LANGUAGE,
-
payload: lng
-
}
-
}
-
import { ADD_LANGUAGE, CHANGE_LANGUAGE, LanguageActionProps } from './actions'
-
-
export interface LanguageState {
-
lng: 'zh' | 'en',
-
languageList: {code: string, language: string}[]
-
}
-
-
const defaultStoreState: LanguageState = {
-
lng: 'zh',
-
languageList: [{ code: 'zh', language: '中文'}, { code: 'en', language: 'English'}]
-
}
-
-
-
export default (state = defaultStoreState, action:LanguageActionProps) => {
-
switch (action.type) {
-
case CHANGE_LANGUAGE:
-
return { ...state, lng: action.payload }
-
case ADD_LANGUAGE:
-
return { ...state, languageList: [...state.languageList, action.payload] }
-
default:
-
return state
-
}
-
}
3、axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 中。
安装
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 = ({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 = in httpCode ? httpCode[] :
-
// (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)
-
})
-
})
-
}
-
import {get, post} from './request'
-
-
export const requestIndex = () => {
-
return get('api/index').then((res) => {
-
// return
-
console.log(res)
-
})
-
}
4、UI组件
基于React的UI组件在这里推荐两个,一个是蚂蚁金服的Ant Design;另外一个是Material-UI。
两个都很不错,这里使用的是Ant Design。
安装
npm install antd --save
修改 src/
,引入 antd 的组件
-
import React from 'react';
-
import { Button } from 'antd';
-
import './';
-
-
const App = () => (
-
<div className="App">
-
<Button type="primary">Button</Button>
-
</div>
-
);
-
-
export default App;
修改 src/
,在文件顶部引入 antd/dist/
。
@import '~antd/dist/';
好了,现在你应该能看到页面上已经有了 antd 的蓝色按钮组件,接下来就可以继续选用其他组件开发应用了。其他开发流程你可以参考 create-react-app 的官方文档。
高级配置
这个例子在实际开发中还有一些优化的空间,比如无法进行主题配置。
此时我们需要对 create-react-app 的默认配置进行自定义,这里我们使用 craco (一个对 create-react-app 进行自定义配置的社区解决方案)。
现在我们安装 craco 并修改 里的
scripts
属性。
npm install @craco/craco
-
/* */
-
"scripts": {
-
- "start": "react-scripts start",
-
- "build": "react-scripts build",
-
- "test": "react-scripts test",
-
+ "start": "craco start",
-
+ "build": "craco build",
-
+ "test": "craco test",
-
}
然后在项目根目录创建一个 用于修改默认配置。
-
/* */
-
module.exports = {
-
// ...
-
};
'
运行
自定义主题
按照 配置主题 的要求,自定义主题需要用到类似 less-loader 提供的 less 变量覆盖功能。我们可以引入 craco-less 来帮助加载 less 样式和修改变量。
首先把 src/
文件修改为 src/
,然后修改样式引用为 less 文件。
-
/* src/ */
-
- import './';
-
+ import './';
-
/* src/ */
-
- @import '~antd/dist/';
-
+ @import '~antd/dist/';
然后安装 craco-less
并修改 文件如下
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
1、安装
-
$ yarn add @craco/craco
-
-
# OR
-
-
$ npm install @craco/craco --save
2、在项目根目录新建文件
3、修改 里的启动配置
-
/* */
-
"scripts": {
-
- "start": "react-scripts start",
-
+ "start": "craco start",
-
- "build": "react-scripts build",
-
+ "build": "craco build",
-
- "test": "react-scripts test",
-
+ "test": "craco test",
-
}
通过上面3步已经可以扩展webpack配置了,接下来就根据需求去配置即可。
示例代码:
-
/* */
-
/**
-
* TODO: 区分环境 —— NODE_ENV
-
* - whenDev ☞ .NODE_ENV === 'development'
-
* - whenTest ☞ .NODE_ENV === 'test'
-
* - whenProd ☞ .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
-
}) => {
-
// ='public'
-
paths.appBuild = outputDirName // 配合输出打包修改文件目录
-
// webpackConfig中可以解构出你想要的参数比如mode、devtool、entry等等,更多信息请查看文件
-
/**
-
* 修改 output
-
*/
-
webpackConfig.output = {
-
...webpackConfig.output,
-
path: path.resolve(__dirname, outputDirName), // 修改输出文件目录
-
publicPath: process.env.REACT_APP_PUBLICPATH // 设置的是部署应用包的基本 URL,不是项目打包出来的文件存放的位置
-
}
-
-
// 返回重写后的新配置
-
return webpackConfig
-
},
-
plugins: [
-
// compression-webpack-plugin 因为版本问题,将 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: '/',
-
// changeOrigin: true,
-
// secure: false,
-
// xfwd: false,
-
// }
-
// }
-
// }
-
};
6、多环境打包运行配置
1.1、内置环境
有一个名为 NODE_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:默认。
- .:本地覆盖。除 test 之外的所有环境都加载此文件。
- ., ., .:设置特定环境。
- ., ., .:设置特定环境的本地覆盖。
左侧的文件比右侧的文件具有更高的优先级:
- npm start: ., ., ., .env
- npm run build: ., ., ., .env
- npm test: ., ., .env (注意没有 . )
1.2、dotenv
我们可以使用 dotenv 来做环境变量的管理(dotenv
可将环境变量从 .env 文件加载到 中)
安装dotenv-cli:
npm install dotenv-cli -g
在 里修改命令
-
"scripts": {
-
"dev": "GENERATE_SOURCEMAP=false cross-env PORT=5000 craco start",
-
"test": "craco test",
-
"prd": "dotenv -e . craco start",
-
"build": "craco build",
-
"build:dev": "dotenv -e . craco build",
-
"build:test": "dotenv -e . 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/ 文件,可配置多个代理
-
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: "/mock/5c0f31837214cf627b8d43f0/",
-
changeOrigin: true
-
})
-
);
-
};
四、开箱即用的前端开发框架
1、UMI
umi是一个企业级的前端开发框架,开箱即用,内置路由、构建、部署、测试、Lint 等。内置微前端、数据流、权限、国际化、icons 方案、埋点、antd、请求、CSS 方案、图表等最佳实践。有完备的路由,基于 React Router 6,类 Remix,支持嵌套、动态、动态可选、预加载、基于路由的请求优化等。
Umi 有很多非常有意思的特性,比如:
1、企业级,在安全性、稳定性、最佳实践、约束能力方面会考虑更多
2、插件化,啥都能改,Umi 本身也是由插件构成
3、MFSU,比 Vite 还快的 Webpack 打包方案
4、基于 React Router 6 的完备路由
5、默认最快的请求
6、SSR & SSG
7、稳定白盒性能好的 ESLint 和 Jest
8、React 18 的框架级接入
9、Monorepo 最佳实践
...
官方文档:/
2、Ant Design Pro
Ant Design Pro 以 umi 作为脚手架,启动和开发与 umi 基本相同,是基于 Ant Design 和 umi 的封装的一整套企业级中后台前端/设计解决方案,为我们提供完整的脚手架,涉及国际化,权限,mock,数据流,网络请求等各个方面。为这些中后台中常见的方案提供了最佳实践来减少学习和开发成本。
官方使用文档:开始使用 - Ant Design Pro
学习参考文档
React 官方中文文档 – 用于构建用户界面的 JavaScript 库
Create React App 中文文档 · 通过运行一个命令来建立现代Web应用程序。
Introduction | React Router 中文文档
React Router: Declarative Routing for
React Router 使用教程 - 阮一峰的网络日志
Ant Design - The world's second most popular React UI framework
Redux中文文档
Redux 入门教程(一):基本用法 - 阮一峰的网络日志
Redux 入门教程(三):React-Redux 的用法 - 阮一峰的网络日志
axios中文网|axios API 中文文档 | axios
Vue CLI4.0 webpack配置属性——outputDir、assetsDir、indexPath_Jay的博客-****博客
Vue CLI4.0 webpack配置属性——publicPath_Jay的博客-****博客