Foundational Layout Elements
The framework ships with a suite of rendering primitives designed to mirror standard HTML tags while mapping directly to mini-program and native view engines. Although legacy tags like div or span parse without errors, the build pipeline automatical compiles them into native equivalents (view, text, navigator) when targeting non-H5 platforms. Consistently using the official component API guarantees predictable layout behavior and simplifies cross-platform debugging.
- view: Primary flex container. In
nvueenvironments, always wrap textual content insidetextto avoid layout fragmentation. - swiper: Handles carousel interactions. Child nodes must be
swiper-item. The engine enforces full-segment snapping; intermediate scroll positions are intentionally unsupported. - template / block: Invisible wrappers for conditional or list rendering. They inject zero DOM nodes. Prefer
templatefor consistent behavior acros WeChat, Alipay, and Toutiao ecosystems.
JavaScript Runtime & Platform APIs
H5 execution runs inside browser contexts, while Android mini-programs utilize V8 and iOS relies on JSCore. This architecture yields a dual-API surface: standard ECMAScript operations merged with vendor-agnostic uni.* extensions. Browser globals such as window, document, or location are intentionally absent outside H5 mode. Focus on native JavaScript constructs—arrays, iterators, promise chains—and rely on unified APIs for platform-specific features.
Key operational utilities include:
uni.getSystemInfoSync(): Synchronously extracts device metrics including screen resolution, model, and status bar height.- rpx (Responsive Pixels): Auto-scaling unit tiedd to viewport width. Baseline defaults to 750rpx. Designers often treat 1 logical pixel as 2rpx for rapid prototyping.
- Navigation Control: Inspect routes via
getCurrentPages(), push views withuni.navigateTo(), swap tabs usinguni.switchTab(), and traverse backwards withuni.navigateBack(). - Layout Anchoring:
uni.getMenuButtonBoundingClientRect()returns coordinates for platform floating menus (e.g., WeChat's capsule button), enabling precise padding overrides for custom headers. - Lifecycle Hooks:
onLoadintercepts initial query parameters.onShowandonHidemonitor visibility toggles. Tab navigation continuously fires these events, whereas stacked routing follows standard mount/unmount cycles. - Storage & Utilities:
uni.getStorageSync()retrieves cached values immediately.uni.makePhoneCall()triggers system dialers.
Constructing a Responsive Header Module
The following Composition API implementation demonstrates a dynamic navigation bar that distinguishes between root screens and nested routes. It calculates safe areas, maps floating menu boundaries, and applies theme variables reactively.
<template>
<view class="custom-header" :style="{ paddingTop: `${safeZoneHeight}px` }">
<!-- Status Bar Spacer -->
<view class="spacer" :style="containerStyles"></view>
<!-- Dashboard Layout -->
<view v-if="routeState === 'home'" class="header-row" :style="headerStyles">
<text class="region-label">{{ regionText }}</text>
<navigator
class="search-btn"
:url="searchPath"
:style="buttonLayoutStyles"
>
<text>Locate Provider</text>
</navigator>
</view>
<!-- Standard Page Layout -->
<view v-else class="page-bar" :style="headerStyles">
<view class="action-icon" @click="handleNavigation">
<image v-if="stackDepth > 1" src="/assets/icon-back.png" />
<image v-else src="/assets/icon-home.png" />
</view>
<view v-if="pageTitle" class="center-title" :style="textStyles">
{{ pageTitle }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, reactive } from 'vue'
const props = defineProps({
themeBg: { type: String, default: '#ffffff' },
textColor: { type: String, default: '#222222' },
charSize: { type: String, default: '30' },
iconDims: { type: String, default: '80' },
headerLabel: { type: String, default: '' },
isRootScreen: { type: Boolean, default: false },
searchRoute: { type: String, default: '/pages/query/main' }
})
const emit = defineEmits(['headerReady'])
const routeState = ref('home')
const stackDepth = ref(1)
const safeZoneHeight = ref(0)
const headerHeight = ref(0)
const menuRect = reactive({ top: 0, height: 40, width: 0 })
const containerStyles = computed(() => `background:${props.themeBg}`)
const headerStyles = computed(() => `height:${headerHeight.value}px;line-height:${headerHeight.value}px;padding-left:16px;`)
const textStyles = computed(() => `color:${props.textColor};font-size:${props.charSize}rpx;height:${headerHeight.value}px;line-height:${headerHeight.value}px;`)
const buttonLayoutStyles = computed(() => {
const marginT = Math.max(0, (menuRect.top * 2) - safeZoneHeight.value)
return `height:${menuRect.height * 2}px;line-height:${menuRect.height * 2}px;margin-top:${marginT}px;margin-right:${(menuRect.width * 2) + 16}px;border-radius:999px;background:#f8f8f8;text-align:center`
})
onMounted(() => {
initDimensions()
calculateMenuPosition()
stackDepth.value = getCurrentPages().length
routeState.value = props.isRootScreen ? 'home' : 'page'
emit('headerReady', {
statusBarPx: safeZoneHeight.value / 2,
navBarPx: headerHeight.value,
totalHeight: (safeZoneHeight.value + headerHeight.value) / 2
})
})
const initDimensions = () => {
const sys = uni.getSystemInfoSync()
safeZoneHeight.value = sys.statusBarHeight * 2
headerHeight.value = sys.system.includes('iOS') ? 88 : 96
}
const calculateMenuPosition = () => {
try {
Object.assign(menuRect, uni.getMenuButtonBoundingClientRect())
} catch (e) {
console.warn('Menu bounds unavailable, fallback applied')
}
}
const handleNavigation = () => {
if (stackDepth.value > 1) {
uni.navigateBack()
} else {
uni.switchTab({ url: '/pages/dashboard/index' })
}
}
</script>
<style scoped>
.custom-header {
position: fixed;
inset: 0 0 auto;
z-index: 50;
width: 100%;
}
.spacer {
background: inherit;
}
.header-row {
display: flex;
align-items: center;
}
.region-label {
font-weight: 600;
font-size: 28rpx;
padding-left: 48rpx;
background: url('/assets/bg-region.svg') left center / 32rpx no-repeat;
}
.search-btn {
flex: 1;
margin-left: 24rpx;
color: #999;
font-size: 24rpx;
padding-left: 28rpx;
background: url('/assets/bg-search.svg') left center / 20rpx no-repeat;
}
.page-bar {
position: relative;
}
.action-icon {
display: flex;
align-items: center;
width: 56rpx;
margin-left: 16rpx;
}
.action-icon image {
width: 56rpx;
height: 56rpx;
}
.center-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
white-space: nowrap;
}
</style>
Advanced Interaction Patterns
Modern single-file components leverage standardized dataset bindings to pass contextual payloads alongside event triggers. Assign data-* attributes directly in markup. The runtime populates the event target's dataset collection upon firing.
handleContextClick(evt) {
const payloadId = evt.currentTarget.dataset.recordId;
console.log('Selected module:', payloadId);
}
For decoupled communication across disjoint component trees, utilize the framework's native event emitter. This bypasses prop drilling entirely.
// Subscriber
uni.$on('profileUpdate', (newConfig) => {
console.log('Configuration refreshed:', newConfig);
});
// Publisher
function savePreferences(config) {
uni.$emit('profileUpdate', config);
}
Additional platform integrations streamline common user workflows. uni.showToast() displays transient feedback overlays, while uni.chooseAddress() delegates to the OS address picker. When implementing location-dependent features in restricted markets, declare the corresponding keys under requiredPrivateInfos in your manifest.json configuration to prevent runtime policy violations.