前端组知识库

👍欢迎大家积极投稿交流👍

如果文档内容陈旧或者链接失效,请发现后及时同步,我将尽快修改

👇微信👇

图片
Skip to content

layout 布局模板

功能说明

得益于 @uni-helper/vite-plugin-uni-layouts,你可以轻松地切换不同的布局。

src/layouts 文件夹下的 vue 文件都会自动生成一个布局,默认的布局文件名为 default ,路径 src/layouts/default.vue

如果需要修改使用的布局,可以通过 vue 文件内 route 代码块指定需要的布局,如下示例使用 demo 布局。

不推荐json5的写法

新版的 definePage 具有更高的灵活性和可读性。

vue
<route lang="json5">
{
  layout: 'navbar',
  style: {
    navigationBarTitleText: '关于',
  },
}
</route>

<script lang="ts" setup>
// JS逻辑
</script>

<template>
  <view>
    <!-- 这里可以写通用的布局,比如导航栏,tabbar 等 -->
    <!-- slot里面装的就是子页面的内容 -->
    <slot />
  </view>
</template>

👍 推荐写法

2025-08-28 更新, route-blockv3.12.0 已被 definePage 替代。

vue
<script lang="ts" setup>
definePage({
  layout: 'navbar',
  style: {
    navigationBarTitleText: '关于',
  },
})
</script>

<template>
  <view>
    <!-- 这里可以写通用的布局,比如导航栏,tabbar 等 -->
    <!-- slot里面装的就是子页面的内容 -->
    <slot />
  </view>
</template>

模板项目中除了默认的 default 布局,还提供了 navbar 布局,主要是用于自定义导航以及页面在顶部时为透明背景,当页面在滚动的时候,有透明背景变为指定颜色或者图片的效果,效果如下:

图片

使用方法

  1. 在使用这个自定义布局时,需要在 vue 文件内 definePage 禁用 layout ,直接使用 navbar 无法传递参数
  2. 模板中使用uni-layout组件通过name属性指定布局为navbar来实现效果。

使用页面滚动

vue
<script setup lang="ts">
definePage({
  layout: false,
  style: {
    navigationBarTitleText: '关于',
  },
})

// 因使用 AutoImport 自动引入的方式,需要在页面内调用 onPageScroll 事件,不处理回调即可,组件中已处理相应事件
onPageScroll(() => {})
</script>

<template>
  <uni-layout
    ref="uniLayout"
    name="navbar"
    title="标题"
    :placeholder="true"
    :fixed="true"
    navbar-style="background: 'linear-gradient(180deg, #d14328 0%, #a83220 100%)'"
  >
    <!-- 页面内容 -->
  </uni-layout>
</template>

使用 scroll-view 组件

vue
<script setup lang="ts">
definePage({
  layout: false,
  style: {
    navigationBarTitleText: '关于',
  },
})

// 定义 uni-layout 组件 Ref
const uniLayout = ref()

// 处理滚动事件
const handleScroll = (e) => {
  // 调用 uni-layout 组件的 setPageScroll 方法设置滚动距离
  uniLayout.value?.setPageScroll(e.detail.scrollTop)
}
</script>

<template>
  <uni-layout
    ref="uniLayout"
    name="navbar"
    title="标题"
    :placeholder="true"
    :fixed="true"
    use-scroll-view
    navbar-style="background: 'linear-gradient(180deg, #d14328 0%, #a83220 100%)'"
  >
    <!-- 页面内容 -->
    <scroll-view scroll-y @scroll="handleScroll">
      <!-- 内容 -->
    </scroll-view>
  </uni-layout>
</template>

参数说明

参数名类型默认值说明
namestringdefault布局名称,需要设置为 navbar
titlestring导航栏标题
borderedbooleantrue是否显示边框
fixedbooleantrue是否固定导航栏
placeholderbooleantrue是否显示占位符
safeAreaInsetTopbooleantrue是否顶部安全区域
useScrollViewbooleantrue是否使用 scroll-view,启用后页面的 onPageScroll 事件失效
distancenumber200导航栏背景完全显示需要滚动的距离,单位 px
navbarStylestring/CSSProperties导航栏自定义样式(字符串或者样式对象类型)
customStylestring/CSSPropertiesnavbar 组件自定义样式(字符串或者样式对象类型)基本不用,主要用 navbarStyle 控制显示效果

使用限制

navbar 布局中,传入组件的插槽,因组件使用 v-if="$slots.capsule" 的方式做判断,当自定义组件使用 <template v-if="capsule" #capsule></template> 时,v-if="$slots.capsule" 一直为 true ,并不能通过 v-if 动态控制是否使用当前插槽,在网页端可以通过动态插槽的方式解决,在小程序端不支持。目前有两种解决方案,不过都有一定的局限性,具体方法参考如下:

方式一:修改源码

wot-ui 改为 uni_modules 的安装方式,然后修改 wd-navbar 组件,将需要使用的插槽,添加通过 props 传参一起控制是否显示,示例代码如下:

vue
<template>
  <!-- 只展示部分代码 -->
  <view class="wd-navbar__capsule" v-if="$slots.capsule"> 
  <view class="wd-navbar__capsule" v-if="useCapsuleSlot && $slots.capsule"> 
    <slot name="capsule" />
  </view>
</template>

<script setup lang="ts">
import { navbarProps } from './types'

const props = defineProps(navbarProps)

</script>
ts
export const navbarProps = {
  ...baseProps,
  /**
   * 标题文字
   */
  title: String,

  // ..... 忽略中间代码

  /**
   * 是否使用胶囊插槽
   */
  useCapsuleSlot: makeBooleanProp(false),

  // 下边俩视情况决定

  /**
   * 是否使用标题插槽
   */
  useTitleSlot: makeBooleanProp(false),

  /**
   * 是否使用左侧插槽
   */
  useLeftSlot: makeBooleanProp(false),

  /**
   * 是否使用右侧插槽
   */
  useRightSlot: makeBooleanProp(false),
}

方式二:动态插槽

针对网页端使用,除了小程序端不支持,APP 端未测试,不确定是否支持,有使用过并且可行的话,可以告知

vue
<script setup lang="ts">
// 只展示部分代码,其他代码参考原组件
import { computed, useSlots } from 'vue'

// 获取所有传入的插槽然后处理为所需要的插槽
const delegatedSlots = computed(() => {
  // 获取所有传入的插槽
  const slots = useSlots()
  const resultSlots: string[] = []

  for (const key of Object.keys(slots)) {
    // 排除默认插槽
    if (key !== 'default') {
      resultSlots.push(key)
    }
  }

  // 如果使用胶囊插槽,添加胶囊插槽
  if (props.capsule) {
    resultSlots.push('capsule')
  }
  return resultSlots
})
</script>

<template>
  <!-- 只展示部分代码 -->
  <wd-navbar
    :title="title"
    :left-arrow="leftArrow"
    :left-text="leftText"
    :safe-area-inset-top="safeAreaInsetTop"
    :bordered="bordered"
    :custom-style="navbarCustomStyle"
    @click-left="handleBack"
  >
    <template v-for="slotName in delegatedSlots" :key="slotName" #[slotName]>
      <template v-if="slotName === 'capsule'">
        <wd-navbar-capsule @back="handleBack" @back-home="handleBackHome" />
      </template>
      <slot v-else :name="slotName" />
    </template>
  </wd-navbar>
</template>