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

封面

用vue3+ts实现全局Header和列表数据渲染ColumnList

  • 🖼️序言
  • 📻一、ColumnList数据渲染
    • 1、设计稿抢先知
    • 2、数据构思
    • 3、视图数据绑定
    • 4、数据传递
    • 5、挠头情况
  • ☎️二、GlobalHeader全局Header
    • 1、设计稿抢先看
    • 2、数据构思
    • 3、视图数据绑定
    • 4、数据传递
  • 📸三、Dropdown下拉菜单
    • 1、组件基本功能
    • 2、自定义菜单内容DropdownItem
    • 3、组件点击外部区域自动隐藏
    • 4、自定义函数
    • 5、联合效果
  • 🛒四、结束语
  • 🐣彩蛋 One More Thing
    • 基础知识回顾
    • 软件推荐
    • 番外篇

🖼️序言

最近在用 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传递给setupsetup(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 的右边显示的是登录注册两个按钮,如预期所料。

现在,我们来把 isLogin 的状态改为 true具体代码如下:

const currentUser: UserProps = {isLogin: true,name: 'Monday'
}

此时我们来看下浏览器的显示效果,如下图所示:

isLogin为true时

现在,可以看到,当 isLogintrue 时,表示用户成功登录了。所以 header 的右方显示的是 欢迎你 Monday 的字样,也如我们预期所料。

📸三、Dropdown下拉菜单

看完上面的内容,大家心里是否有一个疑惑,我们 header 最右方的下拉菜单还没有实现。不着急,接下来我们就来设计这个组件。

1、组件基本功能

我们现在先来设计下这个组件的基本功能。首先在 vue3 项目的 src|components 文件夹下,新增一个 .vue 文件,命名为 Dropdown.vue 。之后编写该文件的代码,具体代码如下:

<template><div class="dropdown"><!-- 下拉菜单标题 --><a href="#" class="btn btn-outline-light my-2dropdown-toggle" @click.prevent="toggleOpen()">{{title}}</a><!-- 下拉菜单内容 --><ul class="dropdown-menu" :style="{ display: 'block' }" v-if="isOpen"><li class="dropdown-item"><a href="#">新建文章</a></li><li class="dropdown-item"><a href="#">编辑资料</a></li></ul></div>
</template><script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue'export default defineComponent({name: 'Dropdown',props: {title: {type: String,required: true}},setup() {const isOpen = ref(false)//点击后打开菜单const toggleOpen = () => {isOpen.value = !isOpen.value}return {isOpen,toggleOpen}}
})
</script><style lang="scss" scoped></style>

继续,我们来改造下刚刚的 GlobalHeader.vue 文件。具体代码如下:

<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"><dropdown :title="`欢迎你 ${user.name}`"></dropdown></li></ul></nav>
</template><script lang="ts">
import { defineComponent, PropType } from 'vue'
import Dropdown from './Dropdown.vue'export interface UserProps{isLogin: boolean;name?: string;id?: number;
}
export default defineComponent({name: 'GlobalHeader',components: {Dropdown},props: {user: {type: Object as PropType<UserProps>,required: true}}
})
</script><style lang="scss" scoped></style>

现在,我们来看下浏览器的显示效果:

dropdown组件基本功能

2、自定义菜单内容DropdownItem

现在,我们已经完成了组件的基本功能。但是细心的小伙伴已经发现,下拉菜单没有办法自定义,因为它被写成固定的了。还有一个问题就是,点击其他区域我们不能收起菜单,这间接地,用户体验好像就没有那么好了。所以呢,有需求了,我们就来完成需求。现在,我们就来解决上述所说的两个问题。

同样地,在vue3项目中的 src|components 文件夹下添加一个 .vue 文件,命名为 DropdownItem.vue 。具体代码如下:

