UniApp跨平台开发实战:从零构建多端任务管理应用

2026-04-26 0 712

2025年,UniApp已经成为国内最流行的跨平台开发框架之一。它基于Vue.js,一套代码可以同时发布到iOS、Android、H5以及各类小程序。本文通过一个完整的任务管理应用案例,带你掌握UniApp的项目架构、状态管理多端适配以及发布流程。


1. 为什么选择UniApp?

UniApp的核心优势在于“一次开发,多端运行”。与React Native或Flutter相比,它更贴近前端开发者的Vue生态,且对国内小程序生态支持极好。

  • 多端覆盖:App、H5、微信/支付宝/百度/抖音小程序
  • Vue生态:支持Vue3 Composition API,上手快
  • 热重载:开发体验接近H5开发
  • 插件丰富:原生插件市场提供大量功能模块

2. 环境搭建与项目创建

首先安装HBuilderX(UniApp官方IDE)或使用CLI方式:

# 使用CLI创建项目(需要Node.js 18+)
npx @dcloudio/uni-app-cli create task-manager
cd task-manager
npm install
npm run dev:mp-weixin  # 运行到微信小程序
npm run dev:h5         # 运行到H5

项目目录结构:

task-manager/
├── src/
│   ├── pages/          # 页面文件
│   ├── components/     # 公共组件
│   ├── store/          # 状态管理(Pinia)
│   ├── api/            # API请求
│   ├── utils/          # 工具函数
│   ├── App.vue         # 根组件
│   ├── main.js         # 入口文件
│   ├── pages.json      # 页面路由配置
│   ├── manifest.json   # 应用配置
│   └── uni.scss        # 全局样式变量
├── package.json
└── vite.config.js

3. 页面路由与TabBar配置

pages.json 中配置页面路径和底部导航:

{
  "pages": [
    {"path": "pages/index/index", "style": {"navigationBarTitleText": "任务列表"}},
    {"path": "pages/add/add", "style": {"navigationBarTitleText": "添加任务"}},
    {"path": "pages/profile/profile", "style": {"navigationBarTitleText": "个人中心"}}
  ],
  "tabBar": {
    "color": "#999",
    "selectedColor": "#007aff",
    "list": [
      {"pagePath": "pages/index/index", "text": "任务", "iconPath": "static/task.png", "selectedIconPath": "static/task_hl.png"},
      {"pagePath": "pages/add/add", "text": "添加", "iconPath": "static/add.png", "selectedIconPath": "static/add_hl.png"},
      {"pagePath": "pages/profile/profile", "text": "我的", "iconPath": "static/profile.png", "selectedIconPath": "static/profile_hl.png"}
    ]
  }
}

4. 状态管理:使用Pinia构建任务Store

UniApp官方推荐使用Pinia进行状态管理。安装:

npm install pinia

创建 src/store/task.js

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

export const useTaskStore = defineStore('task', () => {
  // 状态
  const tasks = ref([
    { id: 1, title: '学习UniApp', done: false, createdAt: '2025-01-15' },
    { id: 2, title: '编写技术文章', done: true, createdAt: '2025-01-14' }
  ])
  
  // 计算属性
  const pendingTasks = computed(() => tasks.value.filter(t => !t.done))
  const completedTasks = computed(() => tasks.value.filter(t => t.done))
  
  // 动作
  function addTask(title) {
    tasks.value.push({
      id: Date.now(),
      title,
      done: false,
      createdAt: new Date().toISOString().slice(0, 10)
    })
  }
  
  function toggleTask(id) {
    const task = tasks.value.find(t => t.id === id)
    if (task) task.done = !task.done
  }
  
  function deleteTask(id) {
    tasks.value = tasks.value.filter(t => t.id !== id)
  }
  
  return { tasks, pendingTasks, completedTasks, addTask, toggleTask, deleteTask }
})

main.js 中注册Pinia:

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

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

5. 主页面:任务列表(index.vue)

<template>
  <view class="container">
    <view class="header">
      <text class="title">我的任务</text>
      <text class="count">待办: {{ store.pendingTasks.length }}</text>
    </view>
    
    <view class="task-list">
      <view class="task-item" v-for="task in store.tasks" :key="task.id">
        <view class="task-left">
          <view 
            class="checkbox" 
            :class="{ checked: task.done }"
            @click="store.toggleTask(task.id)"
          >
            <text v-if="task.done">✓</text>
          </view>
          <text class="task-title" :class="{ done: task.done }">{{ task.title }}</text>
        </view>
        <text class="delete-btn" @click="store.deleteTask(task.id)">删除</text>
      </view>
      
      <view v-if="store.tasks.length === 0" class="empty">
        <text>暂无任务,点击下方添加</text>
      </view>
    </view>
  </view>
