设计vue业务组件

一、设计优质vue业务组件

(一)基本原则:

1.组件需要复用到很多的地方,设计组件的核心就是可扩展性和贴合业务平衡。业务组件更偏向业务。基础组件更偏向扩展

2.尽量提供简便,满足大多数场景下0配置或极少配置可用,特殊场景也可以扩展自定义
业务组件样式,来自:https://www.bilibili.com/video/BV1Vj421f7fo/?spm_id_from=333.1387.homepage.video_card.click&vd_source=e32b78c665d106e3e5677ed9f00f47a9

(二)分解ui结构

ui结构示意图(来自https://www.bilibili.com/video/BV1Vj421f7fo/?spm_id_from=333.1387.homepage.video_card.click&vd_source=e32b78c665d106e3e5677ed9f00f47a9)

A. 图片区域分析:

问题分析:

1.图片控制大小

2.统一兜底图片

3.图片地址

专门思考:

1.以后是否有图片上需要加上icon

2.图片的兜底图是否会有替换的可能

图片区方案:
1.图片大小默认按设计图,有特殊需要用css,组件css层级设计的短一点(意思是不要套太多的层级,以免未来调整图片大小的

时候不方便。比如可以是item-wraper myitem/img-container,不要套四五级那么多)–方便调节

2.统一兜底图,留出兜底图的自定义空间

3.默认显示一张图片,如果有特殊需要,支持插槽替换

4.地址不单独传入,直接传入整体的商品信息,组件内部提取地址,也预留props可以强制定义url地址

示例代码:

<script setup>
import { ref } from "vue";
//示例信息
let {shopItem={},errorImg,forceImageUrl}=defineProps(['shopItem','errorImg','forceImageUrl'])
let imgurl = ref(forceImageUrl || shopItem.imgurl || 'http://localhost:5173/logo.svg');
</script>
<template>  
<div class="item-wraper">
    <div class="img-container">
        <slot name="imgslot">
            <img src="imgurl" @error="()=>{
                if (errorImg) {
                    imgurl=errorImg;
                    return;
                }
                imgurl='http://localhost:5173/logo.svg'
            }">
        </slot>
    </div>
    <div class="info-container">
        
    </div>
</div>

</template>
<style>
/* 样式随意设置一下 */
</style>

B. 标题区分析

问题分析:

1.标题字体大小,字体样式是否固定

2.标题有时候两行省略,有时候一行省略该如何控制

3.标题的内容如何处理

标题区域方案:

1.按额外内容区有几条决定标题几行,等于四条就一行,小于等于三条显示两行,预留props可以强制指定几行

2.字体大小,样式固定,交由css处理

3.内容默认按组件传入的所有商品信息提取标题,支持强制指定

示例代码:

<script setup>
import { ref,computed } from "vue";
//示例信息
let {shopItem={},errorImg,forceImageUrl,forceTitle}=defineProps(['shopItem','errorImg','forceImageUrl','forceTitle'])
//标题区域显示一行还是两行需要根据额外信息区域来确定,因此此处先写成默认的。
let istwo = computed(()=>{
    return false;
})
let imgurl = ref(forceImageUrl || shopItem.imgurl || 'http://localhost:5173/logo.svg');
</script>
<template>  
<div class="item-wraper">
    <div class="img-container">
        <slot name="imgslot">
            <img src="imgurl" @error="()=>{
                if (errorImg) {
                    imgurl=errorImg;
                    return;
                }
                imgurl='http://localhost:5173/logo.svg'
            }">
        </slot>
    </div>
    <div class="info-container">
            <h4 :class="{'info-title':true,'two-line':istwo}">
                {{ forceTitle || shopItem.title || 默认标题}}
            </h4>
    </div>
</div>

</template>
<style>
/* 样式随意设置一下 */
</style>

C. 额外信息区

问题分析:

1.行数不固定

方案1:整个额外信息区都用插槽插入

