UniApp 使用教程:从入门到精通

📅 更新时间:2026年5月14日 🎯 适用版本:UniApp 4.x / Vue 3 💡 适合人群:跨平台应用开发者、Web前端转移动端


1. UniApp 简介

1.1 什么是 UniApp?

UniApp 是一个基于 Vue.js 的跨平台开发框架,一套代码可以编译到:

  • iOS
  • Android
  • H5(移动端浏览器)
  • 微信小程序
  • 支付宝小程序
  • 抖音小程序
  • QQ 小程序
  • 百度小程序
  • 快应用

1.2 核心优势

优势说明
一套代码多端运行节省开发成本,提高效率
基于 Vue.js前端开发者零门槛上手
性能优秀渲染性能接近原生
生态完善支持丰富的插件和组件
云开发支持内置云函数、数据库、存储
HBuilderX IDE强大的可视化开发工具

1.3 技术架构

┌─────────────────────────────────────────────────────────┐
│                      UniApp                              │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │   Vue 3     │  │   组件库    │  │   API 层    │     │
│  │   核心      │  │   uni-ui    │  │   uni API   │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────┤
│                    UniApp 内核                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐  │
│  │   iOS    │  │   安卓   │  │  小程序   │  │  H5   │  │
│  │   原生   │  │   原生   │  │   渲染    │  │  浏览器│  │
│  └──────────┘  └──────────┘  └──────────┘  └────────┘  │
└─────────────────────────────────────────────────────────┘

2. 核心概念

2.1 项目结构

my-uniapp/
├── pages/                  # 页面目录
│   ├── index/
│   │   └── index.vue       # 首页
│   ├── list/
│   │   └── list.vue        # 列表页
│   └── detail/
│       └── detail.vue      # 详情页
├── static/                 # 静态资源
│   ├── images/
│   └── fonts/
├── components/             # 公共组件
│   ├── my-header/
│   │   └── my-header.vue
│   └── my-list/
│       └── my-list.vue
├── utils/                  # 工具函数
│   └── request.js
├── store/                  # 状态管理
│   └── index.js
├── App.vue                 # 应用入口
├── main.js                 # Vue 入口
├── manifest.json           # 应用配置
├── pages.json              # 页面路由配置
├── uni.scss                # 全局样式变量
└── package.json           # 依赖配置

2.2 应用配置 (manifest.json)

{
  "name": "MyUniApp",
  "appid": "__UNI__XXXXXX",
  "description": "我的应用",
  "versionName": "1.0.0",
  "versionCode": "100",
  "transformPx": false,
  "app-plus": {
    "usingComponents": true,
    "nvueStyleCompiler": "uni-app",
    "compilerVersion": 3,
    "splashscreen": {
      "alwaysShowBeforeRender": true,
      "waiting": true,
      "autoclose": true,
      "delay": 0
    },
    "modules": {},
    "distribute": {
      "android": {
        "permissions": [
          "<uses-permission android:name=\"android.permission.INTERNET\"/>"
        ]
      },
      "ios": {}
    }
  },
  "quickapp": {},
  "mp-weixin": {
    "appid": "wx1234567890",
    "setting": {
      "urlCheck": false,
      "es6": true,
      "postcss": true,
      "minified": true
    },
    "usingComponents": true
  },
  "h5": {
    "title": "MyUniApp",
    "router": {
      "mode": "hash",
      "base": "./"
    }
  }
}

2.3 页面路由配置 (pages.json)

{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页",
        "enablePullDownRefresh": true,
        "navigationBarBackgroundColor": "#ffffff",
        "navigationBarTextStyle": "black"
      }
    },
    {
      "path": "pages/list/list",
      "style": {
        "navigationBarTitleText": "列表",
        "disableScroll": false
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "UniApp",
    "navigationBarBackgroundColor": "#F8F8F8",
    "backgroundColor": "#F8F8F8"
  },
  "tabBar": {
    "color": "#7A7E83",
    "selectedColor": "#3cc51f",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "iconPath": "static/tab-home.png",
        "selectedIconPath": "static/tab-home-active.png",
        "text": "首页"
      },
      {
        "pagePath": "pages/list/list",
        "iconPath": "static/tab-list.png",
        "selectedIconPath": "static/tab-list-active.png",
        "text": "列表"
      }
    ]
  }
}

3. 环境搭建

3.1 开发工具

推荐使用 HBuilderX

