网络请求-axios
跨域的解决方案
- 配套依赖:axios/qs/react-cookie/[http-proxy-middleware!]
// 安装依赖
npm i -s http-proxy-middleware/axios
// 在src目录下新建setupProxy.js文件以便对跨域做配置 ↓配置
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/chair", {
target: "https://xy.cozzia.com.cn",
changeOrigin: true,
pathRewrite: {
"^/chair": "/",
},
secure: true, //如果访问的是https类的链接,就需要设置为true
})
);
};
// 在需要网络请求的文件下 发起请求。
// 由于后端对请求格式有需要,这里的qs对post的数据做处理(单定义content-type没用哦)
axios.post(`/rechargeBill/refundBill`, qs.stringify(data), {
headers: {
"Content-type": "application/x-www-form-urlencoded;charset=utf-8",
},})
redux
初级使用
// 导入redux
const redux = require("redux");
// 初始化State
const initialState = {
num: 0,
};
// 创建reducer函数
function reducer(state = initialState, action) {
switch (action.type) {
case "add":
return { ...state, num: state.num + 1 };
case "sub":
return { ...state, num: state.num - 12 };
default:
return state;
}
}
// 初始化store-创建需要传入reducer
const store = redux.createStore(reducer);
// 订阅改变
store.subscribe(() => {
console.log("num", store.getState().num);
});
// 定义action
const action1 = { type: "add" };
const action2 = { type: "sub" };
// 派发action
store.dispatch(action1);
store.dispatch(action2);
入门使用
目录utils-connect.js
import React, { PureComponent } from "react";
// context函数
import { StoreContext } from "./context";
// 高阶函数
function EnhanceConnect(mapStateToProps, mapDispatchToProps) {
// connect(mapStateToProps, mapDispatchToProps)(Home);
// ↓接收高阶组建↑
return function High(WrapperComponent) {
class EnhanceComponent extends PureComponent {
constructor(props,context) {
super(props,context);
this.state = {
// 初始化State,具体要state的什么数据 从mapStateToProps里定义
storeState: mapStateToProps(context.getState()),
};
}
componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.setState({
storeState: mapStateToProps(this.context.getState()),
});
});
}
render() {
return (
<div>
<WrapperComponent
// 把state里的数据通过props传下去给 高阶组件
{...this.props}
{...mapStateToProps(this.context.getState())}
{...mapDispatchToProps(this.context.dispatch)}
/>
</div>
);
}
}
// index.js里定义好的store 作用:把store传下去
EnhanceComponent.contextType = StoreContext;
return EnhanceComponent;
};
}
export default EnhanceConnect;
目录page-about.js
import React, { PureComponent } from "react";
// 导入action 同vuex用法差不多
import { addAction, subAction } from "../../store/actionCreater";
// ↑上面提到的组件
import connect from "../../utils/connect";
class Home extends PureComponent {
constructor(props){
super(props)
}
render() {
return (
// 傀儡dom 用this.props接收上方 {...mapStateToProps(this.context.getState())} 传来的值
<div>
<h1>Home</h1>
<span>当前计数:{this.props.num}</span>
<button onClick={this.props.clickAdd}>+1</button>
</div>
);
}
}
const mapstate = (store) => {
// 向store索要num
return { num: store.num };
};
const mapdispatch = (dispatch) => {
return {
clickAdd() {
dispatch(addAction(1));
},
};
};
const enhanceHome = connect(mapstate, mapdispatch)(Home);
export default enhanceHome;
移动端自适应
rem配置
- 安装postcss-pxtorem依赖
npm i postcss-pxtorem -s
- 开启webpack配置文件(若遇到git相关报错,先把更改的文件git push上去再yarn eject)
yarn eject
- 再webpack.config.js文件下引入postcss-pxtorem
const px2rem = require('postcss-pxtorem')
-----------------------------------------------
postcssOptions: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: "postcss",
config: false,
plugins: !useTailwind
? [
// 加上↓ 的意思就是1rem = 75px 这个是根据750px设计稿来的
px2rem({ rootValue: 75, unitPrecision: 5, propList: ["*"] }),
"postcss-flexbugs-fixes",
[
"postcss-preset-env",
{
autoprefixer: {
flexbox: "no-2009",
},
stage: 3,
},
],
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
"postcss-normalize",
]
// 省略...
- 再index.js文件引入lib-flexible
import 'lib-flexible'
- !重要 兼容ios初始滚动条问题 index.html
<script>
/(iPhone|iPad|iPhone OS|Phone|iPod|iOS)/i.test(navigator.userAgent) && (head = document.getElementsByTagName('head'), viewport = document.createElement('meta'), viewport.name = 'viewport', viewport.content = 'target-densitydpi=device-dpi, width=480px, user-scalable=no', head.length > 0 && head[head.length - 1].appendChild(viewport));
</script>
- over写的px自动转成rem
移动端快速点击
- 场景:抽奖转盘,快速连点会触发不可预料的后果,用比对时间戳的方法防止快速双击!
if (this.state.BeginTimestamp + 2000 > new Date().getTime()) {
return false;
}
this.state.BeginTimestamp = new Date().getTime();
打包部署问题
静态文件
如果 图片超过webpack配置文件中的限制大小,则不转成base64,直接打进/media文件夹,不被打包。若需要图片被打包,则将webpack限制大小调大
- 文件:webpack.config.js ;适当调大
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '1000000'
);
homepage指向改为相对路径
- 场景:一个nginx下代理多个文件夹下的应用,相对路径就会走丢变为:/static。显然这不可能访问到各个文件夹下依赖的静态文件
- 文件:package.json下新增
"homepage": ".",
Umi的使用(antdPro)
安装
官方:yarn create @umijs/umi-app
// 但是官方的用法实战报错(yarn的锅),遂用↓
npm create @umijs/umi-app
// 加载依赖
yarn
前端国际化(useIntl方法)
- 粗看:↓引入umi带的useIntl方法;key:'pages.welcome.alertMessage',value:对应组件放在语言配置文件中
import { useIntl } from 'umi’;
useIntl().formatMessage({
id: 'pages.welcome.alertMessage',
// 找不到国际化键值对时展示的默认值。
defaultMessage: '更快更强的重型组件,已经发布。',
})
- 语言配置文件 键值对关系
export default {
'pages.welcome.alertMessage': '更快更强的重型组件,已经发布。',
};
Dva嵌入Umi的使用
model用例
- model.ts结构
pages
└─users
index.tsx *
model.ts *
import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
export interface UserModelState {
name: string;
}
export interface UserModelType {
namespace: 'users';
state: UserModelState;
effects: {
query: Effect;
};
reducers: {
save: Reducer<UserModelState>;
// 启用 immer 之后
// save: ImmerReducer<UserModelState>;
};
subscriptions: { setup: Subscription };
}
const UserModel: UserModelType = {
namespace: 'users',
state: {
name: '',
},
effects: { // 异步
*query({ payload }, { call, put }) {},
},
reducers: { // 同步
save(state, action) { // 简化save数据输出
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
return data
},
},
// 订阅
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/users') { // 触发前提条件
dispatch({
type: 'save', // 触发effect
});
}
});
},
},
};
export default UserModel;
页面mapStateToProps用例
- index.tsx
import { Table, Tag, Space } from 'antd';
import { connect } from 'umi';
// 记得吧users传入
const index = ({ users }) => {
// 此处省略columns配置 antd自己扒
return (
<div className='list-table'>
<Table columns={columns} dataSource={users} />
</div>
);
};
// 收mapState
const mapStateToProps = ({ users }) => {
return { users };
};
export default connect(mapStateToProps)(index);
Utils
canvas画布吸颜色
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
- sx:图像本身需要裁剪的X轴起点
- sy:图像本身需要裁剪的Y轴起点(有了XY就有了一个起点)
- sw:图像需要裁剪的宽度
- sy:图像需要裁剪的高度 将图像抽象到canvas画布上(图中蓝底图中)
- dx:抽象后的摆放x轴起点
- dy:抽象后的摆放y轴起点(dx,dy讲白了就摆放位置)
- dw,dh:绘制的图像宽高
- 引用美图↓
getImgColor() {
let img = new Image();
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
img.src =
"https://wxchart.cozzia.com.cn/chair/image/ioReadImage?imgPath=/ogawa/file/upload/reward_shop/202011/1605513392414.png";
img.crossOrigin = "anonymous";
img.onload = function () {
ctx.drawImage(img, 34,175,104,48,0,0,104,48);
img.style.display = "none";
let imgData = ctx.getImageData(0, 0, img.width, img.height).data;
var red = imgData[0];
var green = imgData[1];
var blue = imgData[2];
var alpha = imgData[3]
console.log(imgData)
console.log( 'background-color: rgba(' + red + ', ' + green + ', ' + blue + ', ' + alpha + ');');
};
}
Refs使用
class/函数组件混合取子组件ref
//父class
export default class index extends PureComponent {
constructor(props) {
super(props);
// 注册refs
this.scrollWinneref = React.createRef();
}
render(){
return <ScrollWinner ref={this.scrollWinneref} />
}
}
//子函数组件
const ScrollWinner = forwardRef((props, ref) => {
// *挂载到ref上
ref.current = { init };
})
Learn UMI
layout踩坑
layout开启
/.umirc.ts
layout: {
name: 'XX资本', // 网站名字
layout: 'side', // layout 的菜单模式, side: 左侧导航 top: 顶部导航 mix: 混合式导航
navTheme: 'light', // 左侧导航的主题为 浅色 主题
logo: 'http://v-project.coderv.cn/youpin/logo.png',
},
右上角头像/名称/logout...
- 注意:getInitialState是umi中的初始化数据方法,组件总消费+使用方法如下
import { history, useModel } from 'umi';
export default () => {
const { initialState, loading, refresh, setInitialState } = useModel('@@initialState');
//
setInitialState({avatar})
// 读取初始化数据
return <div>{initialState.userName}</div>;
};
/src/app.tsx
export async function getInitialState(name,avatar) {
return {
// name:'vas', // 默认 layout 导航右上角展示的用户名字段
avatar, // 默认 layout 导航右上角展示的用户头像字段
};
}