Mobile Development 16 min read

Common Pitfalls and Solutions When Developing WeChat Mini Programs with UniApp (Vue 3 + TypeScript)

This guide summarizes the typical problems encountered while building a WeChat mini‑program with uniapp + Vue 3 + TypeScript—such as nickname input validation, custom navigation bars, custom tabbars, iOS safe‑area handling, list scrolling quirks, and privacy‑policy configuration—and provides concrete code‑level solutions and best‑practice recommendations.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Common Pitfalls and Solutions When Developing WeChat Mini Programs with UniApp (Vue 3 + TypeScript)

The author recently built a WeChat mini‑program using the uniapp stack (uniapp + Vue 3 + TypeScript) and collected a series of common pitfalls, linking to official documentation and reliable articles for each issue.

1. Nickname Input Issue

Since 2022‑10‑25 the wx.getUserProfile and wx.getUserInfo APIs were removed, requiring developers to use the new nickname capability. The input field performs asynchronous compliance checks, so the value is not immediately available on blur. The solution is to await the check or listen to the bindnicknamereview event.

<uv-input v-model="form.name" type="nickname" placeholder="请输入内容" @blur="handleSubmit"></uv-input>
async function handleSubmit() {
  // wait for async nickname validation
  await new Promise(resolve => setTimeout(resolve, 0));
  console.log('form.value.name', form.value.name);
  if (form.value.name === rawName) return;
  // ...
}

If the nickname is rejected, the input is cleared automatically; therefore the bindnicknamereview callback must be used to retry or restore the original value.

<uv-input v-model="form.name" type="nickname" placeholder="请输入内容" @nicknamereview="handleSubmit"></uv-input>
function onNickNameReview(e) {
  console.log('onNickNameReview', e);
  if (e.detail.pass) {
    handleSubmit();
  } else {
    form.value.name = rawName;
  }
}

Because the uv‑ui library does not expose this event, the author patched the component source in node_modules/uv-input and submitted a PR.

2. Custom Navigation Bar

The native navigation bar cannot be customized (e.g., font size). The workaround is to set navigationStyle: "custom" in pages.json , calculate the status‑bar and capsule heights, and build a custom view.

// pages.json
{ navigationStyle: "custom" }
const statusBarHeight = ref(0);
const navBarHeight = ref(0);
statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight;
let menuButtonInfo = uni.getMenuButtonBoundingClientRect();
navBarHeight.value = menuButtonInfo.height + (menuButtonInfo.top - statusBarHeight.value) * 2;
<view class="nav-bar">
  <!-- status‑bar placeholder -->
  <view :style="{ height: statusBarHeight + 'px' }"></view>
  <!-- actual navigation content -->
  <view class="nav-bar-content" style="font-size: 34rpx;" :style="{ height: navBarHeight + 'px' }">导航栏标题</view>
</view>

Note: the native bar still follows system settings (dark mode, font size) which cannot be overridden.

3. Custom TabBar

To overcome the limitations of the native tab bar, enable custom: true in pages.json and create a custom-tab-bar component under src .

// pages.json
tabBar: { custom: true, /* ... */ }
<!-- src/custom-tab-bar/index.wxml -->
<view class="tab-bar">
  <view class="tab-bar-border"></view>
  <view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
    <image class="tab-bar-item-img" src="{{selected === index ? item.selectedIconPath : item.iconPath}}"></image>
    <view class="tab-bar-item-text" :style="{ color: selected === index ? selectedColor : color }">{{item.text}}</view>
  </view>
</view>
// src/custom-tab-bar/index.js
Component({
  data: {
    selected: 0,
    color: "#8d939f",
    selectedColor: "#e3eaf9",
    list: [
      { pagePath: "/pages/index/index", iconPath: "../static/tabbar/home01.png", selectedIconPath: "../static/tabbar/home02.png", text: "首页" },
      { pagePath: "/pages/my/my", iconPath: "../static/tabbar/user01.png", selectedIconPath: "../static/tabbar/user02.png", text: "我的" }
    ]
  },
  methods: {
    switchTab(e) {
      const { path, index } = e.currentTarget.dataset;
      wx.switchTab({ url: path });
      this.setData({ selected: index });
    }
  }
});

Each page must set the active index of the custom tab bar, e.g. in onShow :

onShow(() => {
  const currentPage = getCurrentPages()[0];
  const currentTabBar = currentPage?.getTabBar?.();
  currentTabBar?.setData({ selected: 0 });
});

4. iOS Safe‑Area Adaptation

Three approaches are described:

Configure safearea in manifest.json (background color only, not flexible for images).

Read system info via uni.getSystemInfoSync() to obtain statusBarHeight and bottom values.

Use CSS env() / constant() functions with viewport-fit=cover meta tag for dynamic padding.

// manifest.json
"app-plus": {
  "safearea": {
    "background": "#FFFFFF",
    "bottom": { "offset": "auto" }
  }
}
let app = uni.getSystemInfoSync();
console.log('statusBarHeight', app.statusBarHeight);
console.log('bottom safe area', app.bottom);
padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom);      /* iOS ≥ 11.2 */
/* with extra offset */
padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx);

5. List Scrolling Issues

Using overflow:auto on a list causes the whole page to pull down on the first swipe. Wrapping the list in a scroll-view solves the problem. For pull‑to‑refresh, enable the enhanced attribute and programmatically scroll to top.

<scroll-view scroll-y class="max-h-[800rpx] overflow-auto"></scroll-view>
<scroll-view id="scrollview" :enhanced="true" scroll-y class="max-h-[800rpx] overflow-auto"></scroll-view>
function scrollToTop(id) {
  wx.createSelectorQuery().select(id).node().exec(res => {
    const scrollView = res[0].node;
    scrollView.scrollTo({ top: 0, animated: true });
  });
}
onPullDownRefresh(async () => {
  try { await fetchList(); } finally { uni.stopPullDownRefresh(); scrollToTop('#scrollview'); }
});

6. Mini‑Program Privacy‑Protection Guide

When a mini‑program accesses any user data, a privacy agreement must be configured in the WeChat public‑platform settings. The author also sets __usePrivacyCheck__: true in the manifest, adds a custom privacy‑popup component, and handles location permission flow.

// manifest.config.ts
'mp-weixin': { __usePrivacyCheck__: true }
<WsWxPrivacy id="privacy-popup" @agree="onAgree" @disagree="onDisAgree" />
function handleCheckLocation() {
  return new Promise((resolve, reject) => {
    uni.getLocation({
      type: 'gcj02',
      success: async res => {
        try {
          let r = await checkLocation({ lon: res.longitude.toString(), lat: res.latitude.toString() });
          resolve('success');
        } catch (e) { reject(e); }
      },
      fail: err => { console.log('获取位置失败:', err); reject(err); }
    });
  });
}

The author also provides a reusable getSetting helper that checks the current authorization state, prompts the user with wx.showModal , and opens the settings page if needed.

function getSetting(scopeName, cb) {
  uni.getSetting({
    success: res => {
      if (res.authSetting[scopeName]) {
        cb();
      } else if (res.authSetting[scopeName] === false) {
        wx.showModal({
          title: '您未开启相关授权',
          content: '请在设置中开启授权',
          success: r => { if (r.confirm) wx.openSetting({ success: s => { if (s.authSetting[scopeName]) cb(); } }); }
        });
      } else {
        uni.authorize({ scope: scopeName, success: cb });
      }
    }
  });
}

Following these steps ensures the mini‑program complies with privacy regulations and provides a smooth user experience.

TypeScriptWeChat Mini Programprivacy protectionVue3uniappcustom-navigationcustom-tabbarios-safe-area
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.