工具说明下载地址
HBuilderX官方 IDE,功能强大https://www.dcloud.io/hbuilderx.html
VS Code + 插件轻量级 + uni-app 插件VS Code Marketplace 搜索 uni

3.2 HBuilderX 使用

1. 下载并安装 HBuilderX
2. 安装 uni-app 语法提示插件(内置)
3. 创建或导入项目
4. 运行到浏览器/小程序模拟器/真机

3.3 命令行项目创建

# 确保已安装 Node.js (>= 16)
node -v
​
# 使用 vue-cli 创建项目
npx degit dcloudio/uni-preset-vue#vite my-uniapp
​
# 进入项目目录
cd my-uniapp
​
# 安装依赖
npm install
​
# 运行开发服务器
npm run dev:h5      # H5
npm run dev:mp-weixin  # 微信小程序
npm run dev:app       # Android/iOS
​
# 项目构建
npm run build:h5
npm run build:mp-weixin
npm run build:app

3.4 安装 Sass 支持

npm install sass sass-loader -D

4. 项目创建与运行

4.1 创建第一个页面

pages/index/index.vue

<template>
  <view class="container">
    <view class="title">欢迎使用 UniApp</view>
    <view class="content">
      <text>{{ message }}</text>
    </view>
    <button @click="handleClick">点击我</button>
  </view>
</template>
​
<script setup>
import { ref } from 'vue'
​
// 定义响应式数据
const message = ref('Hello UniApp!')
​
// 方法定义
const handleClick = () => {
  message.value = '你点击了按钮!'
  uni.showToast({
    title: '点击成功',
    icon: 'success'
  })
}
​
// 页面加载时触发
onLoad(() => {
  console.log('页面加载')
})
</script>
​
<style lang="scss" scoped>
.container {
  padding: 40rpx;
  text-align: center;
}
​
.title {
  font-size: 48rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 40rpx;
}
​
.content {
  font-size: 32rpx;
  color: #666;
  margin-bottom: 40rpx;
}
</style>

4.2 运行项目

# H5 模式运行
npm run dev:h5
# 访问 http://localhost:5173

# 微信小程序模式运行
npm run dev:mp-weixin
# 使用微信开发者工具打开 dist/dev/mp-weixin 目录

4.3 微信开发者工具配置

  1. 下载并安装 微信开发者工具
  2. 设置 → 安全设置 → 开启服务端口
  3. HBuilderX 运行 → 运行到小程序模拟器 → 微信开发者工具

5. 页面与路由

5.1 页面跳转

声明式导航(navigator 组件)

<!-- 跳转普通页面 -->
<navigator url="/pages/detail/detail?id=1&name=test">
  <button>跳转详情页</button>
</navigator>

<!-- 跳转 TabBar 页面 -->
<navigator url="/pages/index/index" open-type="switchTab">
  <button>跳转到首页</button>
</navigator>

<!-- 跳转并返回 -->
<navigator url="/pages/detail/detail" open-type="navigateBack">
  <button>返回上一页</button>
</navigator>

编程式导航(uni API)

// 跳转到普通页面
uni.navigateTo({
  url: '/pages/detail/detail?id=1&name=test'
})

// 跳转到 TabBar 页面
uni.switchTab({
  url: '/pages/index/index'
})

// 页面返回
uni.navigateBack({
  delta: 1  // 返回的页面数
})

// 关闭当前页面跳转
uni.redirectTo({
  url: '/pages/detail/detail'
})

// 关闭所有页面跳转(谨慎使用)
uni.reLaunch({
  url: '/pages/index/index'
})

// 预加载页面
uni.preloadPage({
  url: '/pages/detail/detail'
})

5.2 获取页面参数

// 在目标页面获取参数
onLoad((options) => {
  console.log('页面参数:', options)
  // { id: "1", name: "test" }
})

5.3 路由守卫

// main.js 中设置
const router = uni.createRouter({
  routes: [
    {
      path: '/pages/index/index',
      aliasPath: '/',
      name: 'index'
    }
  ]
})

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 检查登录状态
  const token = uni.getStorageSync('token')
  if (to.path !== '/pages/login/login' && !token) {
    next('/pages/login/login')
  } else {
    next()
  }
})

export default router

6. 组件开发

6.1 组件创建

components/my-header/my-header.vue

