Skip to content

UniApp 开发规范

目录


1. 功能说明

概述

本文档规定 UniApp 端的开发规范,包括代码风格、组件使用、平台适配等方面。

适用场景

  • UniApp 功能开发
  • 跨平台适配
  • 代码审查参考

2. 设计思路

规范目标

  • 统一多端代码风格
  • 提高跨平台兼容性
  • 优化小程序性能
  • 提升开发效率

3. 代码规范

3.1 文件命名规范

类型命名方式示例
页面文件kebab-caseuser-setting.vue
组件文件kebab-casegoods-card.vue
工具文件camelCaserequest.ts
样式文件kebab-casecommon-style.scss

3.2 页面规范

页面结构

vue
<template>
  <view class="page-user-setting">
    <!-- 导航栏 -->
    <nav-bar title="设置" />
    
    <!-- 页面内容 -->
    <view class="page-content">
      <!-- 内容区域 -->
    </view>
    
    <!-- 底部操作 -->
    <view class="page-footer">
      <!-- 底部按钮等 -->
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, onLoad } from '@dcloudio/uni-app';
import NavBar from '@/components/nav-bar/nav-bar.vue';

// 页面数据
const loading = ref(false);
const userInfo = ref<UserInfo | null>(null);

// 生命周期
onLoad((options) => {
  console.log('页面参数:', options);
  fetchUserInfo();
});

// 方法
const fetchUserInfo = async () => {
  loading.value = true;
  try {
    // 请求数据
  } finally {
    loading.value = false;
  }
};
</script>

<style lang="scss" scoped>
.page-user-setting {
  min-height: 100vh;
  background-color: #f5f5f5;
  
  .page-content {
    padding: 20rpx;
  }
  
  .page-footer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 20rpx;
    background-color: #fff;
  }
}
</style>

页面配置

json
// pages.json
{
  "path": "pages/user/setting",
  "style": {
    "navigationBarTitleText": "设置",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTextStyle": "black",
    "enablePullDownRefresh": true,
    "onReachBottomDistance": 50
  }
}

3.3 组件规范

组件定义

vue
<template>
  <view class="goods-card" @click="handleClick">
    <image class="goods-image" :src="image" mode="aspectFill" />
    <view class="goods-info">
      <text class="goods-title">{{ title }}</text>
      <text class="goods-price">¥{{ price }}</text>
    </view>
  </view>
</template>

<script setup lang="ts">
interface Props {
  id: number;
  title: string;
  price: number;
  image: string;
}

const props = withDefaults(defineProps<Props>(), {
  price: 0
});

const emit = defineEmits<{
  (e: 'click', id: number): void;
}>();

const handleClick = () => {
  emit('click', props.id);
};
</script>

<style lang="scss" scoped>
.goods-card {
  background-color: #fff;
  border-radius: 12rpx;
  overflow: hidden;
  
  .goods-image {
    width: 100%;
    height: 300rpx;
  }
  
  .goods-info {
    padding: 20rpx;
    
    .goods-title {
      font-size: 28rpx;
      color: #333;
      line-height: 1.4;
    }
    
    .goods-price {
      font-size: 32rpx;
      color: #ff4d4f;
      font-weight: bold;
    }
  }
}
</style>

3.4 API 封装规范

typescript
// src/api/member.ts
import { request } from '@/utils/request';
import type { MemberInfo, MemberUpdateParams } from './types';

/**
 * 获取会员信息
 */
export function getMemberInfo() {
  return request<MemberInfo>({
    url: '/api/member/info',
    method: 'GET'
  });
}

/**
 * 更新会员信息
 * @param data 更新数据
 */
export function updateMemberInfo(data: MemberUpdateParams) {
  return request({
    url: '/api/member/info',
    method: 'PUT',
    data
  });
}

/**
 * 上传头像
 * @param filePath 文件路径
 */
export function uploadAvatar(filePath: string) {
  return request({
    url: '/api/member/avatar',
    method: 'POST',
    filePath,
    name: 'file'
  });
}

3.5 状态管理规范

typescript
// src/stores/modules/user.ts
import { defineStore } from 'pinia';
import { getMemberInfo } from '@/api/member';
import type { MemberInfo } from '@/api/types';

