组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList

Posted 星期一研究室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList相关的知识,希望对你有一定的参考价值。

封面

🖼️序言

最近在用 vue3ts 捣鼓一些小工具,发现平常开发中一个很常见的需求就是,数据列表的渲染。现在重新学习,发现我在学 vue2 时的很多设计规范和逻辑都考虑的不是特别妥当。

因此,写下这篇文章,记录组件设计中数据列表渲染和全局头部的设计。

一起来学习吧~🙆

📻一、ColumnList数据渲染

1、设计稿抢先知

在了解功能实现之前,我们先来看看原型图,看我们想要实现的数据列表是怎么样的。如下图所示:

columnList设计稿

大家可以先了解一下,我们待会所要实现内容的效果图。

2、数据构思

了解完具体的效果图之后呢,现在我们要开始来干活啦!

首先我们需要先构思这个组件所需要的数据有哪一些呢?

这个组件所需要的数据,首先是每一行数据它自己唯一的 id ,其次就是标题 title ,还有一个是头像 avatar ,最后一个是每个标题对应的文字描述 description

分析完成之后,我们现在在 vue3 项目下的 src|components 文件夹下新建一个文件,命名为 ColumnList.vue 。之后再编写这段业务代码。具体代码如下:

<template>
	<div></div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
//用ts写一个接口,存放列表数据的属性
export interface ColumnProps {
  id: number;
  title: string;
  avatar?: string;
  description: string;
}
export default defineComponent({
  name: 'ColumnList',
  props: {
    //将接口的内容赋值给list数组,方便接收父组件传来的数据
    list: {
      type: Array as PropType<ColumnProps[]>,
      required: true
    }
  }
})
</script>
<style lang="scss" scoped>
  
</style>

3、视图数据绑定

现在,对数据构思完毕之后,我们是还没有取到任何数据可以渲染的,相当于是一个空的 ColumnList 。但是我们已经有了接口的属性内容,所以我们先来把数据绑定到视图当中。具体代码如下:

<template>
  <div class="row">
    <div v-for="column in columnList" :key="column.id" class="col-4 mb-3">
      <div class="card h-100 shadow-sm">
        <div class="card-body text-center">
          <img :src="column.avatar" :alt="column.title" class="rounded-circle border border-light w-25 my-3">
          <h5 class="title">{{column.title}}</h5>
          <p class="card-text text-left">{{column.description}}</p>
          <a href="#" class="btn btn-outline-primary">进入专栏</a>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
export interface ColumnProps {
  id: number;
  title: string;
  avatar?: string;
  description: string;
}
export default defineComponent({
  name: 'ColumnList',
  props: {
    list: {
      type: Array as PropType<ColumnProps[]>,
      required: true
    }
  }
})
</script>
<style lang="scss" scoped>
  
</style>

注: 这里我用到的是 bootstrap 的样式库,所以 css 方面不做过多的编写,大家有需要可以到官方中文文档进行查看,也可以自己进行样式设计。

到此,我们就完成了第一轮的数据绑定。接下来我们在父组件中,进行数据传递。

4、数据传递

我们在vue3项目中的 src 文件夹下的 App.vue 中来进行数据传递。具体代码如下:

<template>
  <div class="container">
    <column-list :list="list"></column-list>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
//在根文件下引入bootstrap
import 'bootstrap/dist/css/bootstrap.min.css'
//引入子组件
import ColumnList, { ColumnProps } from './components/ColumnList.vue'

//制造子组件的接口数据
const testData: ColumnProps[] = [
  {
    id: 1,
    title: 'test1专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。'
    avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  },
  {
    id: 2,
    title: 'test2专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。',
    avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  }
]

export default defineComponent({
  name: 'App',
  components: {
    ColumnList
  },
  setup () {
    return {
      list: testData
    }
  }
})
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

现在,我们来看下此时浏览器的运行效果:

columnList静态组件

大家可以看到,通过以上的代码编写,数据正常的传递并运行成功了。

5、挠头情况

看到这里,感觉整个组件的设计还挺尽善尽美的。但是呢,大家有没有想过有一种特殊情况,假设后端传来的数据中,有一行数据里面,没有头像avatar的值。那这个时候,如果我们前期没有考虑清楚有可能遇到的各种情况,程序估计很容易地就报错了。

所以我们还要做的一件事情就是,当收不到头像的数据时,我们要给它加一张初始化的图片,以至于保持列表内容一致。

现在我们来对 ColumnList.vue 文件进行改造,具体代码如下:

<template>
  <div class="row">
    <div v-for="column in columnList" :key="column.id" class="col-4 mb-3">
      <div class="card h-100 shadow-sm">
        <div class="card-body text-center">
          <img :src="column.avatar" :alt="column.title" class="rounded-circle border border-light w-25 my-3">
          <h5 class="title">{{column.title}}</h5>
          <p class="card-text text-left">{{column.description}}</p>
          <a href="#" class="btn btn-outline-primary">进入专栏</a>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