<template>
  <view class="header" :style="{ backgroundColor: bgColor }">
    <view class="header-left" @click="handleBack" v-if="showBack">
      <text class="icon">←</text>
    </view>
    <view class="header-title">
      <text>{{ title }}</text>
    </view>
    <view class="header-right">
      <slot name="right"></slot>
    </view>
  </view>
</template>

<script setup>
defineProps({
  title: {
    type: String,
    default: '标题'
  },
  bgColor: {
    type: String,
    default: '#ffffff'
  },
  showBack: {
    type: Boolean,
    default: true
  }
})

const emit = defineEmits(['back'])

const handleBack = () => {
  emit('back')
  uni.navigateBack()
}
</script>

<style lang="scss" scoped>
.header {
  display: flex;
  align-items: center;
  height: 88rpx;
  padding: 0 24rpx;
}

.header-left,
.header-right {
  width: 80rpx;
}

.header-title {
  flex: 1;
  text-align: center;
  font-size: 34rpx;
  font-weight: 500;
}

.icon {
  font-size: 40rpx;
}
</style>

6.2 组件使用

<template>
  <view>
    <my-header title="详情页" @back="onBack">
      <template #right>
        <text class="share">分享</text>
      </template>
    </my-header>
  </view>
</template>

<script setup>
import MyHeader from '@/components/my-header/my-header.vue'

const onBack = () => {
  console.log('返回上一页')
}
</script>

6.3 组件通信

Props 父传子

// 子组件定义 props
defineProps({
  title: String,
  list: {
    type: Array,
    default: () => []
  }
})

// 父组件传值
<child-component title="标题" :list="dataList" />

Emit 子传父

// 子组件触发事件
const emit = defineEmits(['update', 'delete'])
emit('update', newValue)
emit('delete', itemId)

// 父组件监听
<child-component @update="onUpdate" @delete="onDelete" />

Ref 获取组件实例

// 父组件
const childRef = ref(null)

onMounted(() => {
  childRef.value.someMethod()
})

// 子组件暴露方法
defineExpose({
  someMethod() {
    console.log('暴露的方法')
  }
})

6.4 uni-ui 组件库

# 安装 uni-ui
npm install @dcloudio/uni-ui

# 或在 HBuilderX 中直接使用

使用示例

<template>
  <view>
    <!-- 按钮 -->
    <uni-button type="primary" @click="handleClick">
      主按钮
    </uni-button>

    <!-- 卡片 -->
    <uni-card title="卡片标题" thumbnail="/static/logo.png">
      <text>卡片内容</text>
    </uni-card>

    <!-- 搜索栏 -->
    <uni-search-bar
      v-model="searchValue"
      placeholder="搜索"
      @confirm="onSearch"
    />

    <!-- 列表 -->
    <uni-list>
      <uni-list-item title="列表项1" />
      <uni-list-item title="列表项2" />
    </uni-list>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import {
  UniButton,
  UniCard,
  UniSearchBar,
  UniList,
  UniListItem
} from '@dcloudio/uni-ui'

const searchValue = ref('')

const handleClick = () => {
  uni.showToast({ title: '点击' })
}

const onSearch = () => {
  console.log('搜索:', searchValue.value)
}
</script>

7. 样式与布局

7.1 尺寸单位

单位说明转换规则
px屏幕像素固定值
rpx响应式像素屏幕宽度 / 750
vh视口高度CSS 标准
%百分比相对父元素

rpx 使用建议

// 页面全宽
width: 750rpx;

// 页面半宽
width: 375rpx;

// 元素间距
padding: 20rpx;
margin: 30rpx;

// 字体大小
font-size: 28rpx;

7.2 Flex 布局

<template>
  <view class="flex-container">
    <!-- 水平排列 -->
    <view class="flex-row">
      <view class="item">1</view>
      <view class="item">2</view>
      <view class="item">3</view>
    </view>

    <!-- 垂直排列 -->
    <view class="flex-column">
      <view class="item">1</view>
      <view class="item">2</view>
      <view class="item">3</view>
    </view>
  </view>
</template>

<style lang="scss" scoped">
// 水平居中
.flex-row {
  display: flex;
  justify-content: center;
  align-items: center;
}

// 两端对齐
.flex-between {
  display: flex;
  justify-content: space-between;
}

// 换行
.flex-wrap {
  display: flex;
  flex-wrap: wrap;
}

// 垂直列
.flex-column {
  display: flex;
  flex-direction: column;
}
</style>