interface UserState {
  info: MemberInfo | null;
  token: string;
  isLogin: boolean;
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    info: null,
    token: uni.getStorageSync('token') || '',
    isLogin: false
  }),

  actions: {
    // 设置 Token
    setToken(token: string) {
      this.token = token;
      this.isLogin = !!token;
      uni.setStorageSync('token', token);
    },

    // 获取用户信息
    async fetchUserInfo() {
      try {
        const res = await getMemberInfo();
        this.info = res.data;
        return res.data;
      } catch (error) {
        return Promise.reject(error);
      }
    },

    // 退出登录
    logout() {
      this.token = '';
      this.info = null;
      this.isLogin = false;
      uni.removeStorageSync('token');
    }
  }
});

3.6 样式规范

rpx 使用

scss
/* 推荐:使用 rpx 适配不同屏幕 */
.container {
  padding: 20rpx;
  font-size: 28rpx;
}

/* 特殊场景使用 px */
.border {
  border: 1px solid #eee;
}

变量定义

scss
// src/styles/variables.scss
// 颜色变量
$primary-color: #2979ff;
$success-color: #19be6b;
$warning-color: #ff9900;
$error-color: #fa3534;

// 文字颜色
$text-color: #303133;
$text-color-secondary: #909399;

// 背景颜色
$bg-color: #f5f5f5;
$bg-color-white: #ffffff;

// 边框颜色
$border-color: #ebeef5;

// 字体大小
$font-size-sm: 24rpx;
$font-size-base: 28rpx;
$font-size-lg: 32rpx;

3.7 平台适配规范

typescript
// src/utils/platform.ts

/**
 * 平台判断
 */
export const isH5 = () => {
  // #ifdef H5
  return true;
  // #endif
  return false;
};

export const isWeixin = () => {
  // #ifdef MP-WEIXIN
  return true;
  // #endif
  return false;
};

export const isApp = () => {
  // #ifdef APP-PLUS
  return true;
  // #endif
  return false;
};

/**
 * 获取平台特定配置
 */
export const getPlatformConfig = () => {
  // #ifdef H5
  return { uploadUrl: '/api/upload' };
  // #endif
  
  // #ifdef MP-WEIXIN
  return { uploadUrl: 'https://api.example.com/upload' };
  // #endif
  
  // #ifdef APP-PLUS
  return { uploadUrl: 'https://api.example.com/upload' };
  // #endif
};

4. 常见问题

Q1: 如何优化小程序包体积

解决方案:

  1. 使用分包加载
  2. 压缩图片资源
  3. 按需引入组件
  4. 删除无用代码
json
// pages.json
{
  "subPackages": [
    {
      "root": "pages-sub/goods",
      "pages": [...]
    }
  ]
}

Q2: 如何处理页面通信

typescript
// 页面 A - 发送数据
uni.navigateTo({
  url: '/pages/b/index?id=1'
});

// 或使用事件总线
uni.$emit('updateData', { key: 'value' });

// 页面 B - 接收数据
onLoad((options) => {
  console.log(options.id);
});

// 监听事件
onMounted(() => {
  uni.$on('updateData', (data) => {
    console.log(data);
  });
});

Q3: 如何实现下拉刷新和上拉加载

vue
<script setup lang="ts">
import { ref, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';

const list = ref([]);
const page = ref(1);
const loading = ref(false);
const finished = ref(false);

// 下拉刷新
onPullDownRefresh(async () => {
  page.value = 1;
  finished.value = false;
  await fetchList();
  uni.stopPullDownRefresh();
});

// 上拉加载
onReachBottom(() => {
  if (!finished.value && !loading.value) {
    page.value++;
    fetchList();
  }
});

const fetchList = async () => {
  loading.value = true;
  try {
    const res = await getList({ page: page.value });
    if (page.value === 1) {
      list.value = res.data;
    } else {
      list.value.push(...res.data);
    }
    finished.value = res.data.length < 10;
  } finally {
    loading.value = false;
  }
};
</script>

修订记录

版本日期修订人修订内容
v1.02026-04-08Niucloud Team初始版本