客户网站开发全流程图,泰国网站的域名,怎么查公司信息,网页设计分为哪几类用vue3ts实现全局Header和列表数据渲染ColumnList#x1f5bc;️序言#x1f4fb;一、ColumnList数据渲染1、设计稿抢先知2、数据构思3、视图数据绑定4、数据传递5、挠头情况☎️二、GlobalHeader全局Header1、设计稿抢先看2、数据构思3、视图数据绑定4、数据传递#x1f4f… 用vue3ts实现全局Header和列表数据渲染ColumnList️序言一、ColumnList数据渲染1、设计稿抢先知2、数据构思3、视图数据绑定4、数据传递5、挠头情况☎️二、GlobalHeader全局Header1、设计稿抢先看2、数据构思3、视图数据绑定4、数据传递三、Dropdown下拉菜单1、组件基本功能2、自定义菜单内容DropdownItem3、组件点击外部区域自动隐藏4、自定义函数5、联合效果四、结束语彩蛋 One More Thing基础知识回顾软件推荐番外篇️序言 
最近在用 vue3 和 ts 捣鼓一些小工具发现平常开发中一个很常见的需求就是数据列表的渲染。现在重新学习发现我在学 vue2 时的很多设计规范和逻辑都考虑的不是特别妥当。 
因此写下这篇文章记录组件设计中数据列表渲染和全局头部的设计。 
一起来学习吧~ 
一、ColumnList数据渲染 
1、设计稿抢先知 
在了解功能实现之前我们先来看看原型图看我们想要实现的数据列表是怎么样的。如下图所示 大家可以先了解一下我们待会所要实现内容的效果图。 
2、数据构思 
了解完具体的效果图之后呢现在我们要开始来干活啦 
首先我们需要先构思这个组件所需要的数据有哪一些呢 
这个组件所需要的数据首先是每一行数据它自己唯一的 id 其次就是标题 title 还有一个是头像 avatar 最后一个是每个标题对应的文字描述 description 。 
分析完成之后我们现在在 vue3 项目下的 src|components 文件夹下新建一个文件命名为 ColumnList.vue 。之后再编写这段业务代码。具体代码如下 
templatediv/div
/templatescript langts
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 PropTypeColumnProps[],required: true}}
})
/script
style langscss scoped/style 
3、视图数据绑定 
现在对数据构思完毕之后我们是还没有取到任何数据可以渲染的相当于是一个空的 ColumnList 。但是我们已经有了接口的属性内容所以我们先来把数据绑定到视图当中。具体代码如下 
templatediv classrowdiv v-forcolumn in columnList :keycolumn.id classcol-4 mb-3div classcard h-100 shadow-smdiv classcard-body text-centerimg :srccolumn.avatar :altcolumn.title classrounded-circle border border-light w-25 my-3h5 classtitle{{column.title}}/h5p classcard-text text-left{{column.description}}/pa href# classbtn btn-outline-primary进入专栏/a/div/div/div/div
/templatescript langts
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 PropTypeColumnProps[],required: true}}
})
/script
style langscss scoped/style 
注 这里我用到的是 bootstrap 的样式库所以 css 方面不做过多的编写大家有需要可以到官方中文文档进行查看也可以自己进行样式设计。 
到此我们就完成了第一轮的数据绑定。接下来我们在父组件中进行数据传递。 
4、数据传递 
我们在vue3项目中的 src 文件夹下的 App.vue 中来进行数据传递。具体代码如下 
templatediv classcontainercolumn-list :listlist/column-list/div
/templatescript langts
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/u3101694723,748884042fm26fmtautogp0.jpg},{id: 2,title: test2专栏,description: 众所周知 js 是一门弱类型语言并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误等到项目一上线浑然不觉地bug就UpUp了。于是在过去的这两年ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。,avatar: https://img0.baidu.com/it/u3101694723,748884042fm26fmtautogp0.jpg}
]export default defineComponent({name: App,components: {ColumnList},setup () {return {list: testData}}
})
/scriptstyle
#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 
现在我们来看下此时浏览器的运行效果 大家可以看到通过以上的代码编写数据正常的传递并运行成功了。 
5、挠头情况 
看到这里感觉整个组件的设计还挺尽善尽美的。但是呢大家有没有想过有一种特殊情况假设后端传来的数据中有一行数据里面没有头像avatar的值。那这个时候如果我们前期没有考虑清楚有可能遇到的各种情况程序估计很容易地就报错了。 
所以我们还要做的一件事情就是当收不到头像的数据时我们要给它加一张初始化的图片以至于保持列表内容一致。 
现在我们来对 ColumnList.vue 文件进行改造具体代码如下 
templatediv classrowdiv v-forcolumn in columnList :keycolumn.id classcol-4 mb-3div classcard h-100 shadow-smdiv classcard-body text-centerimg :srccolumn.avatar :altcolumn.title classrounded-circle border border-light w-25 my-3h5 classtitle{{column.title}}/h5p classcard-text text-left{{column.description}}/pa href# classbtn btn-outline-primary进入专栏/a/div/div/div/div
/templatescript langts
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 PropTypeColumnProps[],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}}
})
/scriptstyle langscss scoped/style 
继续我们把 App.vue 中 testData 的数据进行删减。具体代码如下 
templatediv classcontainercolumn-list :listlist/column-list/div
/templatescript langts//制造子组件的接口数据
const testData: ColumnProps[]  [{id: 1,title: test1专栏,description: 众所周知 js 是一门弱类型语言并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误等到项目一上线浑然不觉地bug就UpUp了。于是在过去的这两年ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。//avatar: https://img0.baidu.com/it/u3101694723,748884042fm26fmtautogp0.jpg},{id: 2,title: test2专栏,description: 众所周知 js 是一门弱类型语言并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误等到项目一上线浑然不觉地bug就UpUp了。于是在过去的这两年ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。,avatar: https://img0.baidu.com/it/u3101694723,748884042fm26fmtautogp0.jpg}
]大家定位到 testData 中的 avatar 那一行我们把第一个数据的 avatar 属性进行注释。现在我们来看下浏览器的效果 大家可以看到缺 avatar 属性时按照我们预期的浏览器自动显示了我们预先初始化的图片。这样不论从组件结构设计还是从代码逻辑结构设计来说是不是感觉可扩展性又增强了许多。 
☎️二、GlobalHeader全局Header 
1、设计稿抢先看 
写完 columnList 组件我们用一个新的组件来强化这种设计方法。接下来我们来写一个新的组件GlobalHeader 即全局头部。先来看下我们要实现的效果图。详情见下图 2、数据构思 
了解完具体的效果图之后呢同样地我们先来构思这个组件所需要的数据有哪一些。 
这个组件所需要的数据首先是针对每一个用户的所以它每个用户拥有自己唯一的 id 其次就是用户名 name 最后一个是 是否登录 isLogin 。 
分析完成之后我们现在在 vue3 项目下的 src|components 文件夹下新建一个文件命名为 GlobalHeader.vue 。之后编写这段业务代码。具体代码如下 
templatediv/div
/templatescript langts
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 PropTypeUserProps,required: true}}
})
/script
style langscss scoped/style 
3、视图数据绑定 
现在对数据构思完毕之后我们来把数据绑定到视图当中。具体代码如下 
templatenav classnavbar navbar-dark bg-primary justify-content-between mb-4 px-4a classnavbar-brand href#周一专栏/aul v-if!user.isLogin classlist-inline mb-0li classlist-inline-itema href# classbtn btn-outline-light my-2登录/a/lili classlist-inline-itema href# classbtn btn-outline-light my-2注册/a/li/ulul v-else classlist-inline mb-0li classlist-inline-itema href# classbtn btn-outline-light my-2欢迎你 {{user.name}}/a/li/ul/nav
/templatescript langts
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 PropTypeColumnProps[],required: true}}
})
/script
style langscss scoped/style 
4、数据传递 
现在我们在 vue3 项目中的 src 文件夹下的 App.vue 中来进行数据传递。具体代码如下 
templatediv classcontainerglobal-header :useruser/global-header/div
/templatescript langts
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}}
})
/scriptstyle langscss scoped/style 
当前 isLogin 的状态我们是设置成 false 。现在我们来看下此时浏览器的运行效果 大家可以看到当前状态为 false 所以 header 的右边显示的是登录和注册两个按钮如预期所料。 
现在我们来把 isLogin 的状态改为 true 具体代码如下 
const currentUser: UserProps  {isLogin: true,name: Monday
}此时我们来看下浏览器的显示效果如下图所示 现在可以看到当 isLogin 为 true 时表示用户成功登录了。所以 header 的右方显示的是 欢迎你 Monday 的字样也如我们预期所料。 
三、Dropdown下拉菜单 
看完上面的内容大家心里是否有一个疑惑我们 header 最右方的下拉菜单还没有实现。不着急接下来我们就来设计这个组件。 
1、组件基本功能 
我们现在先来设计下这个组件的基本功能。首先在 vue3 项目的 src|components 文件夹下新增一个 .vue 文件命名为 Dropdown.vue 。之后编写该文件的代码具体代码如下 
templatediv classdropdown!-- 下拉菜单标题 --a href# classbtn btn-outline-light my-2dropdown-toggle click.preventtoggleOpen(){{title}}/a!-- 下拉菜单内容 --ul classdropdown-menu :style{ display: block } v-ifisOpenli classdropdown-itema href#新建文章/a/lili classdropdown-itema href#编辑资料/a/li/ul/div
/templatescript langts
import { defineComponent, ref, onMounted, onUnmounted, watch } from vueexport default defineComponent({name: Dropdown,props: {title: {type: String,required: true}},setup() {const isOpen  ref(false)//点击后打开菜单const toggleOpen  ()  {isOpen.value  !isOpen.value}return {isOpen,toggleOpen}}
})
/scriptstyle langscss scoped/style 继续我们来改造下刚刚的 GlobalHeader.vue 文件。具体代码如下 
templatenav classnavbar navbar-dark bg-primary justify-content-between mb-4 px-4a classnavbar-brand href#周一专栏/aul v-if!user.isLogin classlist-inline mb-0li classlist-inline-itema href# classbtn btn-outline-light my-2登录/a/lili classlist-inline-itema href# classbtn btn-outline-light my-2注册/a/li/ulul v-else classlist-inline mb-0li classlist-inline-itemdropdown :title欢迎你 ${user.name}/dropdown/li/ul/nav
/templatescript langts
import { defineComponent, PropType } from vue
import Dropdown from ./Dropdown.vueexport interface UserProps{isLogin: boolean;name?: string;id?: number;
}
export default defineComponent({name: GlobalHeader,components: {Dropdown},props: {user: {type: Object as PropTypeUserProps,required: true}}
})
/scriptstyle langscss scoped/style 
现在我们来看下浏览器的显示效果 2、自定义菜单内容DropdownItem 
现在我们已经完成了组件的基本功能。但是细心的小伙伴已经发现下拉菜单没有办法自定义因为它被写成固定的了。还有一个问题就是点击其他区域我们不能收起菜单这间接地用户体验好像就没有那么好了。所以呢有需求了我们就来完成需求。现在我们就来解决上述所说的两个问题。 
同样地在vue3项目中的 src|components 文件夹下添加一个 .vue 文件命名为 DropdownItem.vue 。具体代码如下 
templateliclassdropdown-option:class{is-disabled: disabled}!-- 定义一个插槽供给父组件使用 --slot/slot/li
/templatescript langts
import { defineComponent } from vue
export default defineComponent({props: {//禁用状态属性disabled: {type: Boolean,default: false}}
})
/scriptstyle
/* 注意*表示两个类下面的所有元素 */
.dropdown-option.is-disabled * {color: #6c757d;/* 不让其点击将pointer-events设置为none */pointer-events: none;background: transparent;
}
/style 
接下来我们来将之前写死的内容进行该打造大家定位到 Dropdown.vue 文件具体代码如下 
templatediv classdropdown!-- 下拉菜单标题 --a href# classbtn btn-outline-light my-2dropdown-toggle click.preventtoggleOpen(){{title}}/a!-- 下拉菜单内容 --ul v-ifisOpen classdropdown-menu :style{ display: block }slot/slot/ul/div
/templatescript langts
import { defineComponent, ref  } from vueexport default defineComponent({name: Dropdown,props: {title: {type: String,required: true}},setup() {const isOpen  ref(false)//点击后打开菜单const toggleOpen  ()  {isOpen.value  !isOpen.value}return {isOpen,toggleOpen}}
})
/scriptstyle langscss scoped/style 
通过以上代码我们可以了解到将插槽放到 dropdown-menu 中去。 现在到了最后一步我们来看把它引入 GlobalHeader.vue 文件中。具体代码如下 
templatenav classnavbar navbar-dark bg-primary justify-content-between mb-4 px-4a classnavbar-brand href#周一专栏/aul v-if!user.isLogin classlist-inline mb-0li classlist-inline-itema href# classbtn btn-outline-light my-2登录/a/lili classlist-inline-itema href# classbtn btn-outline-light my-2注册/a/li/ulul v-else classlist-inline mb-0li classlist-inline-itemdropdown :title欢迎你 ${user.name}drop-down-itema href# classdropdown-item新建文章/a/drop-down-itemdrop-down-item disableda href# classdropdown-item编辑资料/a/drop-down-itemdrop-down-itema href# classdropdown-item退出登陆/a/drop-down-item/dropdown/li/ul/nav
/templatescript langts
import { defineComponent, PropType } from vue
import Dropdown from ./Dropdown.vue
import DropDownItem from ./DropDownItem.vueexport interface UserProps{isLogin: boolean;name?: string;id?: number;
}
export default defineComponent({name: GlobalHeader,components: {Dropdown,DropDownItem},props: {user: {type: Object as PropTypeUserProps,required: true}}
})
/scriptstyle langscss scoped/style 
此时我们来看下浏览器的显示效果 大家可以看到此时的编辑资料已经变成了灰色并且无法进行点击和跳转。同时自定义菜单的内容也一一显示了出来。 
到这里这一步的内容我们就完成啦接下来我们继续升华这个组件让它的用户体验更为极致。 
3、组件点击外部区域自动隐藏 
大家可以联想到平常自己点击各大网站时的场景当点击菜单外部区域时组件是不是就会自动隐藏。所以接下来我们就来实现这个功能。 
首先我们要明确需要完成的两个任务 
在 onMounted 时候添加 click 事件在 onUnmounted 的时候将事件删除拿到 Dropdown 的 DOM 元素从而判断点击的内容是否被这个元素包含。 
接下来我们定位到 Dropdown.vue 文件继续升级改造。具体代码如下 
templatediv classdropdown refdropdownRefa href# classbtn btn-outline-light my-2 dropdown-toggle click.preventtoggleOpen(){{title}}/aul v-ifisOpen classdropdown-menu :style{ display: block }slot/slot/ul/div
/templatescript langts
import { defineComponent, ref, onMounted, onUnmounted, watch } from vueexport default defineComponent({name: DropDown,props: {title: {type: String,required: true}},setup() {const isOpen  ref(false)// 获取ref的dow节点const dropdownRef  refnull | 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}}
})
/scriptstyle langscss scoped/style 
此时我们来看下浏览器的显示效果 大家可以看到改造完 dropdown 的逻辑后如我们预期所料的当我们点击组件外部区域时组件也自动隐藏了。 
4、自定义函数 
到这里整个 GlobalHeader 组件可以说是相对比较完美了。但是大家有没有发现在设计 dropdown 组件时dropdown.vue 的代码好像看起来还稍微有一点点冗余。 
这个时候我们可以考虑把它的逻辑抽离出来单独放到一个自定义函数里面。接下来一起来实现一下吧~ 
首先我们在 vue3 项目中的 src 文件夹下新建一个文件夹命名为 hooks 。之后在 hooks 下新建一个文件命名为 useClickOutside.ts 。 useClickOutside 的具体代码如下 
import { ref, onMounted, onUnmounted, Ref } from vue;const useClickOutside  (elementRef: Refnull | 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 化繁为简。具体代码如下 
templatediv classdropdown refdropdownRefa href# classbtn btn-outline-light my-2 dropdown-toggle click.preventtoggleOpen(){{title}}/aul v-ifisOpen classdropdown-menu :style{ display: block }slot/slot/ul/div
/templatescript langts
import { defineComponent, ref, onMounted, onUnmounted, watch } from vue
import useClickOutside from ../hooks/useClickOutsideexport default defineComponent({name: DropDown,props: {title: {type: String,required: true}},setup() {const isOpen  ref(false)// 获取ref的dow节点const dropdownRef  refnull | 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}}
})
/scriptstyle langts scoped/style 
此时浏览器的显示效果如下 大家可以看到最终的显示效果也是一样的。但是呢通过代码逻辑抽离我们整个组件的设计看起来也更加完美可扩展性也变得更高。 
5、联合效果 
最后我们把上面所学的GlobalHeader和Columnist结合起来来看一下一体化的效果。详情见下图 以上就是关于 ColumnList 和 GlobalHeader 两个组件的实现方式。不知道大家是否还意犹未尽呢~ 
后面还会持续出关于组件设计的文章欢迎关注~ 
四、结束语 
讲到这里关于组件 GlobalHeader 和 ColumnList 的设计就结束啦在设计组件的时候呢要特别考虑组件的可扩展性。如果一个组件在写的时候感觉没什么复用度那么这个时候可能就得思考下是不是哪个环节出现问题了。多问自己为什么多问自己这个组件是否能抽离的更好。 
以上就是本文的全部内容如有疑问或文章有误欢迎评论区留言或公众号后台加我微信交流~ 
彩蛋 One More Thing 
基础知识回顾 
vuejs基础知识ts基础知识 
软件推荐 
这里给大家推荐文章用到的一个画图软件Axure RP 9 
Axure RP 旨在用于画低保真原型图对于开发者来说也是极其友好的。丰富的控件库和动画交互可以满足日常画图的大部分需求。安利一波~ 
番外篇 关注公众号星期一研究室第一时间关注学习干货更多精选专栏待你解锁~  如果这篇文章对你有用记得留下脚印再走哦~  我们下期见