(可扩展性、灵活性最高,但是使用者需要自己写整个信息区域的html和css,工作量较大)

方案2:分成4个插槽,按需插入

(扩展性不错,使用者不需要写区域的布局,减少工作量)

示例代码:

<!-- 这是组件文件 -->
<script setup>
    import { ref, computed, useSlots } from "vue";
    //示例信息
    let { shopItem = {}, errorImg, forceImageUrl, forceTitle } = defineProps(['shopItem', 'errorImg', 'forceImageUrl', 'forceTitle'])
    //使用了几个插槽
    let slotObj = useSlots();
    //标题区域显示一行还是两行需要根据额外信息区域来确定,因此此处先写成默认的。
    let istwo = computed(() => {
        let slotArr = Object.key(slotObj)
        let slotrow = slotArr.filter((item) => {
            if (item.indexOf('row') !== -1) {
                return item;
            }
        })
        if (slotrow.length === 4) {
            return false;
        } else {
            return true;
        }
    })
    let imgurl = ref(forceImageUrl || shopItem.imgurl || 'http://localhost:5173/logo.svg');
</script>
<template>
    <div class="item-wraper">
        <div class="img-container">
            <slot name="imgslot">
                <img src="imgurl" @error="() => {
                    if (errorImg) {
                        imgurl = errorImg;
                        return;
                    }
                    imgurl = 'http://localhost:5173/logo.svg'
                }">
            </slot>
        </div>
        <div class="info-container">
            <h4 :class="{ 'info-title': true, 'two-line': istwo }">
                {{ forceTitle || shopItem.title || 默认标题 }}
            </h4>
            <div class="info-extra">
                <div class="info-row">
                    <slot name="row1"></slot>
                </div>
                <div class="info-row">
                    <slot name="row2"></slot>
                </div>
                <div class="info-row">
                    <slot name="row3"></slot>
                </div>
                <div class="info-row row-bottom">
                    <slot name="row4"></slot>
                </div>
            </div>
        </div>
    </div>

</template>
<style>

    /* 样式随意设置一下 */
    .info-container {
        width: 0;
        padding-left: 10px;
        background-color: pink;
        display: flex;
        flex-direction: column;
        flex-grow: 1;

        .info-extra {
            display: flex;
            flex-direction: column;
            flex-grow: 1;
        }

        .info-row {
            margin-bottom: 10px;
        }

        .row-bottom {
            margin-top: auto;
            /* 在底部显示 */
        }
    }
</style>
<!-- 这是页面文件 -->
<template>
    <div class="wraper">
        <baseItem :shop-item="{
            imgurl: 'http://localhost:5173/echart1s.png'
        }">
            <template #row1>
                123
            </template>
            <template #row2>
                123
            </template>
            <template #row3>
                123
            </template>
            <template #row4>
                123
            </template>
        </baseItem>
    </div>
</template>
<script setup>
    import { ref } from "vue";
    import baseItem from "./components"
</script>
<style>

</style>

D. 按钮区域

按钮区域基本无规律,直接插槽传入

添加的代码:

<div class="button-container">
            <slot name="button">
                
            </slot>
</div>

(三)行为角度

从行为角度来看,根据不同的点击,会产生不同的效果。而且商品都会有公共的

异常状态的点击效果。通过给代码添加点击事件@click="()=>{}"可以实现行为角

度的需求。

添加行为角度后的代码:

下面分别是页面文件和组件文件的代码。

在页面文件中使用该组件,并使用ClickShop监听,在这里可以对基本行为做出拓展。

在组件文件中,增加点击事件的方法,该方法中有点击跳转到目标链接、异常情况的处理,回调等功能。