7.3 全局样式变量 (uni.scss)

// uni.scss
// 主题色
$primary-color: #007aff;
$success-color: #4cd964;
$warning-color: #f0ad4e;
$error-color: #dd524d;

// 文字色
$text-color: #333333;
$text-color-inverse: #ffffff;
$text-color-grey: #999999;
$text-color-placeholder: #cccccc;

// 背景色
$bg-color: #f5f5f5;
$bg-color-grey: #eeeeee;

// 间距
$spacing-sm: 16rpx;
$spacing-md: 24rpx;
$spacing-lg: 32rpx;

// 圆角
$border-radius-sm: 8rpx;
$border-radius-md: 16rpx;
$border-radius-lg: 24rpx;

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

7.4 条件编译样式

/* #ifdef H5 */
.header {
  height: 60px;
  line-height: 60px;
}
/* #endif */

/* #ifdef MP-WEIXIN */
.header {
  height: 88rpx;
  line-height: 88rpx;
}
/* #endif */

/* #ifdef APP-PLUS */
.header {
  padding-top: 20px;
}
/* #endif */

8. 生命周期

8.1 应用生命周期

// App.vue
<script setup>
import { onLaunch, onShow, onHide, onError, onPageNotFound } from 'vue'

// 应用初始化完成
onLaunch(() => {
  console.log('App Launch')
  // 检查更新
  checkUpdate()
})

// 应用显示
onShow(() => {
  console.log('App Show')
  // 获取分享信息
})

// 应用隐藏
onHide(() => {
  console.log('App Hide')
  // 保存状态
})

// 报错监听
onError((err) => {
  console.error('App Error:', err)
  // 上报错误
})

// 页面不存在
onPageNotFound((res) => {
  console.log('Page Not Found:', res.path)
  // 跳转 404 页面
})
</script>

8.2 页面生命周期

<script setup>
import {
  onLoad,        // 监听页面加载
  onShow,        // 监听页面显示
  onReady,       // 监听页面初次渲染完成
  onHide,        // 监听页面隐藏
  onUnload,      // 监听页面卸载
  onPullDownRefresh,    // 监听下拉刷新
  onReachBottom,        // 监听上拉加载
  onPageScroll,         // 监听页面滚动
  onShareAppMessage,    // 监听用户点击右上角转发
  onShareTimeline       // 监听右上角分享到朋友圈
} from 'vue'

// 页面加载
onLoad((options) => {
  console.log('页面参数:', options)
  getData(options.id)
})

// 页面显示
onShow(() => {
  console.log('页面显示')
})

// 页面初次渲染完成
onReady(() => {
  console.log('页面渲染完成')
  // 可以开始使用 Canvas 等
})

// 页面隐藏
onHide(() => {
  console.log('页面隐藏')
})

// 页面卸载
onUnload(() => {
  console.log('页面卸载')
})

// 下拉刷新
onPullDownRefresh(() => {
  getData().then(() => {
    uni.stopPullDownRefresh()
  })
})

// 上拉加载
onReachBottom(() => {
  loadMore()
})

// 页面滚动
onPageScroll((e) => {
  console.log('滚动距离:', e.scrollTop)
})

// 分享给朋友
onShareAppMessage(() => {
  return {
    title: '分享标题',
    path: '/pages/index/index',
    imageUrl: '/static/share.png'
  }
})

// 分享到朋友圈
onShareTimeline(() => {
  return {
    title: '分享标题',
    query: 'from=share',
    imageUrl: '/static/share.png'
  }
})
</script>

8.3 组件生命周期

<script setup>
import {
  onMounted,
  onUpdated,
  onUnmounted,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount
} from 'vue'

// 组件即将挂载
onBeforeMount(() => {
  console.log('Before Mount')
})

// 组件挂载完成
onMounted(() => {
  console.log('Mounted')
  // 获取 DOM
})

// 组件即将更新
onBeforeUpdate(() => {
  console.log('Before Update')
})

// 组件更新完成
onUpdated(() => {
  console.log('Updated')
})

// 组件即将卸载
onBeforeUnmount(() => {
  console.log('Before Unmount')
})

// 组件卸载完成
onUnmounted(() => {
  console.log('Unmounted')
})
</script>

9. 状态管理

9.1 Pinia 状态管理

# 安装 Pinia
npm install pinia

store/index.js

import { createPinia } from 'pinia'

const pinia = createPinia()