//用ts写一个接口,存放列表数据的属性
export interface ColumnProps {
  id: number;
  title: string;
  avatar?: string;
  description: string;
}
export default defineComponent({
  name: 'ColumnList',
  props: {
    //将接口的内容赋值给list数组,方便接收父组件传来的数据
    list: {
      type: Array as PropType<ColumnProps[]>,
      required: true
    }
  },
  //将props传递给setup
  setup(props) {
    const columnList = computed(() => {
      //遍历list数组数据的每一行
      return props.list.map(column => {
        //当遇到当前行数据没有头像时
        if (!column.avatar) {
          //赋予初始化图片
          column.avatar = require('@/assets/logo.png')
        }
        return column
      })
    })
    return {
      columnList
    }
  }
})
</script>

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

继续,我们把 App.vuetestData 的数据进行删减。具体代码如下:

<template>
  <div class="container">
    <column-list :list="list"></column-list>
  </div>
</template>

<script lang="ts">

//制造子组件的接口数据
const testData: ColumnProps[] = [
  {
    id: 1,
    title: 'test1专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。'
    //avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  },
  {
    id: 2,
    title: 'test2专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。',
    avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  }
]

大家定位到 testData 中的 avatar 那一行,我们把第一个数据的 avatar 属性进行注释。现在,我们来看下浏览器的效果:

缺avatar时的效果

大家可以看到,缺 avatar 属性时,按照我们预期的,浏览器自动显示了我们预先初始化的图片。这样,不论从组件结构设计还是从代码逻辑结构设计来说,是不是感觉可扩展性又增强了许多。

☎️二、GlobalHeader全局Header

1、设计稿抢先看

写完 columnList 组件,我们用一个新的组件来强化这种设计方法。接下来我们来写一个新的组件,GlobalHeader ,即全局头部。先来看下我们要实现的效果图。详情见下图:

globalHeader原型图

2、数据构思

了解完具体的效果图之后呢,同样地,我们先来构思这个组件所需要的数据有哪一些。

这个组件所需要的数据,首先是针对每一个用户的,所以它每个用户拥有自己唯一的 id ,其次就是用户名 name ,最后一个是 是否登录 isLogin

分析完成之后,我们现在在 vue3 项目下的 src|components 文件夹下新建一个文件,命名为 GlobalHeader.vue 。之后编写这段业务代码。具体代码如下:

<template>
	<div></div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'

//用ts写一个接口,存放列表数据的属性
//name和id加?表示是可选项
export interface UserProps{
    isLogin: boolean;
    name?: string;
    id?: number;
}
export default defineComponent({
  name: 'GlobalHeader',
  props: {
    //将接口的内容赋值给user对象,方便接收父组件传来的数据
    user: {
      type: Object as PropType<UserProps>,
      required: true
    }
  }
})
</script>
<style lang="scss" scoped>
  
</style>

3、视图数据绑定

现在,对数据构思完毕之后,我们来把数据绑定到视图当中。具体代码如下:

<template>
  <nav class="navbar navbar-dark bg-primary justify-content-between mb-4 px-4">
        <a class="navbar-brand" href="#">周一专栏</a>
        <ul v-if="!user.isLogin" class="list-inline mb-0">
            <li class="list-inline-item"><a href="#" class="btn btn-outline-light my-2">登录</a></li>
            <li class="list-inline-item"><a href="#" class="btn btn-outline-light my-2">注册</a></li>
        </ul>
        <ul v-else class="list-inline mb-0">
            <li class="list-inline-item"><a href="#" class="btn btn-outline-light my-2">欢迎你 {{user.name}}</a></li>
        </ul>
    </nav>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
export interface ColumnProps {
  id: number;
  title: string;
  avatar?: string;
  description: string;
}
export default defineComponent({
  name: 'ColumnList',
  props: {
    list: {
      type: Array as PropType<ColumnProps[]>,
      required: true
    }
  }
})
</script>
<style lang="scss" scoped>
  
</style>

4、数据传递

现在,我们在 vue3 项目中的 src 文件夹下的 App.vue 中来进行数据传递。具体代码如下:

<template>
  <div class="container">
    <global-header :user="user"></global-header>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
//在根文件下引入bootstrap
import 'bootstrap/dist/css/bootstrap.min.css'
//引入子组件
import GlobalHeader, { UserProps } from './components/GlobalHeader.vue'

//制造子组件的接口数据
const currentUser: UserProps = {
  isLogin: false,
  name: 'Monday'
}

export default defineComponent({
  name: 'App',
  components: {
    GlobalHeader
  },
  setup () {
    return {
      user: currentUser
    }
  }
})
</script>

<style lang="scss" scoped>

</style>

当前 isLogin 的状态我们是设置成 false 。现在,我们来看下此时浏览器的运行效果:

isLogin为false时

大家可以看到,当前状态为 false ,所以 header 的右边显

以上是关于组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList的主要内容,如果未能解决你的问题,请参考以下文章

Vue3 企业级优雅实战 - 组件库框架 - 11 组件库的打包构建和发布

Vue3+TS Day34 - tsconfig.ts 、defineComponent、父组件调用子组件方法(函数)

vue3+ts如何改成js

Vue3_15(全局组件,局部组件,递归组件)

vue3全局注册组件

猿创征文Vue3 企业级优雅实战 - 组件库框架 - 7 组件库文档的开发和构建