<template><liclass="dropdown-option":class="{'is-disabled': disabled}"><!-- 定义一个插槽,供给父组件使用 --><slot></slot></li>
</template><script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({props: {//禁用状态属性disabled: {type: Boolean,default: false}}
})
</script><style>
/* 注意:*表示两个类下面的所有元素 */
.dropdown-option.is-disabled * {color: #6c757d;/* 不让其点击,将pointer-events设置为none */pointer-events: none;background: transparent;
}
</style>

接下来我们来将之前写死的内容进行该打造,大家定位到 Dropdown.vue 文件,具体代码如下:

<template><div class="dropdown"><!-- 下拉菜单标题 --><a href="#" class="btn btn-outline-light my-2dropdown-toggle" @click.prevent="toggleOpen()">{{title}}</a><!-- 下拉菜单内容 --><ul v-if="isOpen" class="dropdown-menu" :style="{ display: 'block' }"><slot></slot></ul></div>
</template><script lang="ts">
import { defineComponent, ref  } from 'vue'export default defineComponent({name: 'Dropdown',props: {title: {type: String,required: true}},setup() {const isOpen = ref(false)//点击后打开菜单const toggleOpen = () => {isOpen.value = !isOpen.value}return {isOpen,toggleOpen}}
})
</script><style lang="scss" scoped></style>

通过以上代码我们可以了解到,将插槽放到 dropdown-menu 中去。


现在,到了最后一步,我们来看把它引入 GlobalHeader.vue 文件中。具体代码如下:

<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"><dropdown :title="`欢迎你 ${user.name}`"><drop-down-item><a href="#" class="dropdown-item">新建文章</a></drop-down-item><drop-down-item disabled><a href="#" class="dropdown-item">编辑资料</a></drop-down-item><drop-down-item><a href="#" class="dropdown-item">退出登陆</a></drop-down-item></dropdown></li></ul></nav>
</template><script lang="ts">
import { defineComponent, PropType } from 'vue'
import Dropdown from './Dropdown.vue'
import DropDownItem from './DropDownItem.vue'export interface UserProps{isLogin: boolean;name?: string;id?: number;
}
export default defineComponent({name: 'GlobalHeader',components: {Dropdown,DropDownItem},props: {user: {type: Object as PropType<UserProps>,required: true}}
})
</script><style lang="scss" scoped></style>

此时,我们来看下浏览器的显示效果:

dropdown自定义菜单内容

大家可以看到,此时的编辑资料已经变成了灰色并且无法进行点击和跳转。同时,自定义菜单的内容也一一显示了出来。

到这里,这一步的内容我们就完成啦!接下来我们继续升华这个组件,让它的用户体验更为极致。

3、组件点击外部区域自动隐藏

大家可以联想到平常自己点击各大网站时的场景,当点击菜单外部区域时,组件是不是就会自动隐藏。所以,接下来我们就来实现这个功能。

首先我们要明确,需要完成的两个任务:

  • onMounted 时候添加 click 事件,在 onUnmounted 的时候将事件删除;
  • 拿到 DropdownDOM 元素,从而判断点击的内容是否被这个元素包含。

接下来我们定位到 Dropdown.vue 文件,继续升级改造。具体代码如下:

<template><div class="dropdown" ref="dropdownRef"><a href="#" class="btn btn-outline-light my-2 dropdown-toggle" @click.prevent="toggleOpen()">{{title}}</a><ul v-if="isOpen" class="dropdown-menu" :style="{ display: 'block' }"><slot></slot></ul></div>
</template><script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue'export default defineComponent({name: 'DropDown',props: {title: {type: String,required: true}},setup() {const isOpen = ref(false)// 获取ref的dow节点const dropdownRef = ref<null | HTMLElement>(null)const toggleOpen = () => {isOpen.value = !isOpen.value}const handler = (e: MouseEvent) => {if (dropdownRef.value) {// 用contains来判断是否包含另外一个dow节点// 当点击的不是当前区域的节点,并且菜单是打开的if (!dropdownRef.value.contains(e.target as HTMLElement) && isOpen.value) {isOpen.value = false}}}onMounted(() => {document.addEventListener('click', handler)})onUnmounted(() => {document.removeEventListener('click', handler)})return {isOpen,toggleOpen,dropdownRef,handler}}
})
</script><style lang="scss" scoped></style>