</template>

<script setup>
import { useTaskStore } from '@/store/task'
const store = useTaskStore()
</script>

6. 添加任务页面(add.vue)

<template>
  <view class="container">
    <view class="form">
      <input 
        class="input" 
        v-model="title" 
        placeholder="请输入任务标题"
        @confirm="handleSubmit"
      />
      <button class="btn" @click="handleSubmit">添加任务</button>
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { useTaskStore } from '@/store/task'
import { useRouter } from '@/utils/router'  // uni-app内置路由

const store = useTaskStore()
const title = ref('')

function handleSubmit() {
  const trimmed = title.value.trim()
  if (!trimmed) {
    uni.showToast({ title: '请输入任务内容', icon: 'none' })
    return
  }
  store.addTask(trimmed)
  uni.showToast({ title: '添加成功', icon: 'success' })
  title.value = ''
  // 返回上一页
  setTimeout(() => uni.navigateBack(), 500)
}
</script>

7. 多端适配技巧

UniApp使用条件编译处理平台差异,文件后缀或代码中的条件宏:

<!-- 条件编译示例 -->
<!-- #ifdef MP-WEIXIN -->
<view class="wx-only">仅微信小程序显示</view>
<!-- #endif -->

<!-- #ifdef H5 -->
<view class="h5-only">仅H5显示</view>
<!-- #endif -->

<!-- #ifndef APP-PLUS -->
<view>非App端显示</view>
<!-- #endif -->

在JavaScript中:

// #ifdef H5
console.log('仅在H5环境执行')
// #endif

// #ifdef APP-PLUS
console.log('仅在App环境执行')
// #endif

样式适配建议:

  • 使用rpx单位(类似小程序的rpx),自动适配屏幕宽度
  • 避免使用固定px,改用百分比或flex布局
  • 通过 uni.getSystemInfoSync() 获取设备信息动态调整

8. 网络请求与API封装

创建 src/api/request.js

const BASE_URL = 'https://api.example.com'

export function request(config) {
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        'Authorization': uni.getStorageSync('token') || ''
      },
      success: (res) => {
        if (res.statusCode === 200) {
          resolve(res.data)
        } else {
          uni.showToast({ title: '请求失败', icon: 'error' })
          reject(res)
        }
      },
      fail: (err) => {
        uni.showToast({ title: '网络错误', icon: 'none' })
        reject(err)
      }
    })
  })
}

// 使用示例
export function fetchTasks() {
  return request({ url: '/tasks', method: 'GET' })
}

export function createTask(data) {
  return request({ url: '/tasks', method: 'POST', data })
}

9. 打包与发布

平台 打包命令 产物路径
H5 npm run build:h5 dist/build/h5
微信小程序 npm run build:mp-weixin dist/build/mp-weixin
App (Android) HBuilderX -> 发行 -> 原生App-云打包 apk或ipa
支付宝小程序 npm run build:mp-alipay dist/build/mp-alipay

发布前检查清单:

  • manifest.json 中配置应用名称、图标、版本号
  • 检查各平台权限配置(如相机、定位)
  • 使用 uni-app 性能分析 工具优化包体积
  • 测试不同屏幕尺寸下的UI表现

10. 性能优化与最佳实践

  • 组件按需加载:使用 uni.lazyLoad 或动态导入
  • 列表性能:长列表使用 uni-virtual-listscroll-view 的增强模式
  • 图片优化:使用 image 组件的 lazy-load 属性和WebP格式
  • 状态管理:避免在Pinia中存储大量数据,使用持久化插件 pinia-plugin-persistedstate
  • 分包加载:微信小程序超过2MB时使用分包
// 分包配置示例 (pages.json)
{
  "subPackages": [
    {
      "root": "subPages",
      "pages": [
        {"path": "detail/detail", "style": {}}
      ]
    }
  ]
}

11. 总结

通过本文的案例,你掌握了UniApp跨平台开发的核心技能:

  • 项目创建与目录结构
  • 页面路由与TabBar配置
  • Pinia状态管理实战
  • 多端条件编译与适配
  • 网络请求封装
  • 打包发布全流程

UniApp让“一套代码,多端运行”成为现实。结合Vue3的Composition API和Pinia,你可以高效构建跨平台应用。现在就开始你的第一个UniApp项目吧!


本文原创,基于UniApp 3.12+和Vue 3.4+。所有代码均在HBuilderX 4.0中测试通过。

UniApp跨平台开发实战:从零构建多端任务管理应用
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 uniapp UniApp跨平台开发实战:从零构建多端任务管理应用 https://www.taomawang.com/web/uniapp/1749.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务