export default pinia

store/user.js

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  // 状态
  const userInfo = ref(null)
  const token = ref('')
  const isLogin = computed(() => !!token.value)

  // 方法
  const setUserInfo = (info) => {
    userInfo.value = info
    uni.setStorageSync('userInfo', info)
  }

  const setToken = (newToken) => {
    token.value = newToken
    uni.setStorageSync('token', newToken)
  }

  const login = async (username, password) => {
    const res = await uni.request({
      url: '/api/login',
      method: 'POST',
      data: { username, password }
    })
    if (res.code === 0) {
      setToken(res.data.token)
      setUserInfo(res.data.userInfo)
      return true
    }
    return false
  }

  const logout = () => {
    userInfo.value = null
    token.value = ''
    uni.removeStorageSync('token')
    uni.removeStorageSync('userInfo')
  }

  // 初始化
  const init = () => {
    token.value = uni.getStorageSync('token') || ''
    userInfo.value = uni.getStorageSync('userInfo') || null
  }

  return {
    userInfo,
    token,
    isLogin,
    setUserInfo,
    setToken,
    login,
    logout,
    init
  }
})

main.js 引入

import { createSSRApp } from 'vue'
import App from './App.vue'
import pinia from './store'

export function createApp() {
  const app = createSSRApp(App)
  app.use(pinia)
  return { app }
}

组件中使用

<script setup>
import { useUserStore } from '@/store/user'

const userStore = useUserStore()

// 登录
const handleLogin = async () => {
  const success = await userStore.login('user', 'pass')
  if (success) {
    uni.showToast({ title: '登录成功' })
  }
}

// 退出登录
const handleLogout = () => {
  userStore.logout()
  uni.reLaunch({ url: '/pages/login/login' })
}
</script>

<template>
  <view>
    <text v-if="userStore.isLogin">
      欢迎,{{ userStore.userInfo?.name }}
    </text>
    <text v-else>未登录</text>
  </view>
</template>

9.2 全局状态共享

// mixins/global.js
import { useUserStore } from '@/store/user'

export const globalMixin = {
  computed: {
    isLogin() {
      const userStore = useUserStore()
      return userStore.isLogin
    },
    userInfo() {
      const userStore = useUserStore()
      return userStore.userInfo
    }
  }
}

10. 接口与云函数

10.1 请求封装 (utils/request.js)

// utils/request.js

// 创建请求实例
const request = (options) => {
  return new Promise((resolve, reject) => {
    const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000'

    uni.request({
      url: baseURL + options.url,
      method: options.method || 'GET',
      data: options.data || {},
      header: {
        'Content-Type': 'application/json',
        'Authorization': uni.getStorageSync('token') || '',
        ...options.header
      },
      timeout: 30000,
      success: (res) => {
        if (res.statusCode === 200) {
          if (res.data.code === 0) {
            resolve(res.data.data)
          } else {
            uni.showToast({
              title: res.data.message || '请求失败',
              icon: 'none'
            })
            reject(res.data)
          }
        } else if (res.statusCode === 401) {
          // Token 过期,跳转登录
          uni.removeStorageSync('token')
          uni.reLaunch({ url: '/pages/login/login' })
          reject(res)
        } else {
          uni.showToast({
            title: '网络错误',
            icon: 'none'
          })
          reject(res)
        }
      },
      fail: (err) => {
        uni.showToast({
          title: '网络请求失败',
          icon: 'none'
        })
        reject(err)
      }
    })
  })
}

// 导出常用方法
export const get = (url, data, header) =>
  request({ url, method: 'GET', data, header })

export const post = (url, data, header) =>
  request({ url, method: 'POST', data, header })

export const put = (url, data, header) =>
  request({ url, method: 'PUT', data, header })

export const del = (url, data, header) =>
  request({ url, method: 'DELETE', data, header })

export default request

10.2 API 接口管理 (api/user.js)

import { get, post } from '@/utils/request'

// 获取用户信息
export const getUserInfo = () => get('/api/user/info')

// 用户登录
export const login = (data) => post('/api/user/login', data)

// 用户注册
export const register = (data) => post('/api/user/register', data)

// 更新用户信息
export const updateUserInfo = (data) => post('/api/user/update', data)

10.3 云函数

云函数目录结构

cloudfunctions/
├── getUserInfo/
│   ├── index.js
│   └── package.json
└── login/
    ├── index.js
    └── package.json