此时我们来看下浏览器的显示效果:

dowpdown组件点击外部区域自动隐藏

大家可以看到,改造完 dropdown 的逻辑后,如我们预期所料的,当我们点击组件外部区域时,组件也自动隐藏了。

4、自定义函数

到这里,整个 GlobalHeader 组件可以说是相对比较完美了。但是大家有没有发现,在设计 dropdown 组件时,dropdown.vue 的代码好像看起来还稍微有一点点冗余。

这个时候我们可以考虑把它的逻辑抽离出来,单独放到一个自定义函数里面。接下来一起来实现一下吧~

首先我们在 vue3 项目中的 src 文件夹下,新建一个文件夹,命名为 hooks 。之后在 hooks 下新建一个文件,命名为 useClickOutside.tsuseClickOutside具体代码如下:

import { ref, onMounted, onUnmounted, Ref } from "vue";const useClickOutside = (elementRef: Ref<null | HTMLElement>) => {const isClickOutside = ref(false)const handler = (e: MouseEvent) => {if (elementRef.value){if(elementRef.value.contains(e.target as HTMLElement)){isClickOutside.value = true   }else{isClickOutside.value = false}}}onMounted( () => {document.addEventListener('click', handler)})onUnmounted(() => {document.removeEventListener('click', handler)})return isClickOutside
}export default useClickOutside

抽离完代码后,我们继续把 Dropdown.vue 化繁为简。具体代码如下:

