从0到1构造组件库的List组件 | 青训营笔记

303 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第18天

参考资料

列表 List - Ant Design Vue (antdv.com)

基于vue写的, (但是我没有看懂源码(用tsx写的),后面加上不记得插槽,所以卡了很久才大概弄懂)

Descriptions 描述列表 | Element Plus (gitee.io)

Table 表格 | Element Plus (gitee.io)

ps:要实现ant网页上的东西不是单独写一个组件就OK了,它是结合了很多它其他的组件(布局组件,card组件等等)一起实现的。

逻辑:

每个组件都应该有自己的文件夹,包含组件的模板、逻辑和样式文件。

参数传递可以使用Vue的props机制。在组件的props中声明需要接收的参数,然后在组件的模板中使用这些参数。如果需要验证参数的类型或默认值,可以在props中进行声明。

list结构

这里要记住的一点就是前端在写东西都是按照先搭好html架构,再去写其他的css和js,所以一定要想好结构,这个决定了后面能怎么写。

源码结构:list->list-item->list-item-meta(元) 三层嵌套。

后期再看这个结构就发现了它的灵活性。

善用调试工具来看样式和结构,这是最简单直接的方式

image.png

ant-vue的源码放了五个插槽,是具名作用域插槽,很重要(插槽的具体使用就是我在这个项目中的收获)

list:

slots: ['extra', 'loadMore', 'renderItem', 'header', 'footer']

list-item:

slot:['actions', 'extra','content']

image.png

list-item-meta

slot:['avatar', 'description', 'title']

在这里我把'description'和 'title'放在了一个content里面

image.png

问题

一开始就理解错了,不是让你封装一个完整的列表 是对ul li重构 但还是那样的使用模式,自己把东西搞复杂了,真的是搞复杂了,本身没有那么复杂

换成list+list-item+list-item-meta

如果不拆分复用性更差,就无法实现组件库的各种效果,所以一定要多拆分吗,这样才灵活。

为什么要这么去写,因为你想想原生的ul,是不是ul和li,所以应该是两个在一起去是新年,而不是我直接去循环展示出来,要明白逻辑是什么

最重要的

不要担心,要自己创造,因为这个列表完全没有指定是什么样,你完全可以自己想象的去写,不要被限制。

还是那句话,如果觉得很难,一定是没找对录,方法错了,换一条路走。

image.png

插槽可以先不写,再后面需要改的时候再往里面放插槽

实现具体步骤

思路不清晰的时候就看这篇文章:

模仿element-ui封装vue组件库(一)_耳鼻喉科魏主任的博客-CSDN博客

里面应该加一个list-item,这个可以是一个vue,直接导入在list组件中,要想一下这个组件放什么,应该具体怎么做,有这个组件主要是灵活性大大提升。

样式的实现是如何通过传入参数来进行改变的,:class=传入参数,根据不同的class来实现不同的样式,就这么简单,例如想要一个large的list,就传:class="large",那么就绑定对应的.list-large样式,在这里面设置font-size="30px",那么久变大了,就这么简单。其他的也是一样,根据传入的样式来决定(设置不同类型的样式,这一步可以抄一下ant-design-vue的样式设置)

样式(scss如何看懂,如何理解)

别忘了在调节样式前进行css初始化: 前端实用:CSS样式初始化 - 简书 (jianshu.com)

scss也是我在本次项目中的收获

scss这样的模式对我现在来说确实太难了,那么我们现在最需要做的就是实现样式

走了弯路,不是一定要像它这样写才可以实现,实现的方法有很多,只要我最后样式实现了就可以

// @import没理解 耽误好久 // 被导入的文件将合并编译到同一个 css文件中 // 被导入的文件中所包含的变量或者混合指令 (mixin) 都可以在导入的文件中使用。

现在最重要的就是样式了,scss和一系列的规范要看懂,可以开始写了。

(这是参考的一个视频好像是,但是忘记在哪看的了)

可以看element plu的样式是如何写的 image.png

image.png

image.png 用变量来代替自己来写,注释里的内容就是这些变量翻译成css的内容