云函数代码 (getUserInfo/index.js)

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

const db = cloud.database()

// 云函数入口函数
exports.main = async (event, context) => {
  const { userId } = event

  try {
    // 查询用户信息
    const res = await db.collection('users')
      .doc(userId)
      .get()

    return {
      success: true,
      data: res.data
    }
  } catch (e) {
    return {
      success: false,
      message: e.message
    }
  }
}

调用云函数

// 调用云函数
uni.cloud.callFunction({
  name: 'getUserInfo',
  data: {
    userId: 'xxx'
  },
  success: (res) => {
    console.log('云函数返回:', res.result)
  },
  fail: (err) => {
    console.error('调用失败:', err)
  }
})

10.4 云数据库

// 获取数据库引用
const db = wx.cloud.database()

// 查询数据
const result = await db.collection('todos')
  .where({
    done: false
  })
  .orderBy('createTime', 'desc')
  .skip(0)
  .limit(10)
  .get()

// 添加数据
await db.collection('todos').add({
  data: {
    title: '学习 UniApp',
    done: false,
    createTime: db.serverDate()
  }
})

// 更新数据
await db.collection('todos')
  .doc('todo-id')
  .update({
    data: {
      done: true,
      updateTime: db.serverDate()
    }
  })

// 删除数据
await db.collection('todos')
  .doc('todo-id')
  .remove()

11. 条件编译

11.1 条件编译语法

// #ifdef 平台标识
// 代码仅在指定平台编译
// #endif

// #ifndef 平台标识
// 代码在除指定平台外的平台编译
// #endif

11.2 平台标识

平台标识说明
H5H5 平台
MP-WEIXIN微信小程序
MP-ALIPAY支付宝小程序
MP-BAIDU百度小程序
MP-TOUTIAO抖音小程序
MP-QQQQ 小程序
APP-PLUSApp 平台
APP-NVUENVUE 平台

11.3 使用示例

组件条件编译

<template>
  <view>
    <!-- 微信小程序显示客服 -->
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="contact">客服</button>
    <!-- #endif -->

    <!-- App 显示原生导航 -->
    <!-- #ifdef APP-PLUS -->
    <view class="native-nav">原生导航</view>
    <!-- #endif -->

    <!-- H5 显示特殊处理 -->
    <!-- #ifdef H5 -->
    <view class="h5-special">H5 专属样式</view>
    <!-- #endif -->
  </view>
</template>

JS 条件编译

// #ifdef MP-WEIXIN
wx.login({
  success: (res) => {
    console.log('微信登录:', res.code)
  }
})
// #endif

// #ifdef APP-PLUS
uni.login({
  provider: 'weixin',
  success: (res) => {
    console.log('App 微信登录:', res.authResult)
  }
})
// #endif

// #ifdef H5
// H5 扫码登录逻辑
// #endif

样式条件编译

.container {
  padding: 20rpx;

  // 小程序特有样式
  // #ifdef MP-WEIXIN
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  // #endif

  // App 特有样式
  // #ifdef APP-PLUS
  padding-top: 40rpx;
  // #endif
}

12. 打包与发布

12.1 微信小程序发布

# 1. 运行构建
npm run build:mp-weixin

# 2. 打开 dist/build/mp-weixin 目录
# 3. 使用微信开发者工具导入
# 4. 在开发者工具中上传代码
# 5. 登录微信公众平台提交审核

12.2 App 打包

Android 应用打包

# 1. 修改 manifest.json 配置
# 2. 运行打包命令
npm run build:app

# 3. 在 HBuilderX 中
#    - 选择 "发行" → "原生 App-云打包"
#    - 选择 Android 证书
#    - 等待打包完成

iOS 应用打包

# 需要 macOS + HBuilderX
# 1. 配置 Apple 开发者证书
# 2. 发行 → 原生 App-云打包
# 3. 选择 iOS
# 4. 使用云打包或本地离线打包

12.3 H5 部署

# 1. 构建 H5 版本
npm run build:h5

# 2. 生成的 dist/build/h5 目录即为静态资源
# 3. 部署到服务器
#    - Nginx 配置
#    - Vercel / Netlify 部署
#    - 阿里云 OSS

Nginx 配置示例

server {
    listen 80;
    server_name your-domain.com;
    root /path/to/dist/build/h5;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # SPA 路由支持
    location /api {
        proxy_pass http://backend-server;
    }
}

