react 依赖
- react-native 移动端跨平台
- 16.13.1版本
- react: 核心代码
- react-dom: 渲染在不同平台需要的核心代码
- babel:jsx转换react代码工具
//crossorigin 在控制台显示远程的错误信息
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
ReactDom.render(<App/>,documnet.getElementById('root'))
</script>
//1有继承extends,子类必须调用super()
class Person {
constructor(name,age){
this.name=name;
this.age=age;
}
}
class Student extends Person{
constructor(name,age){
super(name,age)
}
}
jsx核心语法
书写规范:
- 只能有一个根元素div(或者用Fragment)片段,空标签占位
- 用小括号包裹
- 可以写双标签,或者单标签,必须/>结尾
{js表达式}
1.注释书写:
{/*我是一段注释*/}
2.在{}中不能显示,被忽略
test1:null
test2:undefined
test3:Boolean
friend:{
name:'kobe'
}
this.setState.test3.toString()可以展示在页面
this.setState.friend //报错,对象不能直接这样展示 ,不能作为jsx的子类
3.嵌入表达式
{/*三元表达式*/}
{isLogin?'欢迎':'登录'}
{/*函数调用*/}
4.jsx绑定属性className
<div className ='' style={{color:'red',fontSize:'30px'}}></div>
<label htmlFor=''></label>
//方式一,显示绑定
<button onClick={this.btnClick.bind(this)}></button>
//方式二
this.btnClick=this.btnClick.bind(this)
<button onClick={this.btnClick}></button>
<button onClick={(e)=>{this.btnClick(item,index)}}></button>//执行另外一个函数 --推荐
//只要一个参数(e),括号可以省略 e=>{}
btnClick(item,index){
}
//方式三 推荐
<button onClick={this.btnClick></button>
const btnClick=()=>{
}
5.传递参数
6.return () 用小括号包裹起来,标签可以换行
7.条件渲染
// 1.方案一:通过if判断: 逻辑代码非常多的情况
let welcome = null;
let btnText = null;
if (isLogin) {
welcome = <h2>欢迎回来~</h2>
btnText = "退出";
} else {
welcome = <h2>请先登录~</h2>
btnText = "登录";
}
{/* 2.方案二: 三元运算符 */}
<button onClick={e => this.loginClick()}>{isLogin ? "退出" :"登录"}</button>
{/* 3.方案三: 逻辑与&& */}
{/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了 */}
<h2>{ isLogin && "你好啊, coderwhy" }</h2>
{ isLogin && <h2>你好啊, coderwhy</h2> }
react脚手架:create-react-app 项目名称(dccweb-hd)不允许大写字母
npm | yarn |
---|---|
npm install | yarn install |
npm install | yarn add |
PWA 离线
serviceWork.js
yarn eject 暴露webpack配置信息 此操作不可逆
组件的生命周期
生命周期
类组件:可以维护数据状态,有this
函数没有生命周期,没有this,不能管理数据状态
类里面的叫方法,有this绑定
外部叫函数
挂载阶段 mounting
constroct
cwm→ componentWillMount = () => { } DEPRECATED!!! 过时的
cdm→ componentDidMount = () => { } *****
cwr→ componentWillReceiveProps = (nextProps) => { } DEPRECATED!!!
更新阶段 Updating
scu→ shouldComponentUpdate = (nextProps, nextState) => { }
cwup→ componentWillUpdate = (nextProps, nextState) => { } DEPRECATED!!!
cdup→ componentDidUpdate = (prevProps, prevState) => { } *****
卸载Unmount
cwun→ componentWillUnmount = () => { }
组件的数据传递
父子组件的通信
父组件通过属性=值 prodectList={prodectList}
子组件props参数获取
function Main(props) {
const {prodectList}=props
子组件传递父组件
父组件 itemClick={函数}
子组件 执行函数
// 父组件里调用子组件 是否允许ppt向下惯性运动
const flagRef = useRef(true);//是否允许ppt向下惯性运动,默认true,允许,点击一键置顶按钮时,不允许
const getIsInertiaMove = (val: boolean) => {
flagRef.current = val;
};
<Topping
parentCallback={getIsInertiaMove}
currScrollPos={currScrollPosY}
/>
//子组件接收
interface IToppingProps {
currScrollPos: any;
parentCallback: Function;
}
function Topping(props: IToppingProps) {
const { currScrollPos, parentCallback } = props;
// 触发一键置顶按钮,不允许向下惯性运动
parentCallback(false);
}
跨组件通信
1.props,一级一级传值
2.属性展开:jsx语法
const props={firstName='hobby',lastName='Hector'}
<ProfileHeader {...props}/>
<ProfileHeader firstName="Ben" lastName="Hector" />
3.Context:★
类组件
第一步:创建 export const WordContext = createContext({} as IMobSignContext);
<WordContext.provider value={}>
<Profile/> //不包含在这个里面,传递的就是默认值
<WordContext.provider/>
第二步:
ProfileHeader.contextType=WordContext;
this.context
函数组件:
consumer
后续用redux替代 ★
//插槽 slot
<NavBar>
<span></span>
<div></div>
</NavBar>
this.props.children 获取组件内部标签
跨组件事件传递 events
yarn add events
API
创建对象:eventBus
import { EventEmitter } from 'events';
const eventBus = new EventEmitter();
发出事件eventBus.emit("事件名称",参数列表)
监听事件eventBus.addListener("事件名称",监听函数)
移除事件:eventBus.removeListener("事件名称",监听函数)
setState
setState是异步更新的
提高性能:获取多个更新,然后批量更新
如果同步,不能保证render和props同步,不能保证state和props一致性
this.setState({
message:'你好啊'
},()=>{
//拿到异步的数据,类似vue里面的nextTick
console.log(this.state.message)
})
componentDidUpdate()方式二:获取异步数据
同步更新:
放在定时器里面执行,变成同步
数据合并:传对象
setState内部合并:
累计合并:传函数
setState((preState,props)=>{
return{
counter:preState.counter+1
}
})
react性能优化
函数组件:memo包裹
counter改变,包裹memo的组件不会重新渲染
类组件:extends PureCompontent
Ref
React.createRef()
current 获取元素
react中的CSS
CSS in js
yarn add styled-components
style.ts
import styled from 'styled-components';
export const BtnWrapper = styled.div<{ isActive: boolean; activeBgi: any }>`
border: ${(props) =>props.isActive ? '0.05rem solid #0181ff' : '0.05rem solid red'};
span{
&.active{
color:red //表示同时是span并且有active
}
}
`;
<BtnWrapper>
<BtnWrapper/>
react 添加class
/**
* 特点:
* 1.props穿透
* 2.attrs的使用
* 3.传入state作为props属性
*/
const HYInput = styled.input.attrs({
placeholder: "coderwhy",
bColor: "red"
})`
background-color: lightblue;
border-color: ${props => props.bColor};
color: ${props => props.color};
`
<HYInput type="password" color={this.state.color}/>
高级用法:
1.继承
styled(HYInput)`
`
2.设置主题
import styled {ThemeProvider} from 'styled-components';
<ThemeProvider theme={{themeColor:'red',fontSize:'20px'}}>
<ThemeProvider/>
color: ${(props) =>props.theme.themeColor}
动态添加classNames
三元表达式
第三方库:classNames★
classNames({foor:true})
className={classNames(['clear', { iconActive: isBtnActive }])}
<div
className={classnames({
[styles.sheet]: true,
[styles.active]: current.sheetId == v.sheetId,
})}
>
const active = useMemo(
() =>
classNames({
[styles.active]: activeCurrentPage===activeIndex,
}),
[activeCurrentPage],
);
- webpack下,import store from './store/index.js'
- index.js可以省略。,import store from './store'
- import * as actionType from './constants'; actionType.
import {
incAction,
addAction,
changeBannersAction,
changeRecommendAction
} from '../store/actionCreators'
redux
//★actionCreators
export const addAction = num => ({
type: ADD_NUMBER,
num
});
//★constants //export const ADD_NUMBER = "ADD_NUMBER";
//★index
//★reducer
// 拆分counterReducer
const initialCounterState = {
counter: 0
}
function counterReducer(state = initialCounterState, action) {
switch (action.type) {
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num };
case SUB_NUMBER:
return { ...state, counter: state.counter - action.num };
case INCREMENT:
return { ...state, counter: state.counter + 1 };
case DECREMENT:
return { ...state, counter: state.counter - 1 };
default:
return state;
}
}
export default counterReducer;
import { useSelector, shallowEqual } from 'react-redux';
import reducer, { defaultState } from './reducer';
import React, {
memo,
useRef,
useState,
useReducer,
useContext,
useCallback,
useMemo,
useEffect,
useLayoutEffect,
} from 'react';
store
const init={
friend:[
{name:'why'}
]
}
import reducer from './reducer.js';
const store = redux.createStore(reducer);
一般会有多个reducer,需要合并
action
派发(dispatch())action 更新
const action1={type:'ADD_FRIEND',info:{name:'lucy'}}
store.dispatch(action1)
---
---
reducer 纯函数,不能产生副作用
reducer将sotre和action 联系起来
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT
} from './constants.js';
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch (action.type) {
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num };
case SUB_NUMBER:
return { ...state, counter: state.counter - action.num };
case INCREMENT:
return { ...state, counter: state.counter + 1 };
case DECREMENT:
return { ...state, counter: state.counter - 1 };
default:
return state;
}
}
export default reducer;
中间件:redux-thunck
hooks
useState
useEffect
useContext
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
自定义hooks
useSelector
import { useSelector } from 'umi';
import { useSelector, shallowEqual } from 'react-redux';
★useState 返回一个数组
* 元素1: 当前state的值
* 元素2: 设置新的值时,使用的一个函
<React.StricMode>
<App/>
<React.StricMode/>
严格模式下,render会被渲染两次
注意:不要再函数,条件判断,for循环里面使用hooks
const [counter,setCounter]=useState(0)
setCounter(counter+1)
传入函数
const [count, setCount] = useState(() => 10);
setCount((prevCount) => prevCount + 10);
usestate 快捷键,按下tab键
★useEffect 生命周期
useEffect(() => {
console.log("订阅一些事件");
//生命周期 componentWillUnmount
return () => {
console.log("取消订阅事件")
}
}, []);
[]表示第一次渲染的时候执行一次;如果不写,只要组件重新渲染,都会执行一遍
★useContext 子孙组件传递数据
export const UserContext= createContext()
export const PcSignContext = createContext({} as IPcSignContext);
{/* <UserContext.Provider value={{name: "why", age: 18}}>
<TokenContext.Provider value="fdafdafafa">
<CustomContextShareHook/>
</TokenContext.Provider>
</UserContext.Provider> */}
const user = useContext(UserContext);
const theme = useContext(ThemeContext);
★useReducer 是useState的一种替代方案 不共享state
const [state, dispatch] = useReducer(reducer, {counter: 0});
dispatch({type: "increment"})}
export default function reducer(state, action) {
switch(action.type) {
case "increment":
return {...state, counter: state.counter + 1};
case "decrement":
return {...state, counter: state.counter - 1};
default:
return state;
}
}
★useCallback 性能优化 针对函数 meno
场景: 在将一个组件中的函数, 传递给子元素进行回调使用时, 使用useCallback对函数进行处理.
const increment2 = useCallback(() => {
console.log("执行increment2函数");
setCount(count + 1);
}, [count]);
//也可以用useMome
const increment3 = useMemo(() => {
return () => {
console.log("执行increment2函数");
setCount(count + 1);
}
}, [count]);
★useMemo 性能优化 针对返回值,返回记忆值
复杂计算的应用
const total = useMemo(() => {
return calcNumber(count);
}, [count]);
传入子组件 或者用useState
const info = useMemo(() => {
return { name: "why", age: 18 };
}, []);
<HYInfo info={info} />
★useRef 只能在class组件中使用,在函数式组件中使用需要用forwardRef包裹
const titleRef = useRef();
function changeDOM() {
titleRef.current.innerHTML = "Hello World";
inputRef.current.focus();
console.log(testRef.current);
console.log(testRef2.current);
}
<h2 ref={titleRef}>RefHookDemo01</h2>
*** const numRef = useRef(count);
useEffect(() => {
numRef.current = count;
}, [count])
//保存一个函数,在整个生命周期中保存不变
使用场景:记录上一次的值
★useImperativeHandle 和forwardRef关联 目的不过多暴露给父组件
forwardRef 可以将ref转发到子组件
const HYInput = forwardRef((props, ref) => {
const inputRef = useRef();
//()用小括号包裹起来,返回的是一个对象
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}), [inputRef])
return <input ref={inputRef} type="text"/>
})
export default function UseImperativeHandleHookDemo() {
const inputRef = useRef();
return (
<div>
<HYInput ref={inputRef}/>
<button onClick={e => inputRef.current.focus()}>聚焦</button>
</div>
)
}
★useLayoutEffect 布局作用,阻塞浏览器的更新 // 稍稍阻塞浏览器的重绘,尽可能删除存在滞留隐患的监听器
useEffect 不会阻塞dom更新,dom更新完毕之后执行
export default function LayoutEffectCounterDemo() {
const [count, setCount] = useState(10);
//页面不会闪0,如果用useEffect ,页面是先出现0,在出现随机数
useLayoutEffect(() => {
if (count === 0) {
setCount(Math.random() + 200)
}
}, [count]);
return (
<div>
<h2>数字: {count}</h2>
<button onClick={e => setCount(0)}>修改数字</button>
</div>
)
}
★自定义hoos 相同逻辑抽取到一个里面,
const Profile = (props) => {
useLoggingLife("Profile");
return <h2>Profile</h2>
}
//普通函数不能使用hook,只能在自定义hook中使用,use开头就代表自定义hook
function useLoggingLife(name) {
useEffect(() => {
console.log(`${name}组件被创建出来了`);
return () => {
console.log(`${name}组件被销毁掉了`);
}
}, []);
}
//Context共享
export function usePptContext() {
const context = useContext(PptContext);
return context;
}
//获取滚动位置
function useScrollPosition() {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollPosition(window.scrollY);
}
document.addEventListener("scroll", handleScroll);
return () => {
document.removeEventListener("scroll", handleScroll)
}
}, []);
return scrollPosition;
}
const position = useScrollPosition();
★useSelector
const { banners, recommends, counter } = useSelector(state => ({
banners: state.banners,
recommends: state.recommends,
counter: state.counter
}), shallowEqual);
项目
yarn add normalize.css
reset.css
@import "~normalize.css"; //从模块里导入
@import "~antd/dist/antd.css";
配置别名: yarn add @craco/craco
"scripts": {
"start": "craco start",
"build": "umi build",
},
导入文件处理:
最上面第三方的东西
中间导入功能类的东西,比如网络请求,actionCreators,utils
最后导入组件
分号尽量加上
项目里引入redux(24节,一小时)
yarn add redux react-redux redux-thunk 3个依赖
react-redux:作用把react组件和redux结合起来,其中最重要的conText 和Provider
store/index.js 创建store
★store
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
//调试工具生效 如果有就使用,否则使用compose
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
export default store;
一般会有多个reducer,需要合并
★reducer: store/ireducer.js
import { combineReducers } from 'redux-immutable';//用redux-immutable',在useSelector中取值,用getIn方式
//import { combineReducers } from 'redux';
import { reducer as recommendReducer } from '../pages/discover/c-pages/recommend/store';
import { reducer as playerReducer } from '../pages/player/store';
const cReducer = combineReducers({
recommend: recommendReducer,
player: playerReducer
});
export default cReducer;
App.js
import { Provider } from 'react-redux';
import store from './store';
//Provider共享出去,然后通过connect获取store里面的state,disPatch类似的东西
<Provider store={store}>
<HashRouter>
<HYAppHeader />
<Suspense fallback={<div>page loading</div>}>
{renderRoutes(routes)}
</Suspense>
<HYAppFooter />
<HYAppPlayerBar/>
</HashRouter>
</Provider>
★actionCreatios:
export const getLyricAction = (id) => {
return dispatch => {
getLyric(id).then(res => {
const lyric = res.lrc.lyric;
const lyricList = parseLyric(lyric);
dispatch(changLyricListAction(lyricList));
})
}
}
constants
index
reducer
redux hooks
useDispatch()
//拿到里面的值,第一个是一个回调函数
//shallowEqual性能优化,浅层比较
// redux
const {
htmlName,
currentPage,
pageCount,
applicationType,
} = useSelector(
(state: { app: Application; appstate: AppState }) => ({
htmlName: state.app.param.htmlName,
currentPage: state.appstate.currentPage,
pageCount: (state.app.doc as WpDocument).pageCount,
currentPagesSize: state.appstate.currentPagesSize,
applicationType: state.app.param.applicationType,
}),
shallowEqual,
);
★ImmutableJS //优化,提高性能,节省内存空间,只要修改会返回新的对象,旧的对象不发生变化
const im = Immutable;
// Map的使用
// const info = {
// name: "kobe",
// age: 30,
// friend: {
// name: "james",
// age: 25
// }
// }
// const infoIM = im.Map(info);//改成Immutable对象
// const obj = infoIM;
// const infoIM2 = infoIM.set("name", "why");
// console.log(obj.get("name"));
// console.log(infoIM2.get("name"));
// List的使用
// const names = ["abc", "cba", "nba"];
// // const arr = names;
// // names[0] = "why";
// // console.log(arr);
// const namesIM = im.List(names);//改成Immutable对象
// const arrIM = namesIM.set(0, "why");
// console.log(namesIM.get(0));//abc 旧的
// console.log(arrIM.get(0));//why 新的
const info = {
name: "kobe",
age: 30,
friend: {
name: "james",
age: 25
}
}
// const infoIM = im.Map(info);//改成Immutable对象 ,浅层转换
const infoIM = im.fromJS(info);//深层嵌套也会转换Immutable对象 ct
console.log(infoIM.get("friend"));
引入项目
//Map比fromJS性能更高
import { Map ,fromJS} from 'immutable';
import * as actionTypes from './constants';
const defaultState = Map({
topBanners: [],
upRanking: {},
});
function reducer(state = defaultState, action) {
switch (action.type) {
case actionTypes.CHANGE_TOP_BANNERS:
return state.set("topBanners", action.topBanners);//返回新的数据
case actionTypes.CHANGE_UP_RANKING:
return state.set("upRanking", action.upRanking);
default:
return state;
}
}
export default reducer;
取值:
// redux hook
const {
currentSong,
sequence,
lyricList,
currentLyricIndex
} = useSelector(state => ({
currentSong: state.getIn(["player", "currentSong"]),
sequence: state.getIn(["player", "sequence"]),
lyricList: state.getIn(["player", "lyricList"]),
currentLyricIndex: state.getIn(["player", "currentLyricIndex"])
}), shallowEqual);
const dispatch = useDispatch();
路由
yarn add react-router-dom
import { renderRoutes, matchRoutes } from 'react-router-config';//将所有的路由统一管理
import {
BrowserRouter,
Link,
Route,
NavLink,
Switch,
withRouter
} from 'react-router-dom';
组件里写法:
<NavLink exact to="/about" activeClassName="about-active">企业历史</NavLink>
<NavLink exact to="/about/culture" activeClassName="about-active">企业文化</NavLink>
<NavLink exact to="/about/contact" activeClassName="about-active">联系我们</NavLink>
<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
<NavLink to="/about" activeClassName="link-active">关于</NavLink>
<NavLink to="/profile" activeClassName="link-active">我的</NavLink>
<NavLink to="/abc" activeClassName="link-active">abc</NavLink>
<NavLink to="/user" activeClassName="link-active">用户</NavLink>
<NavLink to={`/detail/${id}`} activeClassName="link-active">详情</NavLink>
<NavLink to={`/detail2?name=why&age=18`} activeClassName="link-active">详情2</NavLink>
<NavLink to={{
pathname: "/detail3",
search: "name=abc",
state: info
}}
activeClassName="link-active">
详情3
</NavLink>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:id" component={User} />
<Route path="/user" component={User} />
<Route path="/login" component={Login} />
<Route path="/product" component={Product} />
<Route path="/detail/:id" component={Detail} />
<Route path="/detail2" component={Detail2} />
<Route path="/detail3" component={Detail3} />
<Route component={NoMatch} />
<Switch/>
{renderRoutes(this.props.route.routes)}// 子路由
父页面里面写法:
{renderRoutes(routes)}
export default withRouter(App);
<BrowserRouter>
<App/>
</BrowserRouter>
BrowserRouter使用history模式
HashRouter使用hash模式
动态路由/$(id)
this.props.location
const routes = [
{
path: "/",
exact: true,//精确匹配
component: Home
},
{
path: "/about",
component: About,
routes: [
{
path: "/about",
exact: true,
component: AboutHisotry
},
{
path: "/about/culture",
component: AboutCulture
},
]
},
{
path: "/profile",
component: Profile
},
{
path: "/user",
component: User
}
]
axios网络请求 封装
- 发送请求
- 创建实例
- 拦截器
- 请求封装
get,post,head,
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
interface IResponse {
data: any;
errorcode: number;
message: string;
}
const instance = axios.create({
timeout: 60000
});
instance.interceptors.request.use((config) => {
// 请求文档数据的拦截
// if (config.url?.includes('index.json')) {
// eventBus.emit('reqSent', '请求文档数据中...');
// }
// 发起签批提交请求的拦截
if (config.url?.includes('index.json')) {
// console.log(config,'config.url')
// eventBus.emit('reqSent', '服务器解析签批数据中...');
}
return config;
});
// instance.interceptors.response.use((res: AxiosResponse<IResponse>) => {
// const { status, statusText } = res;
// const { data, message, errorcode } = res.data;
// if (
// errorcode === 0 ||
// message === '操作成功' ||
// status.toString().startsWith('2') ||
// statusText === 'OK'
// ) {
// // 延迟测试
// // setTimeout(() => {
// // }, 1000);
// // eventBus.emit('reqSucceeded');
// } else {
// // eventBus.emit('reqFailed');
// }
// // console.log(res);
// return res;
// });
//请求失败,自动重新发送请求
instance.interceptors.response.use(null, function axiosRetryInterceptor (error) {
error.config.__retryCount = error.config.__retryCount || 0
error.config.__retryCount += 1
if (error.config.__retryCount >= 4) {
console.log(error.config.__retryCount,'error.config.__retryCount')
console.log(`url:${error.config.url}重发达到最大次数,停止重发`)
return Promise.reject(error)
} else {
location.reload();
// console.log(`url:${error.config.url}开始重发,次数:${error.config.__retryCount},状态:${error.status},data:${JSON.stringify(error.config)}`)
// return new Promise(function (resolve) {
// return setTimeout(function () {
// return resolve(axios(error.config))
// }, 1000)
// })
}
})
// get封装
export function get(url: string, params = {}) {
return new Promise((resolve, reject) => {
instance({
url: url,
params,
method: 'get',
})
.then((res) => {
if (res.status === 200) {
resolve(res.data);
} else {
reject(res);
}
})
.catch((err) => {
reject(err)
});
});
}
// post封装
export function post(
url: string,
params = {},
data = {},
// 进一步配置其他项
otherConfigs = {} as AxiosRequestConfig,
) {
return new Promise((resolve, reject) => {
instance({
url,
method: 'post',
params,
data,
...otherConfigs,
})
.then((res) => {
const { data } = res;
const { status, statusText } = res;
if (
data.errorcode === 0 ||
data.message === '操作成功' ||
status.toString().startsWith('2') ||
statusText === 'OK'
) {
resolve(data);
} else {
reject(data);
}
})
.catch((err) => {
reject(err);
});
});
}
// head封装
export function head(url: string, params = {}) {
return new Promise((resolve, reject) => {
instance({
url: url,
params,
method: 'head',
})
.then((res) => {
if (res.status === 200) {
resolve(res.data);
} else {
reject(res);
}
})
.catch((err) => {
reject(err);
});
});
}
export default {
get,
post,
head,
};
import service from './request'
let resp =await service.get(url)
标签:return,counter,React,state,import,组件,const
From: https://www.cnblogs.com/qiyuemh/p/18222047