$是变量 @include是使用方法,@mixin是创建方法

image.png

image.png

Ant Design Vue源码

真的难看懂,tsx没了解过

.tsx是TS使用了jsx语法

渲染函数 & JSX | Vue.js (vuejs.org)

Babel 是一个 JavaScript 编译器。 这些都前端工程化的基础。

组件引入

import { KlList, KlListItem } from 'kunlun-design'

组件在这里被导入 注意引入的是kunlun-design这个打包好的包(单引号),并不是package里的components

所以我们要进行打包后才可以使用,要在全局使用打包命令(pnpm build),之后才可以使用

自己手动添加在kunlun-design是进行打包配置,但是必须要打包出来才可以用

组件源码

list源码:

<template>
    <div :class="[n(), size && n(`--${size}`), bordered && n(`--bordered`)]" :style="{ ...style }">
        <!-- 第一层 kl-list 绑定的是kl-list kl-list--size kl-list--bordered -->

        <!-- 第二层 1.header kl-list--header kl-list-item--bordered -->
        <div :class="[header && 'kl-list--header', header && bordered && 'kl-list-item--bordered']">
            <slot name="header"></slot>
        </div>
        <!-- 第二层 2. ui kl-list-items  -->
        <ul class="kl-list-items">
            <template v-for="item in props.dataSource" :key="item">
                <!-- 第三层 1. li kl-list-item--bordered  -->
                <li :class="bordered && 'kl-list-item--bordered'">
                    <slot name="renderItem" :item="item"></slot>
                </li>
            </template>
        </ul>
        <!-- 第二层 3.footer  -->
        <div
            :class="[footer && 'kl-list--footer ', footer && bordered && 'kl-list-item--bordered']"
        >
            <slot name="footer"></slot>
        </div>

        <div :class="extra && 'kl-list--extra '">
            <slot name="extra"></slot>
        </div>
    </div>
</template>


<script setup lang="ts">
import { ListProps, sizeValidatorList } from './list'
import { createNamespace } from '@kunlun-design/utils'
import { computed } from 'vue'
import './list.scss'
const props = defineProps(ListProps)

// 这里是props中带有样式设置的时候设置样式的,增加了自主性
const style = computed(() => {
    return props.color || props.textColor
        ? {
              '--kl-bg-color': props.color,
              '--kl-font-color': props.textColor,
              '--kl-border-color': props.color,
              '--kl-border': `var(--kl-border-width) var(--kl-border-style) var(--kl-border-color) var(--kl-border-style)`
          }
        : {}
})
//检测size是否符合规范
const size = computed(() => {
    //首先验证的函数不允许是undefined 只允许string类型 如果类型非法就转换到默认类型
    if (props.size === undefined) return 'normal'
    return sizeValidatorList(props.size) ? props.size : 'normal'
})

defineOptions({
    name: 'KlList'
})
const { n } = createNamespace('list')
</script>

<style scoped lang="scss"></style>

list-item源码:

<template>
    <div :class="[n()]">
        <slot name="content"></slot>
        <slot name="extra"></slot>

        <slot name="actions"></slot>
    </div>
</template>

<script setup lang="ts">
// import { ListProps } from './list';
import './list.scss'
import { createNamespace } from '@kunlun-design/utils'

// const props = defineProps(ListProps)

defineOptions({
    name: 'KlListItem'
})
const { n } = createNamespace('list-item')
</script>

<style scoped lang="scss"></style>

list-item-meta源码:

<template>
    <div :class="[n()]">
        <slot name="avatar"></slot>
        <div class="kl-list-item-meta-content">
            <div class="kl-list-item-meta-title">
                <slot name="title"></slot>
            </div>
            <div class="kl-list-item-meta-description">
                <slot name="description"></slot>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
// import { ListProps } from './list';
import './list.scss'
import { createNamespace } from '@kunlun-design/utils'

// const props = defineProps(ListProps)

defineOptions({
    name: 'KlListItemMeta'
})
const { n } = createNamespace('list-item-meta')
</script>

<style scoped lang="scss"></style>