1. 今天的需求是封装一个 Navigation Bar 导航栏组件,目的是给到App几乎所有的页面复用:
2. 因为之前的项目里使用过Vant组件库,笔者第一时间想到了Vant组件库中的 NavBar 组件,和当前App的需求匹配度很高。Vant组件库的 NavBar 组件:
3. 相信你也发现了,Vant组件库默认主题色是蓝色,而当前App的主题色是绿色,翻看了下Vant组件库的API文档,存在 ConfigProvider 全局配置,简单修改下样式即可:
:root {
# 自定义主题色变量
--cp-primary: #16C2A3;
# 覆盖Vant主题色
--van-primary-color: var(--cp-primary);
}
笔者在 http://t.csdn.cn/cCGFP 文章中介绍过全局引入Vant样式,这里我把需要修改主题色的代码放在了项目 src/styles/main.scss 里,不明白的小伙伴可以翻看前文!
4. 准备工作完成后正式开干,项目 src/components 文件夹下新建 cp-nav-bar.vue 组件,因为前文配置过按需引入,这里直接使用 <van-nav-bar />
<template>
<div>
<van-nav-bar
:title="..."
:right-text="..."
left-arrow
fixed
@click-left="..."
@click-right="..."
/>
</div>
</template>
title 标题
right-text 右侧文案
left-arrow 是否显示左侧箭头
fixed 是否固定在顶部
click-left 点击左侧按钮时触发的事件
click-right 点击右侧按钮时触发的事件
跑起来观察后发现,van-nav-bar组件的字号和当前App有些差距,这里使用深度选择器修改下
<style scoped lang="scss">
:deep() {
.van-nav-bar {
&__arrow {
font-size: 18px;
}
&__text {
font-size: 15px;
}
}
}
</style>
vue3深度选择器不熟悉的同学,可以查看笔者的另一篇软文 http://t.csdn.cn/NkDbl
5. cp-nav-bar组件需要支持的功能:
① 支持 title rightText 属性,父传子实现 (标题和右侧文字的自定义)
② 支持 click-right 事件,子传父实现 (右侧点击事件的抛出)
注:右侧按钮对应要做的事情,应该是自己定义的,所以我们要支持事件,让他有自己的实现逻辑
③ 支持返回上一页功能
注: vue2和vue3获取路由方式有区别
this.$router => useRouter()
this.$route => useRoute()
6. 标题和右侧文字的自定义
# 当前组件里
<script lang="ts" setup>
withDefaults(
defineProps<{
title?: string
rightText?: string
}>(),
{
title: '标题',
rightText: '按钮'
}
)
</script>
# 使用组件时
<cp-nav-bar title="凡大来了" right-text="他真的来了" />
7. 右侧点击事件的抛出
# 当前组件里
<script lang="ts" setup>
const emits = defineEmits<{
(e: 'click-right'): void
}>()
const onClickRight = () => {
emits('click-right')
}
</script>
<template>
<div>
<van-nav-bar
:title="title"
:right-text="rightText"
left-arrow
fixed
@click-right="onClickRight"
/>
</div>
</template>
# 使用组件时
<template>
<div>
<cp-nav-bar
title="凡大来了"
right-text="他真的来了"
@click-right="handleClickRight"
/>
</div>
</template>
<script lang="ts" setup>
const handleClickRight = () => {
console.log('儿子的右侧按钮被点击了,父亲你要做点什么吗?')
}
</script>
8. 返回上一页功能
# 当前组件里
<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const onClickLeft = () => {
# 无脑回退是不严谨的
# 如果back字段是个null,强制拉回我们项目的首页,反之回退
# history不熟悉的,看下这里 https://developer.mozilla.org/zh-CN/docs/Web/API/History
if (history.state.back) {
router.back()
} else {
router.push('/')
}
}
</script>
<template>
<div>
<van-nav-bar
:title="title"
:right-text="rightText"
left-arrow
fixed
@click-left="onClickLeft"
@click-right="onClickRight"
/>
</div>
</template>
# 使用组件时
<cp-nav-bar />标签不需要任何操作,@click-left并没有抛出,只要引用了组件,都具有back功能
9. 有同学可能会有疑问:为什么可以直接使用组件,不导入不注册?
因为笔者在前文中 配置了 unplugin-vue-components 插件,该插 默认会把项目 src/compoenents 目录下的组件自动导入注册,是不是爽歪歪!!!
再次附上前文地址 http://t.csdn.cn/cCGFP
10. cp-nav-bar 组件类型配置
写到这里咱们的 cp-nav-bar 组件基本完成,还剩下最后一个痛点是缺失属性提示、事件提示、鼠标放上去也没有类型提示,这该怎么解决呢?
前文里我在 vite.config.js 文件中做了如下配置:
...
export default {
plugins: [
vue(),
Components({
# 不生成类型声明文件自己写
dts: false
...
})
]
}
对!没错!我取消了自动生成类型声明文件,实际上你配置了也没卵用!为了解决缺失提示的痛点,笔者在 src目录下 \ types目录下 \ 新建 components.d.ts
# 怎么给全局的组件提供类型?
# 写一个类型声明文件,declare module 'vue' 声明一个 vue 类型模块
# 然后 interface GlobalComponents 书写全局组件的类型
# key组件名称支持大驼峰, value是组件类型, 通过 typeof 组件实例得到
import CpNavBar from '@/components/cp-nav-bar.vue'
declare module 'vue' {
interface GlobalComponents {
CpNavBar: typeof CpNavBar
}
}
End-------------------