react 开发者工具
react
用于构建用户界面的js库。
印记中文
中文文档,比如:react、react-router、redux、react-redux等
脚手架
npx 是 npm 自带的一个强大工具,它允许你直接运行 create-react-app 命令而不需要提前全局安装。它会在执行时自动下载最新版本,用完即焚,保证你每次创建项目使用的都是最新版。
npx create-react-app my-app
npx create-react-app my-app --template typescript // ts版本
npm install -g create-react-app
create-react-app my-app
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template react-ts // ts版本
淘宝镜像
npm config set registry https://registry.npmmirror.com
特点
组件化、声明式、使用虚拟DOM+diff算法减少与真实DOM的交互。
babel
es6转es5、jsx转js、默认开启了严格模式(use strict)
虚拟DOM
本质是object类型的对象、虚拟DOM比较轻(属性比较少),最终会被转为真实dom
创建虚拟dom的两种方式:
var VDOM = React.createElement('div', {id: 'lxh'}, React.createElement('span', {}, '不常用的创建方式'))
var VDOM = (
<div id='lxh'>
<span>jsx常用</span>
</div>
)
ReactDOM.render(VDOM, document.getElementById('root'))
渲染页面
ReactDOM.render(<Component />, document.getElementById('root'))
react解析组件标签,找到对应的组件,调用该组件,将返回的虚拟dom转为真实dom,呈现页面
jsx
全称JavaScript xml,本质是React.createElement(component, props, children)方法的语法糖。
使用规则:
- 不要写引号,比如
<div id={lxh}>{lxh}</div> - 在标签中混入js表达式要用{}
- 类名写className(因为es6中类定义的关键字是class,为了避免所以使用className)
- 内联样式style={{key: value}}
- 只能有一个根标签
- 标签必须闭合
- 标签名小写,则转html里的同名标签,没有则报错
- 标签名大写,react渲染对象的组件,没有则报错
- xml早期用于存储和传输数据
- 注释 {/* content */}
jsx是 React.createElement() 的语法糖
{{}}
外层括号表示要写js表达式,内层括号表示要写对象
if
function xx({ isT }) {
// 在 JSX 外部使用
if (isT) {
return <h1>xxx</h1>;
} else {
return <h1>yyy</h1>;
}
return <> {isT ? <h1>xxx</h1> : <h1>yyy</h1>} </>
return <> {isT && <h1>xxx</h1>} </>
return <>
{(() => {isT ? <h1>xxx</h1> : <h1>yyy</h1>})()} // 立即执行的函数
</>
// if、switch都可以写
let msg
if (isT) {
msg = <h1>xxx</h1>;
} else {
msg = <h1>yyy</h1>;
}
return <div>{msg}</div>;
for
function xxx({ arr }) {
return (
<ul>
{arr.map((item) => (
<li key={item.id}>{item.text}</li> // 使用数组的map()方法(最推荐)
))}
</ul>
);
// 在jsx外部使用
const _arr = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
_arr.push(<li key={item.id}>{item.text}</li>);
}
return <ul>{_arr}</ul>;
const _arr = [];
for (const item of arr) {
_arr.push(<li key={item.id}>{item.text}</li>);
}
return <ul>{_arr}</ul>;
{
arr.filter(item => item.isT).map(item => <li key={item.id}>{item.text}</li>)
}
}
组件
- 类式组件(适用于复杂组件(有State))
class LxhComponent {
state = { bool: true }
clickFun = () => {
this.setState({ bool: !this.state.bool })
}
render() {
return <div onClick={clickFun}>类式组件</div>
}
}
- 函数式组件(适用于简单组件(无state))当然现在可以使用hook函数了useState()
function LxhComponent () {
return <div>函数式组件</div>
}
样式的模块化
以 import styles from'./xxx.module.css' 的方式导入,然后通过 styles.btn 引用类名
state
state是一个对象
类式组件:修改state,使用setState,修改后的state会和原state合并
state = {};
对象式:this.setState({}, [callback])
函数式:函数式会接收到两个参数,state、props
this.setState((state, props) => {}, [callback])
函数式组件:使用useState
let [lxh, setLxh] = useState({name: "lxh", age: 24});
修改state:修改后的state会覆盖原state
第一种写法:setLxh({...lxh, age: 25});
第二种写法:setLxh((value) => newValue);
参数:第一次初始化的值在内部做缓存
props
props是一个只读对象
类式组件:vscode快捷键(rcep)
第一种:(常用)
// 限制类型
static propTypes = {
name: PropTypes.string.isRequired,
fun: PropTypes.func,
boo: PropTypes.bool,
}
// 设置默认值
static defaultProps = {
name: "lxh",
}
第二种:
组件名.propTypes = {}
组件名.defaultProps = {}
函数式组件:vscode快捷键(rafcp)
组件名.propTypes = {}
组件名.defaultProps = {}
children props、render props
相当于插槽slot
children props
<A>
<B></B>
</A>
{this.props.children}
<div children='xxx'></div>
<div>xxx</div>
问题:如果B组件需要A组件的数据,做不到
render props
三个组件:
parent组件:<A render={(data) => <B data={data}></B>} />
A组件:{this.props.render(内部state数据data)}
B组件:读取A组件传过来的数据{this.props.data}
constructor构造器
可写可不写
- 接收props,并且传给super(props),这样在构造器里就能拿到props
- 不接收props,在构造器里props就是undefined
ref
请勿过度使用ref
类式组件
第一种:字符串(不推荐,过时,效率不高),标签<input ref='xxx' />,通过this.refs.xxx获取
第二种:回调函数形式
如果是内联函数,在更新过程中会执行两次:
第一次传的值是null
第二次才传的DOM节点
使用标签<input ref={c => this.名称 = c} />
获取this.名称
这种情况无关要紧,可以写内联函数
如果需要避免执行两次:
不写内联函数,写成class绑定函数的形式
<input ref={this.xxx} />
xxx = (e) => {
this.yyy = e
console.log(e)
}
第三种:React.createRef,专人专用,如果使用多次,需要定义多次
<input ref={this.xxx} />
this.xxx = React.createRef()
this.xxx.current.value // 获取输入框的值
函数式组件
使用useRef()
const lxh = React.useRef()
lxh.current.value
context
常用于祖组件和后代组合间通信
const lxhContext = React.createContext() // 创建context容器对象
<lxhContext.Provider value={数据}> // 渲染子组件
子组件
</lxhContext.Provider>
// 后代组件读取数据
// 第一种方式:只适用于类式组件
static contextType = lxhContext // 声明接收context,才能获取到
this.context // 读取context中的value数据
// 第二种方式:函数组件与类式组件都可以
<lxhContext.Consumer>
{
value => {
return `${value}` // value就是context中的value数据
}
}
</lxhContext.Consumer>
const ctx = useContext(lxhContext) // 可以使用useContext获取数据
受控组件、非受控组件
表单中收集数据
受控组件:通过input提供的onChange()事件
<input onChange={this.xxx('yyy')} />
xxx = (name) => {
return (e) => {
this.setState({[name]: e.target.value})
}
}
<input onChange={(e) => this.xxx('yyy', e)} />
xxx = (name, e) => {
this.setState({[name]: e.target.value})
}
非受控组件:现取现用,操作DOM(ref),获取数据
推荐使用受控组件
高阶函数、函数的柯里化、纯函数
高阶函数:函数的参数是函数,函数的返回值是函数。符合其一就是高阶函数。
例如:Promise()、setTimeout()、map()等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式
this.lxh(1)(2)
lxh = (a) => {
return (b) => {
// a = 1、b = 2
}
}
纯函数:只要是同样的输入(实参),必定得到同样的输出(返回值)
生命周期
类式组件
旧
render() {
// 必须使用
}
componentDidMount() {
// 常用,一般用于开启定时器、发送网络请求、订阅消息
}
componentWillUnmount() {
// 常用:一般开闭定时器、取消订阅消息
}
componentWillReceiveProps(props) {
/**
* 第一次接收props时,不执行,props改变时,执行,即将移除此生命周期
* 使用:UNSAFE_componentWillReceiveProps() {}
**/
}
componentWillMount() {
// 即将移除此生命周期,使用:UNSAFE_componentWillMount() {}
}
componentWillUpdate() {
// 即将移除此生命周期,使用:UNSAFE_componentWillUpdate
}
shouldComponentUpdate() {
// 必须写返回值,不然报错
return true // 不写这个生命周期,默认返回true
}
this.forceUpdate() // 强制更新
卸载组件:
ReactDOM.unmountComponentAtNode(document.getElementById('xxx'))
触发componentWillUnmount(),但不能卸载createRoot()创建的组件
createRoot()创建的组件卸载方式:
const ROOT = createRoot(document.getElementById('xxx'))
(注意:后面加render函数这样写:ROOT.render())
export default ROOT
import root from '../index'
root.unmount()
新
废弃了3个,新增了2个
过期的生命周期:
UNSAFE_componentWillMount()
UNSAFE_componentWillReceiveProps()
UNSAFE_componentWillUpdate()
getDerivedStateFromProps(props, state) {
/**
* 不常用
* 若state的值在任何时候都取决于props,就可以使用
* 缺点:派生状态会导致代码冗余,并使组件难以维护
* 使用时,前面加static
**/
return props // 必须返回一个state对象 或者 null,是对象与state合并
}
getSnapshotBeforeUpdate() {
/**
* 不常用
* 组件能在发生更改之前从DOM中捕获一些信息(如滚动位置),它的返回值传递给componentDidUpdate()
* 必须和componentDidUpdate一起使用,不然报错
* 必须返回一个值或者null,不能返回undefined
**/
return "snapshotValue" // 传给componentDidUpdate的第三个参数
}
componentDidUpdate(preProps, preState, snapshotValue) {
// 更新完成
}
例子:
import React, { Component } from "react";
class Home extends Component {
state = { arr: [] };
componentDidMount() {
let timer = setInterval(() => {
this.setState(
(state) => ({
arr: ["新闻" + (state.arr.length + 1), ...state.arr],
}),
() => {
if (this.state.arr.length >= 20) {
clearInterval(timer);
timer = null;
}
}
);
}, 1000);
}
componentWillUnmount() {}
getSnapshotBeforeUpdate() {
return this.refs.refStr.scrollHeight;
}
componentDidUpdate(preProps, preState, snapshot) {
this.refs.refStr.scrollTop += this.refs.refStr.scrollHeight - snapshot;
}
render() {
return (
<div
style={{
height: 100,
width: 200,
backgroundColor: "pink",
overflow: "auto",
}}
ref="refStr"
>
{this.state.arr.map((item, index) => {
return (
<div style={{ height: 20 }} key={index}>
{item}
</div>
);
})}
</div>
);
}
}
export default Home;
函数式组件
使用useEffect,可以在函数式组件中执行副作用操作(用于模拟类式组件里的生命周期钩子)
主要用于:
发送ajax请求
设置订阅、启动定时器
手动更改真实DOM
用法:
useEffect(() => {
// 在此可是执行任意带副作用操作
return () => {
// 在组件卸载前执行,比如清除定时器、取消订阅
}
}, []) // 如果指定的是[],回调函数只会在render()后执行
// 没有写[],就是监测所有state状态
/**
* 可以把useEffect看成是三个生命周期的组合
* componentDidMount
* componentDidUpdate
* componentWillUnmount
**/
路由
SPA:单页面应用,整个应用只有一个完整的页面,点击页面的链接,不会刷新页面,只会做页面的局部更新。
路由:一个路由就是一个映射关系(key:value)
key:路径、value:component或function
前端路由:浏览器端路由,value是component,用于展示页面内容
注册路由:<Route to='' component='' /> v5的方式
<Route to='' element='' /> v6的方式
工作过程:当浏览器的path等于对应的路由时,当前的路由就会变成对应的组件。
后端路由:value是function,用于处理客户端提交的请求
注册路由:router.get(path, (req, res) => {})
工作过程:当node接收到一个请求时,根据请求的路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。
react-router-dom
安装:npm i -S react-router-dom@5 // v5的版本,现在默认安装的是v6
npm i -S react-router-dom // v6版本
浏览器:强制刷新,按住shift,点刷新
将BrowserRouter 放在顶层,可以确保应用中任何组件(无论嵌套多深)都能使用路由钩子(如 useNavigate、useLocation)和组件(如 <Link>、<Routes>)
v5
// NavLink用于高亮,它的属性activeClassName(默认值:active)用于选中的样式
<NavLink activeClassName='' className='' to='' replace></NavLink>
<Link className='' to='' replacee></Link> // Link没有activeClassName属性,默认是push模式
<NavLink>content</NavLink> // children,通过props来获取,两个相同
<NavLink children='content'></NavLink>
// 解决样式丢失问题
<link href='./css/bootstrap.css' />
第一种解决:<link href='/css/bootstrap.css' /> // 不用用./css...
第二种解决:<link href='%PUBLIC_URL%/css/bootstrap.css' />
第三种解决:用hash模式
<Switch></Switch> // Switch用于匹配到适合的第一个path时,就不往下匹配了(v6已经移除)
<Route path='' component={xxx} exact></Route> // 严格匹配exact,不要随便开启,有时候开启不能匹配到二级路由
<Redirect to=''></Redirect> // Redirect重定向,一般写在路由的最下方
路由传参:
// params方式:刷新不会丢失
<Link to={`/home/${id}`}></Link>
<Route path='/home/:id' /> // 需要占位
// 获取参数通过props.match.params
this.props.history.push(`/home/${id}`) // 编程式
this.props.history.replace(`/home/${id}`) // 编程式
// search方式(相当于ajax的query参数)刷新不会丢失
<Link to={`/home/?id=${id}&title=${title}`}></Link>
this.props.history.push(`/home?id=${id}`) // 编程式
this.props.history.replace(`/home?id=${id}`) // 编程式
/**
* 不需要声明接收
* 获取参数通过props.location.search,比如拿到'?id=xxx&title=lxh'
* react默认安装了querystring
*/
import qs from 'querystring';
const { search } = this.props.location
const { id, title } = qs.parse(search.slice(1)) // 需要去掉前面的问号
// state方式:history模式刷新不会丢失,hash模式刷新会丢失
<Link to={{pathname: '/home', state: {id: 'xxx'}}}></Link>
// 获取参数通过props.location.state
this.props.history.push({path: '/home', state: {id: ''}}) // 编程式
this.props.history.replace({path: '/home', state: {id: ''}}) // 编程式
history里的其他方法
this.props.history.go(-1)
this.props.history.goBack() // 后退
this.props.history.goForward() // 前进
// 一般组件使用路由的跳转,使用withRouter()
src/router/index.js
import React, { lazy } from "react";
const Home = lazy(() => import("../pages/home")); // 路由懒加载lazy,Suspense配合路由懒加载使用
const Login = lazy(() => import("../pages/login"));
const Error = lazy(() => import("../pages/error"));
// 无权限
export const commonRouter = [
{
path: "/login",
component: Login,
exact: true,
meta: {
title: "登录",
}
},
{
path: "/404",
component: Error,
exact: true,
meta: {
title: "404",
}
},
];
// 有权限
export const authRouter = [
{
path: "/",
component: Home,
exact: true,
meta: {
title: "首页",
}
},
];
src/app.jsx
import React, { Suspense } from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { commonRouter, authRouter} from "./router";
import Layout from "./layout";
const App = () => {
return (
<BrowserRouter>
<Switch>
// Suspense配合路由懒加载lazy,fallback里写组件
<Suspense fallback={<>loading...</>}>
// 无权限
{
commonRouter.map(item => (<Route
key={item.path}
exact={item.exact}
render={(props) => {
document.title = item.meta.title;
return (<item.component {...props} />)
}}
/>)
)
}
// 有权限
{
authRouter.map(item => (<Route
key={item.path}
exact={item.exact}
render={(props) => {
document.title = item.meta.title;
sessionStorage.getItem("token")
? <Redirect to="/login" />
: <Layout><item.component {...props} /><Layout>
}}
/>)
)
}
<Redirect to="/404" />
</Suspense>
</Switch>
</BrowserRouter>
);
};
v6
- Switch移除了,Routes代替,Route必须被Routes包裹,不然报错
- Redirect移除了,Navigate重定向
- useInRouterContext判断组件是否是包路由包裹,返回值true、false
- useNavigationType返回当前的导航类型,返回值POP(刷新)、PUSH、REPLACE
- useOutlet获取当前组件中渲染的嵌套路由
- useResolvedPath给一个URL值,解析其中的path、search、hash值
<Route path='' element={<Lxh />} caseSensitive /> // component被element代替,caseSensitive区分path的大小写
<Route path='' element={<Navigate to='' />} />
<Navigate to='' replace></Navigate>
NavLink的高亮样式,yyy选中的样式,写法不一样了,子组件高亮,不希望父组件高亮,父组件加end属性
<NavLink className={({isActive}) => isActive ? 'xxx yyy' : 'xxx'}></NavLink>
const xxxClassName = ({isActive}) => isActive ? 'xxx yyy' : 'xxx';
<NavLink className={xxxClassName} end></NavLink>
import {useRoutes} from "react-router-dom"; // useRoutes路由表
useRoutes([
{
path: '/home/:id',
element: <Home />,
},
{
path: '/about',
element: <About />,
children: [
{
path: 'message', // 不写斜杠/
element: < />,
},
]
},
{
path: '*',
element: <Navigate to='/home' />,
},
])
/**
* <NavLink to='message'></NavLink> // 跳转子路由的写法
* <NavLink to='./message'></NavLink> // 跳转子路由的写法
* <Route index element={<Components />} /> 嵌套路由index是默认路由
* Outlet指定路由组件呈现的位置
*/
路由传参的方式:3种
params方式
<Link to={`/home/${id}`}></Link>
// 需要声明Route或useRoutes里:id
// 获取参数使用hooks函数,useParams
import { useParams } from 'react-router-dom';
const { id } = useParams()
search方式:相当于(query)
<Link to={`/home?id={id}&title=${title}`}></Link>
// 不需要声明
// 获取参数useSearchParams
import { useSearchParams } from 'react-router-dom';
const [search, setSearch] = useSearchParams() // 返回一个数组
const id = search.get('id') // 通过get获取对应的参数值
setSearch('id=xxx&title=yyy') // 更新search的参数
state方式:
<Link to=`/home` state={{id: ''}}></Link>
// 不需要声明
// 获取参数useLocation
import { useLocation } from 'react-router-dom';
const { state: { id } } = useLocation(); // 解构赋值直接使用id
编程式路由导航
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/home', {
replace: true,
state: {
id: 'xxx',
}
});
navigate(1) // 前进
navigate(-1) // 后退
用的不多的hooks函数
import { useMatch,
useInRouterContext,
useNavigationType,
useOutlet,
useResolvedPath,
} from 'react-router-dom';
useMatch('/home/:id') // 必须传入路径
useInRouterContext() // 返回Boolean值,查看是否是Route包裹的组件
useNavigationType() // 返回PUSH、REPLACE、POP,查看当前的导航类型,刷新后就变成的POP
useOutlet() // 查看当前组件的嵌套路由,返回null说明还没有挂载
useResolvedPath('/user/#/asd?id=xxx&title=yyy') // 传入一个url值,返回path、hash、search值
src/router/index.js
import React, { lazy } from "react";
import { useRoutes, Navigate, Outlet } from "react-router-dom";
const Home = lazy(() => import("../pages/home"));
const Login = lazy(() => import("../pages/login"));
const Error = lazy(() => import("../pages/error"));
export default () => {
return useRoutes([
{
path: "/login",
element: <Login />,
},
{
path: "/",
element: <Home><Outlet /></Home>, // Outlet可写组件里
children: [
{
path: "table",
element: <Table />,
},
],
},
{
path: "/404",
element: <Error />,
},
{
path: "*",
element: <Navigate to='/404' />,
},
]);
};
src/app.jsx
import React, { Suspense } from "react";
import { BrowserRouter as Router } from "react-router-dom";
import RouterAll from "./router";
const App = () => {
return (
<Router>
<Suspense fallback={<>loading...</>}>
<RouterAll></RouterAll>
</Suspense>
</Router>
);
};
export default App;
类式组件跳转
// tsx版
import React, { ComponentClass } from "react";
import {
NavigateFunction,
useLocation,
useNavigate,
useParams,
} from "react-router";
export interface IRouteProps<Params = any, State = any> {
location: State;
navigate: NavigateFunction;
params: Params;
}
export function withRouter<P extends IRouteProps>(Child: ComponentClass<P>) {
return (props: Omit<P, keyof IRouteProps>) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Child
{...(props as P)}
navigate={navigate}
location={location}
params={params}
/>
);
};
}
// 在组件里使用
import { withRouter, IRouteProps } from "../classRouter.tsx";
class Tags extends Component<IRouteProps> {
render() {
return <>
<button onClick={() => {
this.props.navigate('/home')
}}>跳转</button>
</>
}
}
export default withRouter(Tags)
Fragment
import React, { Fragment } from "react";
// 循环遍历使用Fragment,它只有一个属性key
<Fragment key={1}></Fragment>
<></> // 不能接收任何属性,一般用在根标签
PureComponent、memo
PureComponent
重写了:
shouldComponentUpdate(nextProps, nextState) {
return 布尔值
}
/**
* 只有props和state数据改变了,才会返回true
* 注意:只是进行了props和state数据的浅比较,如果只是数据对象内部数据改变了,返回false
* 不要直接修改state数据,而是要产生新数据
* 比如:state = {name: "lxh", arr: [1,2,3]}
* 修改1:let obj = this.state
* obj.name = "LXH"
* this.setState(obj) // 这样会返回false
* 修改2:let {arr} = this.state
* arr.push(4)
* this.setState({arr}) // 返回false
* this.setState({arr: [...arr, 4]}) // 返回ture
* // 不直接修改,产生新数据
**/
memo
主要用于函数组件
import React, { memo } from "react";
const Child = () ={
return (<div>child</div>)
}
export default memo(Child)
useCallback、usememo
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
useRef
Hook API 索引
错误边境
错误边境:只捕获后代组件生命周期产生的错误。
当子组件出现错误的时候,触发getDerivedStateFromError调用,并携带错误信息
state = {hasError: ""}
static getDerivedStateFromError(error) {
// error是错误信息
return {hasError: error}
}
render() {
return (
<>
{this.hasError ? <>当前网络不稳定,请稍后再试</> : <Child />}
</>
)
}
注意:错误边境只适用于生产环境
componentDidCatch() {
// 渲染组件时出错,执行
// 主要用于统计页面的错误,发送请求给后台,反馈给服务器,通知编码人员进行bug的解决
}
使用方式:getDerivedStateFromError 配合 componentDidCatch
react+typescript写法
安装@types/下,如:npm i @types/react-router-dom
import React, { Component, createRef } from "react";
interface propsType {
id?: string;
[propsName: string]: any;
}
interface stateType {
name: string;
}
// 第一个props属性约定类型,第二个state状态约定类型,不写占位可以使用any<any,any>
class Tags extends Component<propsType, stateType> {
state = {
name: "liaoxianhui",
};
refName = createRef<HTMLInputElement>();
render() {
return (
<div>
<input type="text" ref={this.refName} />
<button
onClick={() => {
// as 断言 , ? 或者 ! 都可以三种方式
console.log((this.refName.current as HTMLInputElement).value);
}}
>
获取输入框的值
</button>
</div>
);
}
}
export default Tags;
import React, { FC, useRef, useState } from "react";
// 方法1:约定props里的属性类型
interface propsTypeOne {
id?: string;
}
// 方法2
interface propsTypeTwo {
id?: string;
name?: () => void;
[propName: string]: any;
}
// const Form = (props: propsTypeOne) => { // 方法1
const Form: FC<propsTypeTwo> = (props) => {
// 方法2
const [name, setName] = useState<string>("lxh");
const refName = useRef<HTMLInputElement>(null);
return (
<div>
<input type="text" ref={refName} />
<button
onClick={() => {
// as 断言 , ? 或者 ! 都可以三种方式
console.log(refName.current?.value);
}}
>
获取输入框的值
</button>
</div>
);
};
export default Form;
消息订阅与发布机制
插件PubSubJs
安装:npm install pubsub-js
yarn add pubsub-js
yarn add -D @types/pubsub-js // ts版本
引入:import PubSub from 'pubsub-js';
发布:PubSub.publish("xxx","data")
订阅:const yyy = PubSub.subscribe("xxx",(msg, data)=>{ // msg消息名、date数据
console.log(date)
})
取消订阅:PubSub.unsubscribe(yyy); // 使用 yyy 取消单个订阅
PubSub.unsubscribe('yyy'); // 取消所有对 'yyy' 的订阅
跨域
// http-proxy-middleware react下载好了
const { createProxyMiddleware } = require('http-proxy-middleware') // react18中使用
// const createProxyMiddleware = require('http-proxy-middleware') // react17中使用
module.exports = function (app) {
app.use(
createProxyMiddleware('/api1', { // 遇到/api1的请求,触发该代理
target: 'http://loaclhost:5000', // 后端请求的基础路径
changeOrigin: true, // 控制服务器收到的请求头中host字段的值
pathRewrite: { // 去除请求前缀
'^/api1': ''
}
})
)
app.use(
createProxyMiddleware('/api2', {
target: 'http://localhost:6000',
changeOrigin: true,
pathRewrite: {
'/api2': ''
}
})
)
}
serve
测试打包好的项目在静态服务器,打包好后会提示serve -s build
- 安装:npm i -g serve
- 使用:serve -s build
- 使用指定端口号:serve -s build -l 4000