<template><div class="dropdown" ref="dropdownRef"><a href="#" class="btn btn-outline-light my-2 dropdown-toggle" @click.prevent="toggleOpen()">{{title}}</a><ul v-if="isOpen" class="dropdown-menu" :style="{ display: 'block' }"><slot></slot></ul></div>
</template><script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue'
import useClickOutside from '../hooks/useClickOutside'export default defineComponent({name: 'DropDown',props: {title: {type: String,required: true}},setup() {const isOpen = ref(false)// 获取ref的dow节点const dropdownRef = ref<null | HTMLElement>(null)const toggleOpen = () => {isOpen.value = !isOpen.value}const isClickOutside = useClickOutside(dropdownRef)if (isOpen.value && isClickOutside) {isOpen.value = false}watch(isClickOutside, () => {if (isOpen.value && isClickOutside.value) {isOpen.value = false}})return {isOpen,toggleOpen,dropdownRef}}
})
</script><style lang="ts" scoped></style>

此时浏览器的显示效果如下:

dowpdown组件点击外部区域自动隐藏

大家可以看到,最终的显示效果也是一样的。但是呢,通过代码逻辑抽离,我们整个组件的设计看起来也更加完美,可扩展性也变得更高。

5、联合效果

最后,我们把上面所学的GlobalHeader和Columnist结合起来,来看一下一体化的效果。详情见下图:

联合效果

以上就是关于 ColumnListGlobalHeader 两个组件的实现方式。不知道大家是否还意犹未尽呢~

后面还会持续出关于组件设计的文章,欢迎关注~

🛒四、结束语

讲到这里,关于组件 GlobalHeaderColumnList 的设计就结束啦!在设计组件的时候呢,要特别考虑组件的可扩展性。如果一个组件在写的时候,感觉没什么复用度,那么这个时候可能就得思考下,是不是哪个环节出现问题了。多问自己为什么,多问自己这个组件是否能抽离的更好。

以上就是本文的全部内容!如有疑问或文章有误欢迎评论区留言或公众号后台加我微信交流~

🐣彩蛋 One More Thing

基础知识回顾

  • vuejs基础知识
  • ts基础知识

软件推荐

这里给大家推荐文章用到的一个画图软件:Axure RP 9

Axure RP 旨在用于画低保真原型图,对于开发者来说也是极其友好的。丰富的控件库和动画交互,可以满足日常画图的大部分需求。安利一波~

番外篇

  • 关注公众号星期一研究室,第一时间关注学习干货,更多精选专栏待你解锁~

  • 如果这篇文章对你有用,记得留下脚印再走哦~

  • 我们下期见!👋👋👋

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307942.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初识ABP vNext(8):ABP特征管理

点击上方蓝字"小黑在哪里"关注我吧定义特征应用特征用户数量社交登录前言上一篇提到了ABP功能管理&#xff08;特征管理&#xff09;&#xff0c;它来自ABP的FeatureManagement模块&#xff0c;ABP官方文档貌似还没有这个模块的相关说明&#xff0c;但是个人感觉这个…

.NET Core 中导入导出Excel

操作Excel是一个比较常见的业务场景&#xff0c;本篇将使用EPPlus简单演示一个导入导出的示例。EPPlus开源地址&#xff1a;https://github.com/EPPlusSoftware/EPPlus在项目中添加EPPlus组件Install-Package EPPlus导入先准备一个Excel文件&#xff0c;将其内容读取出来&#…

不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识

一文了解webpack入门核心知识&#x1f3a8;序言&#x1f4c5;一、webpack究竟是什么1、写在前面2、什么是模块打包工具&#xff1f;&#x1f4d0;二、如何用Webpack搭建环境1、安装node2、创建项目3、初始化项目4、安装webpack5、安装具体版本的webpack⚙️三、Webpack的配置文…

剑指 Offer 05. 替换空格(两种做法)

一:题目 二:上码 1:方法一 class Solution { public:string replaceSpace(string s) {string str "";for(int i 0; i < s.size(); i) {if(s[i] ){str "%20";}else{str s[i];}}return str;} };2:方法二&#xff08;双指针&#xff09; class So…

.NET Core 中生成验证码

在开发中&#xff0c;有时候生成验证码的场景目前还是存在的&#xff0c;本篇演示不依赖第三方组件&#xff0c;生成随机验证码图片。先添加验证码接口public interface ICaptcha {/// <summary>/// 生成随机验证码/// </summary>/// <param name"codeLeng…

10分钟手把手教你用Android手撸一个简易的个人记账App

用Android手撸一个简易的个人记账系统⛱️序言&#x1f4cb;一、系统结构设计Design1. 需求分析2. 数据库设计3. 界面设计4. 过程设计&#x1f4d8;二、编码阶段Coding1. 项目结构&#x1f5c2;️&#xff08;1&#xff09;文件目录&#xff08;2&#xff09;AndroidManifest.x…

像素级调整,高效转换——轻松提升你的图片处理体验!

探索更高级的图片处理体验&#xff0c;我们为你带来像素级调整与高效转换的完美结合&#xff01;借助我们的专业工具&#xff0c;轻松调整图片像素&#xff0c;让你在细节处展现无限创意&#xff0c;提升作品质感。 第一步&#xff0c;进入首助编辑高手主页面&#xff0c;可以看…

ABP VNext实践之搭建可用于生产的IdentityServer4

一、前言用了半年多的abp vnext&#xff0c;在开发的效果还是非常的好&#xff0c;可以说节省了很多时间&#xff0c;像事件总线、模块化开发、动态API进行远程调用、自动API控制器等等&#xff0c;一整套的规范&#xff0c;让开发人员更方便的集成&#xff0c;提升效率&#x…

151. 翻转字符串里的单词(思路+详解)

一:题目 二:上码 class Solution { public://利用双指针使除去多余的空格,定义快慢指针&#xff0c;最后满指针指向的位置就是除去冗余空格后的字符串//大小void removespacing(string& s){int slowIndex 0,fastIndex 0;//去掉字符串前面的字符while(s.size() > 0 &a…

一文了解树在前端中的应用,掌握数据结构中树的生命线

一文了解树在前端中的应用&#x1f3d5;️序言&#x1f332;一、树是什么&#xff1f;&#x1f334;二、深/广度优先遍历1、深度优先遍历&#xff08;1&#xff09;定义&#xff08;2&#xff09;口诀&#xff08;3&#xff09;代码实现2、广度优先遍历&#xff08;1&#xff0…

【追加功能】OFFICE插件管理工具重整后再上路,更好用易用。

现在使用OFFICE插件的群体越来越多&#xff0c;在8月份修复过的【OFFICE插件管理工具】&#xff0c;尝试将COM加载项的插件管理进行完善。但仍然有一小部分普通加载项的管理未能加到里面。特别是近期用户反馈到的EasyShu插件不能取消加载问题&#xff08;这个是一个bug&#xf…

剑指 Offer 58 - II. 左旋转字符串

一:题目 二:上码 class Solution { public:string reverseLeftWords(string s, int n) {string str1 s.substr(0,n);//(开始位置&#xff0c;个数)string str2 s.substr(n);return str2str1;} };

太平洋大西洋水流问题如何解决?一文了解图在前端中的应用

一文了解图在前端中的应用&#x1f3a7;序言&#x1f3a4;一、图是什么&#xff1f;1、定义2、举例&#x1f3b9;二、图的表示法1、邻接矩阵表示法2、邻接表表示法&#x1f3ba;三、图的常用操作1、图的深度优先遍历&#xff08;1&#xff09;定义&#xff08;2&#xff09;口诀…

统信发布UOS V20 进军个人市场 生态日益完善

日前&#xff0c;统信桌面操作系统V20个人版正式发布&#xff0c;统信UOS是国产操作系统中屈指可数的在民间拥有一批爱好者和发烧友的操作系统&#xff0c;本次统信发布UOS V20个人版&#xff0c;吹响了进军TO C市场的号角。根据官方宣传介绍&#xff0c;统信桌面操作系统V20个…

leetcode28. 实现 strStr(KMP详解)

一:题目 二&#xff1a;思路 三:上码 // class Solution { // public: // int strStr(string haystack, string needle) { // if (needle.size()0) // return 0; // if (needle.size() > haystack.size()) // return -1; // …

在Docker中配置ASP.NETCore的HTTPS模式

&#xff08;The Continued Rising Power of Developers&#xff09;使用HTTPS&#xff0c;让网站更安全PS&#xff1a;经过两周的学习和部署迁移&#xff0c;目前已经把所有后端都迁到了基于Docker的Jenkins里了&#xff0c;相关文章可以参考《使用Jenkins来发布和代理.NetCor…

最小堆最大堆了解吗?一文了解堆在前端中的应用

一文了解堆在前端中的应用⚡序言&#x1f998;一、堆是什么&#xff1f;&#x1f425;二、JS中的堆&#x1f41d;三、堆的应用&#x1f408;四、构建一个最小堆1. 定义2. 方法3. 用js代码实现最小堆&#xff08;1&#xff09;初始化一个堆&#xff08;2&#xff09;交换位置swa…

​如何编写高质量的C#代码(一)

如何编写高质量的C#代码&#xff08;一&#xff09;从”整洁代码“谈起一千个读者&#xff0c;就有一千个哈姆雷特&#xff0c;代码质量也同样如此。想必每一个对于代码有追求的开发者&#xff0c;对于“高质量”这个词&#xff0c;或多或少都有自己的一丝理解。当我在长沙.NET…

可视化太酷辽!一文了解排序和搜索算法在前端中的应用

一文了解排序和搜索算法在前端中的应用⏳序言&#x1f9ed;一、文章结构抢先知⌚二、排序和搜索1、定义2、JS中的排序和搜索⏰三、排序算法1、冒泡排序&#x1f4a1;&#xff08;1&#xff09;定义&#xff08;2&#xff09;实现思路&#xff08;3&#xff09;图例&#xff08;…

leetcode双指针合集

27. 移除元素 class Solution { public:int removeElement(vector<int>& nums, int val) {/**思路:使用快慢指针&#xff0c;快指针正常往后移动,并将获取的值给慢指针&#xff0c;但是当快指针所指向的值是val的时候慢指针便不再更新;**/ int slowIndex 0;for(…