在使用 uniapp 进行小程序开发的时候,或多或少出于对样式或者功能需求,往往需要我们对小程序导航栏进行自定义操作。
一、自定义导航栏
1、效果图示
2、组件封装
- 导航栏组件中,我们需要在
页面显示前获取高度,因此不能使用 onLoad、onMounted 等生命周期。
生命周期onLoad
Vue3 在实现时 created 先于 onLoad 执行
uniapp 小程序的 onLoad 生命周期在页面显示之后才会执行,而 created 生命周期在页面显示之前执行。因此,不能使用 onLoad、更不能使用 onMounted,具体查看生命周期的适配。
- 为了方便 page 页面使用,封装组件时最好事先手动填充高度,减少影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <template> <view class="customNavigationBar"> <view class="bar-box"> <!-- 状态栏区域 --> <view :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 导航栏区域 --> <view class="custom-box" :style="{ height: barHeight + 'px' }"> <text>{{ title }}</text> </view> </view>
<!-- 仅用于填充作用 --> <view :style="{ height: barBoxHeight + 'px' }"></view> </view> </template>
<script setup lang="ts"> import { onLoad } from '@dcloudio/uni-app' import { onBeforeMount, ref } from 'vue'
const props = defineProps({ title: { type: String, default: '导航栏' } })
let statusBarHeight = ref(0) let barHeight = ref(0) let barBoxHeight = ref(0)
const dataInit = () => { // 1、状态栏区域高度 statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight
// 2、导航栏区域高度 // 自定义导航栏高度 = 胶囊按钮高度 + 胶囊的padding*2 const { top, height } = uni.getMenuButtonBoundingClientRect() barHeight.value = height ? height + (top - statusBarHeight.value) * 2 : 38
// 3、当前组件高度 barBoxHeight.value = statusBarHeight.value + barHeight.value } dataInit() </script>
<style lang="scss" scoped> .customNavigationBar { .bar-box { position: fixed; background-color: #ffffff; width: 100%; z-index: 999;
.custom-box { // 自定义导航栏样式 text-align: center; font-weight: bold; font-size: 17px; line-height: v-bind("barHeight + 'px'"); // js表达式 } } } </style>
|
3、使用示例
1 2 3 4 5 6 7 8 9 10 11
| { "pages": [ { "path": "pages/home/home", "style": { "navigationBarTitleText": "", "navigationStyle": "custom" } } ] }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <view class="home"> <customNavigationBar title="首页"></customNavigationBar>
<view class="content"> <!-- 页面内容 --> </view> </view> </template>
<script setup lang="ts"> // </script>
<style lang="scss" scoped> .home { // } </style>
|
二、导航栏使用问题
问题描述
尽管我们封装了自定义导航栏组件,但是在使用过程中,某些情况下我们不得不需要知道导航栏的高度,才能正确的布局页面内容。
1、flex 布局方案
让 content 盒子高度为 flex: 1,让其自动填充剩余空间,这是我常用的解决方案,也是最有效且简单的方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <view class="home"> <customNavigationBar title="首页"></customNavigationBar>
<view class="content"> <!-- 页面内容 --> </view> </view> </template>
<script setup lang="ts"> // </script>
<style lang="scss" scoped> .home { display: flex; flex-direction: column; min-height: 100vh;
.content { flex: 1;
// 页面内容样式 // ... } } </style>
|
2、获取 bar 高度方案
但有的时候,我们需要真正的获取导航栏的高度,如:首页全屏轮播效果。
生命周期created
uni.createSelectorQuery在页面生命周期的created阶段就可以使用。
在页面的 created 生命周期阶段,页面已经初始化完成,可以进行 DOM 的查询和操作,随后才会进行虚拟 DOM 的渲染。
- 不能使用
customBarRef.value.$el.offsetHeight,因为 customBarRef 是一个响应式对象,无法直接获取到 DOM 节点。
- 不能使用
nextTick(()=>{}),因为 nextTick(()=>{}) 是一个异步函数,在 created 生命周期中使用会导致页面渲染延迟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <template> <view class="home"> <customNavigationBar id="customBarRef" title="首页"></customNavigationBar>
<view class="content"> <view class="search-box"> <!-- search 组件 --> </view> <view class="notice-box"> <!-- uni-notice-bar --> </view> <view class="swiper-box"> <!-- swiper 组件 --> </view> <!-- ... --> </view> </view> </template>
<script setup lang="ts"> import { ref } from 'vue'
const customBarHeight = ref('') const dataInit = () => { const query = uni.createSelectorQuery().in(instance) query .select('#customBarRef') .boundingClientRect((data) => { customBarHeight.value = data.height + 'px' }) .exec() } dataInit() </script>
<style lang="scss" scoped> .home { display: flex; flex-direction: column; min-height: 100vh;
.content { flex: 1;
.search-box { height: 45px; } .notice-box { height: 35px; } .swiper-box { height: calc(100vh - 80px - v-bind(customBarHeight)); } // ... } } </style>
|