12.4 App Store 发布注意事项

{
  "app-plus": {
    "distribute": {
      "ios": {
        "dSYMs": true,
        "idfa": false
      },
      "android": {
        "permissions": [
          "<uses-permission android:name=\"android.permission.INTERNET\"/>",
          "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>"
        ]
      }
    }
  }
}

13. 实战技巧与最佳实践

13.1 性能优化

1. 列表渲染优化

<!-- 使用 v-show 代替 v-if -->
<view v-show="showList">
  <view v-for="item in list" :key="item.id">
    {{ item.name }}
  </view>
</view>

<!-- 长列表使用虚拟列表 -->
<!-- 参考 uView 的虚拟列表组件 -->

2. 图片优化

<!-- 使用懒加载 -->
<image
  :src="item.thumb"
  mode="aspectFill"
  lazy-load
/>

<!-- 条件编译图片资源 -->
<!-- #ifdef MP-WEIXIN -->
<img src="/static/weixin-logo.png" />
<!-- #endif -->
<!-- #ifdef H5 -->
<img src="/static/h5-logo.png" />
<!-- #endif -->

3. 事件处理优化

// 事件节流
let lastTime = 0
const handleScroll = () => {
  const now = Date.now()
  if (now - lastTime > 100) {
    lastTime = now
    // 处理滚动逻辑
  }
}

// 函数防抖
import { debounce } from 'lodash-es'
const handleInput = debounce((value) => {
  console.log('搜索:', value)
}, 300)

13.2 登录鉴权

// router.js
const whiteList = ['/pages/login/login', '/pages/register/register']

router.beforeEach((to, from, next) => {
  const hasToken = uni.getStorageSync('token')

  if (hasToken) {
    if (to.path === '/pages/login/login') {
      next('/pages/index/index')
    } else {
      next()
    }
  } else {
    if (whiteList.includes(to.path)) {
      next()
    } else {
      uni.showToast({
        title: '请先登录',
        icon: 'none'
      })
      setTimeout(() => {
        uni.reLaunch({ url: '/pages/login/login' })
      }, 1000)
    }
  }
})

13.3 错误处理

// 全局错误处理
const errorHandler = (error, vm, info) => {
  console.error('全局错误:', error)
  console.error('错误信息:', info)

  // 上报错误
  reportError({
    error: error.toString(),
    info,
    time: new Date().toISOString()
  })
}

// 注册全局错误处理器
app.config.errorHandler = errorHandler

// Promise 未处理错误
app.config.onUnhandledRejection = (res) => {
  console.error('未处理的 Promise 错误:', res)
}

13.4 常用工具函数

// utils/index.js

// 防抖
export const debounce = (fn, delay = 300) => {
  let timer = null
  return function (...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

// 节流
export const throttle = (fn, delay = 300) => {
  let lastTime = 0
  return function (...args) {
    const now = Date.now()
    if (now - lastTime > delay) {
      lastTime = now
      fn.apply(this, args)
    }
  }
}

// 格式化日期
export const formatDate = (date, format = 'YYYY-MM-DD') => {
  const d = new Date(date)
  const year = d.getFullYear()
  const month = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')
  const hours = String(d.getHours()).padStart(2, '0')
  const minutes = String(d.getMinutes()).padStart(2, '0')
  const seconds = String(d.getSeconds()).padStart(2, '0')

  return format
    .replace('YYYY', year)
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hours)
    .replace('mm', minutes)
    .replace('ss', seconds)
}

// 深拷贝
export const deepClone = (obj) => {
  return JSON.parse(JSON.stringify(obj))
}

// 生成唯一 ID
export const generateId = () => {
  return Date.now().toString(36) + Math.random().toString(36).substr(2)
}

附录:常用命令

命令说明
npm run dev:h5开发 H5
npm run dev:mp-weixin开发微信小程序
npm run dev:app开发 App
npm run build:h5构建 H5
npm run build:mp-weixin构建微信小程序
npm run build:app构建 App

附录:常用插件推荐

插件说明
uView多平台快速开发 UI 框架
uni-uiDCloud 官方 UI 组件库
uni-requestPromise 化请求库
piniaVue 3 状态管理
lodash-esJavaScript 工具库

参考资源


💡 提示:UniApp 生态丰富,建议多参考官方文档和社区资源!

🔥 实战建议:从小程序端开始开发,完成后再扩展到 App 和 H5

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容