Promise封装一个弹窗
promise是什么呢?我对他的理解就是当一件事情的发生,你不确定结果时,peomise可以帮你处理。
那么当一个弹窗出现,你不知道用户会点击确认还是取消,这个时候就可以用promise来处理
我们先来预想一下这个弹窗已经封装好了我们应该怎么去用它,
const m = new myMadel()
m.show({title="我的弹窗",content="今晚打老虎"}).then(res=>{...点击确认做的事情}).catch(res=>.....点击取消做的事情)
这两段代码能够清晰的展现出,用promise封装后使用起来的方便.
然后 我们顺着这个思路一步步把这个弹窗写出来,首先这个构造函数中有一个show方法这个show方法,要做的事情就是让这个弹窗弹出来,并且他要接受一些参数来完成自定义弹窗,最重要的是它要返回一个promise对象,这样才能执行后面的then,catch。(这里粗略的补充一下,其实用好peomise记住一个口诀就可以了,resolve()调用执行then,reject()调用执行catch
,虽然很粗暴但是很实用。)
class Madal {
show(props) {
return new Promise((resolve, reject) => {
let dom = document.createElement("div");
const JSXdom = (<MyModal {...props} resolve={resolve} reject={reject}></MyModal>)
//下面的操作就是把这个弹窗添加到 body上面
ReactDOM.createRoot(dom).render(JSXdom);
document.body.appendChild(dom);
});
}
}
上面的myModal是我对antd中Modal的二次封装,我们把resolve 和reject这两个函数传下去,在点击取消和确定时执行一下就可以了
export default function MyModal(props) {
const [visible, setVisible] = useState(true);
//弹窗默认参数
const {
title = "删除",
content = "你确定要删除么",
okText = "确认",
canCelText = "取消",
reject,
resolve,
} = { ...props };
useEffect(() => {
setVisible(true);
}, []);
const handleOk = () => {
resolve("点击确认");
setVisible(false);
};
const canCel = () => {
reject("点击取消");
setVisible(false);
};
return (
<div className="dvv">
<Modal
title={title}
visible={visible}
cancelText={canCelText}
okText={okText}
onOk={handleOk}
onCancel={canCel}
>
{content}
</Modal>
</div>
)
}
最后我们来调用一下看看效果
let m = new Madel();
m.show({ title: "我的弹窗", content: "今晚打老虎" })
.then((res) => {
console.log(res, "========确认");
})
.catch((res) => {
console.log(res, "==========取消");
});
Effect完成一段动画
import React, { useRef, useState, useEffect } from "react";
import anime from "animejs";
export default function Index() {
const element = useRef(null);
const status = useRef(true);
const [animaGo, setAnimaGo] = useState(false);
const [animaTo, setAnimaTo] = useState(false);
useEffect(() => {
animaGo && goMove();
animaTo && ToMove();
}, [animaGo, animaTo]);
//向左运动
function goMove() {
anime({
targets: element.current,
translateX: 500,
backgroundColor: "#66FF00",
borderRadius: ["0%", "50%"],
complete: () => {
setAnimaGo(false);
setAnimaTo(true);
},
});
}
//向右边运动
function ToMove() {
anime({
targets: element.current,
translateX: 0,
backgroundColor: "#FFF",
borderRadius: ["50%", "0%"],
easing: "easeInOutQuad",
complete: () => {
setAnimaTo(false);
status.current = true;
},
});
}
function handleStatus() {
if (status.current) {
status.current = false;
setAnimaGo(true);
}
}
return (
<div>
<div className="box" onClick={handleStatus}>
<div ref={element} className="childBox"></div>
{animaGo && "第一段动画执行"}
{animaTo && "第二段动画执行"}
</div>
</div>
);
}
这一小段动画很简单,但是它却体现了我们如何更好的去使用Effect函数,这里就是用开关思维让effect去控制事件在合适的时机去执行
vue3+pinia一个小购物车
这个demo主要就是想感受一下pina,结果就是不要太方便,相比vuex4它最大的亮点我觉得是
去掉了mutations,它状态的改变,变得非常简单只能通过actions
这无疑让我们的状态管理变得非常单一和清晰。
@/store/Cart
import { defineStore } from "pinia";
interface List {
goods: string;
price: number;
num: number;
myNum: number;
id: string;
}
const useCart = defineStore("Cart", {
state: () => ({
pricelist: [
{
goods: "金瓶梅",
price: 10,
num: 10,
myNum: 0,
id: "jpm",
},
{
goods: "西厢记",
price: 20,
num: 10,
myNum: 0,
id: "xxj",
},
{
goods: "牡丹亭",
price: 30,
num: 10,
myNum: 0,
id: "mdt",
},
],
}),
getters: {
tatolMoney: (state: any) => {
return state.pricelist.reduce(
(sum: number, item: List) => sum + item.price * item.myNum,
0
);
},
},
actions: {
add(id: string) {
this.pricelist.map((el) => {
if (el.id == id) {
el.myNum++;
el.num--;
}
});
},
//结算
sub(id: string) {
this.pricelist.map((el) => {
if (el.id == id) {
el.myNum--;
el.num++;
}
});
},
},
});
export default useCart;
无论多复杂的页面,只需要,state,getters,actions这三个api就够了,而且,在getters,actions中都可以直接 通过this去访问到state。接下来看一下在组件中的使用
List组件
<template>
<el-row class="col" v-for="el in listData" :key="el.id">
<div class="box">
<span> <b>名称:</b>{{ el.goods }} </span>
<span> <b>价格:</b>{{ el.price }} </span>
<span> <b>剩余数量:</b>{{ el.num }} </span>
</div>
<div class="buy">
<el-button :disabled="el.num == 0" @click="store.add(el.id)">+</el-button>
{{ el.myNum }}
<el-button :disabled="el.myNum == 0" @click="store.sub(el.id)">-</el-button>
</div>
</el-row>
</template>
<script setup lang="ts">
import useCart from "@/store/Cart";
const store = useCart();
const listData = store.pricelist;
</script>
状态的引用再也不会用到令人费解的...map映射在计算属性中。仓库里的所有东西都可以通过store去访问到
,它的add,和sub方法也是一样的.
<template>
<div><el-button type="primary">总价</el-button>¥{{ tatolMoney }}</div>
</template>
<script lang="ts" setup>
import useCart from "@/store/Cart";
import { storeToRefs } from "pinia";
//import { computed } from "vue";
const store = useCart();
const { tatolMoney } = storeToRefs(store);
//const tatolMoney = computed(() => store.tatolMoney);
</script>
<style lang="sxss" spoted></style>
这里是它计算属性的使用,可以直接通过store在模板中访问到,如果想在模板中看起来简洁一点可以用计算属性
,也可以用pinia提供的storeToRefs
去解构,它还可以解构action中的方法state中的状态。是引用变得简洁