<!-- 这是页面文件 -->
<template>
    <div class="wraper">
        <baseItem @ClickShop="()=>{
            //父组件可以在此监听做拓展行为
        }" :shop-item="{
            imgurl: 'http://localhost:5173/echart1s.png'
        }">
            <template #row1>
                123
            </template>
            <template #row2>
                123
            </template>
            <template #row3>
                123
            </template>
            <template #row4>
                123
            </template>
        </baseItem>
    </div>
</template>
<script setup>
    import { ref } from "vue";
    import baseItem from "./components"
</script>
<style>

</style>
<!-- 这是组件文件 -->
<template>
    <div class="item-wraper">
        <div class="img-container">
            <slot name="imgslot">
                <img src="imgurl" @error="() => {
                    if (errorImg) {
                        imgurl = errorImg;
                        return;
                    }
                    imgurl = 'http://localhost:5173/logo.svg'
                }">
            </slot>
        </div>
        <div class="info-container" @click="defaultClick">
            <h4 :class="{ 'info-title': true, 'two-line': istwo }">
                {{ forceTitle || shopItem.title || 默认标题 }}
            </h4>
            <div class="info-extra">
                <div class="info-row">
                    <slot name="row1"></slot>
                </div>
                <div class="info-row">
                    <slot name="row2"></slot>
                </div>
                <div class="info-row">
                    <slot name="row3"></slot>
                </div>
                <div class="info-row row-bottom">
                    <slot name="row4"></slot>
                </div>
            </div>
        </div>
        <div class="button-container">
            <slot name="button">

            </slot>
        </div>
    </div>

</template>
<script setup>
    import { ref, computed, useSlots } from "vue";
    //示例信息
    let { shopItem = {},
        errorImg,
        forceImageUrl,
        forceTitle,
        targeturl,
        noerroStatue
    } = defineProps(['shopItem',
        'errorImg',
        'forceImageUrl',
        'forceTitle',
        'targeturl',
        'noerroStatue'
    ])
    //定义回调
    let emit = defineEmits('ClickShop','ClickError'); 
    //使用了几个插槽
    let slotObj = useSlots();
    //标题区域显示一行还是两行需要根据额外信息区域来确定,因此此处先写成默认的。
    let istwo = computed(() => {
        let slotArr = Object.key(slotObj)
        let slotrow = slotArr.filter((item) => {
            if (item.indexOf('row') !== -1) {
                return item;
            }
        })
        if (slotrow.length === 4) {
            return false;
        } else {
            return true;
        }
    })
    let imgurl = ref(forceImageUrl || shopItem.imgurl || 'http://localhost:5173/logo.svg');
    function defaultClick() {
        if (targeturl) {
            window.location.href = targeturl;
        }
        if (!noerroStatue) {
            if (shopItem.errorArr.length!==0) {
                let str = '';//拼接错误信息
                shopItem.errorArr.forEach((mes) => {
                    str+=mes;
                });
                alert(str);//弹出错误信息
            }
            emit("ClickError")
        }
        emit("ClickShop")
    }
</script>
<style>

    /* 样式随意设置一下 */
    .info-container {
        width: 0;
        padding-left: 10px;
        background-color: pink;
        display: flex;
        flex-direction: column;
        flex-grow: 1;

        .info-extra {
            display: flex;
            flex-direction: column;
            flex-grow: 1;
        }

        .info-row {
            margin-bottom: 10px;
        }

        .row-bottom {
            margin-top: auto;
            /* 在底部显示 */
        }
    }
</style>

(四)总结

分析ui+分析行为

注意:

1.尽量少的props和插槽

2.写的时候,思考是否可以更加贴近业务做的更加简便

3.留拓展接口

4.行为记得开关和回调

开关:可以让使用者决定不去做该事情,点击行为后面加上默认行为。

回调:触发父组件的某个监听,可以做对于基本行为的拓展

本文是我在学习b站这个博主https://space.bilibili.com/2114295304的组件设计课程的笔记和一些感悟吧(图片来源于该up的视频)总结下来便于复习也帮助大家学习!如有不正之处请指正,我会及时修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值