Skip to content

兼容性开发指南

本指南专门针对 APP 端兼容性问题,提供详细的解决方案和最佳实践。如果您的项目仅针对 H5 或小程序平台,可以忽略以下内容。

💡 适用范围

本文档主要解决 uni-app x 在 APP 端(Android/iOS/鸿蒙)的兼容性问题,帮助开发者避免常见陷阱。

代码规范

必须使用双闭合标签

html
<!-- ❌ 错误写法 -->
<image />
<cl-image />

<!--  ✅ 成功写法 -->
<image></image>
<cl-image></cl-image>

函数开发规范

箭头函数参数限制

在 APP 端,箭头函数不支持可选参数,需要使用普通函数替代。

ts
// ❌ 错误写法:箭头函数可选参数
const getName = (age?: number) => {
  console.log(age);
};

getName(10); // ✅ 成功
getName(); // ❌ 报错:No value passed for parameter 'p1'

// ✅ 正确写法:普通函数 + 默认值
function getName(age: number | null = null) {
  console.log(age);
}

getName(10); // ✅ 成功
getName(); // ✅ 成功

导出函数使用陷阱

模板中使用导出函数时,需要注意 export functionexport const 的差异。

ts
// utils.ts
export function getName(val: string) {
  return val;
}

export const getName2 = (val: string) => {
  return val;
};
html
<!-- demo.uvue -->
<template>
  <!-- ❌ 错误:export function 在模板中使用会报错 -->
  <text>{{ getName('用户名') }}</text>

  <!-- ✅ 正确:export const 箭头函数可以正常使用 -->
  <text>{{ getName2('用户名') }}</text>
</template>

解决方案:在模板中优先使用 export const 导出的箭头函数。

样式开发注意事项

父子样式联动限制

APP 端不支持通过父级样式修改控制子元素样式,需要为子元素单独添加响应式类名。

html
<!-- ❌ 问题示例 -->
<view class="container" :class="{ 'active': isActive }">
  <text class="title">标题文本</text>
</view>

<style lang="scss" scoped>
  .container {
    @apply bg-white;

    .title {
      @apply text-gray-700;
    }

    &.active {
      @apply bg-blue-500; /* ✅ 父元素样式生效 */

      .title {
        @apply text-white; /* ❌ 子元素样式不生效 */
      }
    }
  }
</style>
html
<!-- ✅ 正确解决方案 -->
<view class="container" :class="{ 'active': isActive }">
  <text class="title" :class="{ 'active': isActive }">标题文本</text>
</view>

<style lang="scss" scoped>
  .container {
    @apply bg-white;

    &.active {
      @apply bg-blue-500;
    }
  }

  .title {
    @apply text-gray-700;

    &.active {
      @apply text-white; /* ✅ 生效 */
    }
  }
</style>

备选方案

  • 使用 v-if 条件渲染
  • 使用 key 强制重新渲染
  • 为子元素单独绑定响应式类名

动画属性设置

为避免鸿蒙平台的渲染异常,建议手动设置过渡属性。

css
/* ❌ 默认设置可能导致异常 */
.transition-element {
  transition-property: all;
}

/* ✅ 推荐明确指定过渡属性 */
.transition-element {
  transition-property: opacity, transform;
}

类型规范

v-model 类型声明

在 HBuilder X 4.75 以下版本,v-model 必须使用 as 进行类型断言。

html
<!-- ✅ 正确写法 -->
<cl-input v-model="content as string"></cl-input>

<script lang="ts" setup>
  import { ref } from "vue";
  const content = ref<string>("");
</script>

数组类型定义

对于复杂类型的数组,必须明确指定泛型和类型断言。

ts
type Item = {
  label: string;
  value: number;
};

// ✅ 正确:明确指定泛型
const list = ref<Item[]>([
  {
    label: "Vue",
    value: 1,
  },
  {
    label: "React",
    value: 2,
  },
]);

