vue3封装一个基于element-plus的对话框
在vue3中自定义组件双向绑定语法的改变,使得写法和vue2大为不同。我们以element-plus的dialog组件为例。基于它。封装一个自己的对话框,同时介绍两种实现思路:思路一:数据驱动型:我们封装一个test-dialog组件:<el-button @click="open">打开</el-button><test-dialog ref="testDom" v
在vue3中自定义组件双向绑定语法的改变,使得写法和vue2大为不同。我们以element-plus的dialog组件为例。基于它。封装一个自己的对话框,同时介绍两种实现思路:
思路一:数据驱动型:
我们封装一个test-dialog组件:
<el-button @click="open">打开</el-button>
<test-dialog ref="testDom" v-model:visible="flag" ></test-dialog>
import TestDialog from "@/components/Dialogs/TestDialog"
import { reactive, toRefs, ref, onMounted, watch } from "vue"
setup() {
const state = reactive({
flag: false
})
const open = () => {
state.flag = true
}
watch(() => state.flag, (val) => {
console.log("父组件监听flag:", val)
})
}
子组件:
<template>
<el-dialog
title="提示"
v-model="dialogVisble"
width="30%"
:before-close="close"
>
<span>这是一段信息</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="close">取 消</el-button>
<el-button
type="primary"
@click="close"
>确 定</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { ref, watch } from "vue"
export default {
name: "TestDialog",
components: {},
props: {
visible: {
type: Boolean,
default: false
}
},
setup(props, ctx) {
const dialogVisble = ref(false)
const close = () => {
ctx.emit("update:visible", false)
}
watch(() => dialogVisble.value, (val) => {
ctx.emit("update:visible", val)
})
watch(() => props.visible, (val) => {
dialogVisble.value = val
})
return {
dialogVisble,
open,
close
};
},
};
</script>
<style scoped>
</style>
第二种思路:
类似直接操作dom,我们先通过ref拿到test-dialog组件的引用,每次点击打开按钮时,直接通过子组件去操作他内部的变量让它显示和隐藏
父页面:
<el-button @click="openByParentMethod ">打开</el-button>
<test-dialog ref="testDom" v-model:visible="flag" ></test-dialog>
import TestDialog from "@/components/Dialogs/TestDialog"
setup() {
const testDom = ref(null)
const openByParentMethod = () => {
testDom.value.dialogVisble = true
}
const closeByParentMethod= () => {
testDom.value.dialogVisble = false
}
return {
testDom,
openByParentMethod,
closeByParentMethod,
}
}
子组件:
<template>
<el-dialog
title="提示"
v-model="dialogVisble"
width="30%"
:before-close="close"
>
<span>这是一段信息</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="close">取 消</el-button>
<el-button
type="primary"
@click="close"
>确 定</el-button>
</span>
</template>
</el-dialog>
</template>
setup(props, ctx) {
const dialogVisble = ref(false)
const open = () => {
dialogVisble.value = true
}
const close = () => {
dialogVisble.value = false
}
return {
dialogVisble,
open,
close
};
},
而对于vue2来说。实现这样的一个弹窗。则是通过如下方式:
<select-city v-model="cityShow"></select-city>父组件通过v-model绑定变量cityShow,
而子组件中通过定义props:value,然后通过v-model 操控value来实现,如下:
1子组件在props中申明vaule,注意value名称是约定的不能写成其他的名称
2 子组件中通过 v-model="value"绑定改value
3关闭弹窗的时候,直接通过emit一个input事件来派发当前value的值。注意,这里只能写成input事件
(1)
<van-action-sheet
v-model="value"
@close="close"
>
内容xxx
</van-action-sheet>
export default {
name: "selectCity",
components: {},
props: {
value: {
type: Boolean,
default: false
}
},
methods: {
close() {
console.log("close!!!!")
this.$emit("input", this.cityShow)
},
},
针对vue2我们通过一个inputNm组件来具体看一看
<view class="item-menu-name">
<view class="pt6 txtRt">
{{item1.name}}
</view>
<view class=" pt6 vip-price txtRt">
¥ {{item1.vipPrice}}会员价
</view>
<view class=" pt6 txtRt sale-price">
¥ {{item1.salePrice}}
</view>
<view class="num-box">
<InputNum v-model="item1.num" @increase="increseNum" @decresae="decreseNum"
</view>
</view>
<script>
methods:{
caculateTotal() {
let total=0;
this.tabbar.forEach(item => {
item.foods.forEach(k => {
console.log(" k.num", k.num)
total += k.num
})
})
this.total=total
console.log("计算toatl",this.total)
},
increseNum() {
this.caculateTotal()
},
decreseNum() {
this.caculateTotal()
},
inputNum() {
this.caculateTotal()
},
}
</script>
没错InputNum 通过v-model绑定了我们购物车里的商品的数量。我们单独抽出这个组件进行计算
接下来我们看inputMum的具体实现
<template>
<view class="inputnum-page">
<view class="inputnum-container">
<!-- <uni-transition :mode-class="fade" :show="num>0"> -->
<view class="lt-container" v-if="curNum>0">
<view class="oper-left-wrap">
<view class="btn-left" @click="decrease">
-
</view>
<view class="oper-input">
<input v-model="curNum" type="number" class="ipt"/>
</view>
</view>
</view>
<!-- </uni-transition> -->
<view class="rt-container">
<view class="btn-right" @click="increase">
+
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
value: {
type: Number,
default: 0
}
},
data() {
return {
maskClass: {
opacity: 0,
},
}
},
computed:{
curNum:{
get(){
return this.value
},
set(val){
this.$emit("input", val);
}
}
},
methods: {
decrease(){
this.curNum--;
if(this.curNum<=0){
this.curNum=0;
return
}
this.$emit("decrease",this.curNum)
},
increase(){
this.curNum++;
console.log("this.curNum",this.curNum)
this.$emit("increase",this.curNum)
}
},
mounted() {}
}
</script>
<style scoped lang='scss'>
.inputnum-page{
margin-top: 10rpx;
}
.inputnum-container {
width: 100%;
display: flex;
}
.lt-container{
width: 80%;
}
.rt-container{
flex:1
}
.oper-left-wrap {
width: 120rpx;
display: flex;
justify-content: flex-end;
}
.oper-input {
width: 70rpx;
}
.btn-left,
.btn-right {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
border-radius: 50%;
background: #f00;
color:#fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 32rpx;
}
.ipt{text-align: center;}
</style>
props里为什么是value?对。这就是vue多v-model处理时的约定俗成。默认value和input事件就是v-model的语法糖。当然如果你想换成其他值,则需要单独配置model选项。
特别注意:这里computed的使用。如果我们不是通过computed来引用props的值而是单独把value作为inputNum;里v-model的绑定值。这样页面会报错Avoid mutating a prop directly since the value will be overwritten whenever...因为vue默认遵守单向数据流的思想。不允许子组件直接去更改父组件的props的值。而通过computed我们可以巧妙的绕开这个限制
更多推荐
所有评论(0)