// ✅ 正确:添加元素时使用类型断言
list.value.push({
  label: "Angular",
  value: 3,
} as Item);

UTS 强类型限制

UTS 是强类型语言,不允许动态类型转换,即使类型结构相同。

ts
type UserA = {
  name: string;
  age: number;
};

type UserB = {
  name: string;
  age: number;
};

const userA = {
  name: "张三",
  age: 25,
} as UserA;

// ❌ 错误:即使结构相同也不能直接转换
console.log((userA as UserB).age);

// ✅ 正确:需要重新创建对象
const userB: UserB = {
  name: userA.name,
  age: userA.age,
};

条件判断规范

null 值判断

ts
let value: number | null = null;

// ❌ 错误用法
if (value) {
  console.log("有值");
}

// ✅ 正确用法
if (value != null) {
  console.log("有值");
}

// ✅ 推荐:使用工具函数
import { isNull } from "@/cool";
if (!isNull(value)) {
  console.log("有值");
}

逻辑或运算符

ts
let value: number | null = null;

// ❌ 错误用法
console.log(value || 1);

// ✅ 正确用法
console.log(value != null ? value : 1);

// ✅ 推荐:使用空值合并运算符
console.log(value ?? 1);

可空类型处理

严格区分可空和不可空类型,使用 | null? 定义可空属性。

ts
type User = {
  name: string;
  age: number;
  email?: string; // 可选属性
  avatar: string | null; // 可空属性
};

const user = ref<User | null>(null);

// ✅ 安全访问
const userName = user.value?.name ?? "匿名用户";
const userEmail = user.value?.email ?? "未设置";

UTSJSONObject 类型处理

默认对象类型为 UTSJSONObject,提供多种取值方法。

ts
const data = {
  name: "张三",
  age: 25,
  city: "北京",
};

// 基本取值方法
console.log(data.name); // 直接访问
console.log(data["name"]); // 方括号访问
console.log(data["undefined"]); // 不报错,返回 undefined

// 类型安全的取值方法
console.log(data.getString("name", "未知")); // 字符串类型
console.log(data.getNumber("age", 0)); // 数字类型
console.log(data.getBoolean("active", false)); // 布尔类型

类型转换最佳实践

ts
type User = {
  name: string;
  age: number;
  province?: string;
  city?: string;
};

const rawData = {
  name: "李四",
  age: 30,
};

// ❌ 错误:直接类型断言会报错
// console.log((rawData as User).name);

// ✅ 正确:使用解析函数
import { parse } from "@/cool";
const user = parse<User>(rawData);
console.log(user?.name);

开发限制说明

通用限制

  • 不支持 undefined: 所有变量使用前必须赋值
  • 变量声明: 使用 letconst,避免 var
  • 比较运算符: 优先使用 ==!=,避免 ===!==
  • 避免 any 类型: 使用具体类型或联合类型

CSS 限制

  • 暂不支持: 多个 dark: 样式绑定(开发中)

鸿蒙平台

窗口高度获取问题

ts
// ❌ 问题:获取的高度为 0
const { windowHeight } = uni.getWindowInfo();

// ✅ 解决方案:使用屏幕高度
const { screenHeight } = uni.getWindowInfo();

DOM 查询时机

ts
// ❌ 问题:立即查询可能获取不准确
onMounted(() => {
  const query = uni.createSelectorQuery();
  // 可能获取到错误值
});

// ✅ 解决方案:延迟 50ms 执行
onMounted(() => {
  setTimeout(() => {
    const query = uni.createSelectorQuery();
    // 获取准确值
  }, 50);
});

Android 平台

安全区域获取问题

ts
// ❌ 问题:首个页面底部安全区域为 0
const safeAreaBottom = uni.getSystemInfoSync().safeAreaInsets?.bottom;

// ✅ 解决方案:使用默认值或延迟获取
const safeBottom = safeAreaBottom || 20; // 提供默认值

相关资源