基于UniApp与Vue3语法糖的跨平台待办事项应用开发实践

一、项目初始化与基础架构

1.1 环境配置与项目创建

bash 复制代码
# 使用Vite创建UniApp项目(需安装@dcloudio/uni-cli)
npm create vue@latest my-todo-app --template uni-app-vue3
cd my-todo-app
npm install

关键配置说明:

  • vite.config.js中启用响应式语法糖:

    javascript 复制代码
    import { defineConfig } from 'vite'
    import uni from '@dcloudio/vite-plugin-uni'
    
    export default defineConfig({
      plugins: [
        uni({
          vueOptions: {
            reactivityTransform: true // 开启响应式语法糖
          }
        })
      ]
    })

    1.2 项目结构规划

    javascript 复制代码
    src/
    ├── components/       # 公共组件
    │   ├── TodoItem.vue # 单个待办项组件
    │   └── AddTodo.vue  # 添加待办组件
    ├── pages/            # 页面目录
    │   └── index/        # 首页
    │       ├── index.vue # 页面容器
    │       └── store.ts  # 状态管理
    ├── App.vue           # 应用入口
    └── main.js           # 应用配置

    二、核心组件开发:Vue3语法糖实战

    2.1 待办项组件(TodoItem.vue)

    html 复制代码
    <template>
      <view class="todo-item" :class="{ completed: todo.completed }">
        <!-- 使用v-model双向绑定复选框 -->
        <checkbox 
          :value="todo.completed" 
          @change="toggleComplete"
        />
        <text class="todo-text">{{ todo.text }}</text>
        <button @click="emitDelete" class="delete-btn">删除</button>
      </view>
    </template>
    
    <script setup>
    // 定义组件props(带类型校验)
    const props = defineProps({
      todo: {
        type: Object,
        required: true,
        validator: (value) => {
          return 'text' in value && 'completed' in value
        }
      }
    })
    
    // 定义组件事件
    const emit = defineEmits(['toggle', 'delete'])
    
    // 切换完成状态
    const toggleComplete = () => {
      emit('toggle', props.todo.id)
    }
    
    // 删除待办项
    const emitDelete = () => {
      emit('delete', props.todo.id)
    }
    </script>
    
    <style scoped>
    .todo-item {
      display: flex;
      align-items: center;
      padding: 12px;
      margin: 8px 0;
      background-color: #f8f8f8;
      border-radius: 8px;
    }
    
    .completed .todo-text {
      text-decoration: line-through;
      color: #999;
    }
    
    .delete-btn {
      margin-left: auto;
      background-color: #ff5252;
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
    }
    </style>

    2.2 添加待办组件(AddTodo.vue)

    html 复制代码
    <template>
      <view class="add-todo">
        <input 
          v-model="inputValue" 
          placeholder="输入待办事项" 
          @keyup.enter="handleSubmit"
          class="todo-input"
        />
        <button @click="handleSubmit" class="submit-btn">添加</button>
      </view>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    // 响应式输入值
    const inputValue = ref('')
    
    // 定义组件事件
    const emit = defineEmits(['add'])
    
    // 提交表单
    const handleSubmit = () => {
      if (inputValue.value.trim()) {
        emit('add', inputValue.value.trim())
        inputValue.value = '' // 清空输入框
      }
    }
    </script>
    
    <style scoped>
    .add-todo {
      display: flex;
      gap: 8px;
      margin-bottom: 20px;
    }
    
    .todo-input {
      flex: 1;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    
    .submit-btn {
      padding: 10px 20px;
      background-color: #007aff;
      color: white;
      border-radius: 4px;
    }
    </style>

    三、状态管理与业务逻辑

    3.1 使用Pinia进行状态管理

    TypeScript 复制代码
    // src/pages/index/store.ts
    import { defineStore } from 'pinia'
    import { ref } from 'vue'
    
    interface TodoItem {
      id: string
      text: string
      completed: boolean
    }
    
    export const useTodoStore = defineStore('todo', () => {
      // 响应式状态
      const todos = ref<TodoItem[]>([])
      
      // 添加待办
      const addTodo = (text: string) => {
        todos.value.push({
          id: Date.now().toString(),
          text,
          completed: false
        })
      }
      
      // 切换完成状态
      const toggleTodo = (id: string) => {
        const todo = todos.value.find(t => t.id === id)
        if (todo) todo.completed = !todo.completed
      }
      
      // 删除待办
      const deleteTodo = (id: string) => {
        todos.value = todos.value.filter(t => t.id !== id)
      }
      
      return { todos, addTodo, toggleTodo, deleteTodo }
    })

    3.2 页面集成与逻辑处理

    html 复制代码
    <template>
      <view class="container">
        <h1>待办事项</h1>
        <AddTodo @add="handleAddTodo" />
        <view v-if="filteredTodos.length === 0" class="empty-tip">
          暂无待办事项
        </view>
        <view v-else>
          <TodoItem 
            v-for="todo in filteredTodos" 
            :key="todo.id"
            :todo="todo"
            @toggle="handleToggleTodo"
            @delete="handleDeleteTodo"
          />
        </view>
      </view>
    </template>
    
    <script setup>
    import { computed } from 'vue'
    import { useTodoStore } from './store'
    import AddTodo from '@/components/AddTodo.vue'
    import TodoItem from '@/components/TodoItem.vue'
    
    // 获取store实例
    const todoStore = useTodoStore()
    
    // 计算属性:过滤未完成事项
    const filteredTodos = computed(() => {
      return todoStore.todos.filter(todo => !todo.completed)
    })
    
    // 事件处理函数
    const handleAddTodo = (text) => {
      todoStore.addTodo(text)
    }
    
    const handleToggleTodo = (id) => {
      todoStore.toggleTodo(id)
    }
    
    const handleDeleteTodo = (id) => {
      todoStore.deleteTodo(id)
    }
    </script>
    
    <style scoped>
    .container {
      padding: 20px;
      max-width: 600px;
      margin: 0 auto;
    }
    
    .empty-tip {
      text-align: center;
      color: #999;
      padding: 20px;
    }
    </style>

    四、跨平台适配与优化

    4.1 平台差异处理

    javascript 复制代码
    // 适配不同平台样式
    const platformStyle = computed(() => {
      const systemInfo = uni.getSystemInfoSync()
      return {
        padding: systemInfo.platform === 'ios' ? '20px' : '10px',
        fontSize: systemInfo.windowWidth < 400 ? '14px' : '16px'
      }
    })

    4.2 性能优化策略

  • 虚拟滚动 :长列表使用scroll-view的scroll-y属性配合uni-list组件

  • 防抖处理 :输入框使用lodash的debounce优化搜索

  • 数据持久化 :通过uni.setStorageSync实现本地存储

    TypeScript 复制代码
    // 持久化存储
    const saveTodos = () => {
      uni.setStorageSync('todos', JSON.stringify(todoStore.todos))
    }
    
    // 初始化加载
    const loadTodos = () => {
      const saved = uni.getStorageSync('todos')
      if (saved) {
        todoStore.todos = JSON.parse(saved)
      }
    }

    4.3 多端发布配置

    TypeScript 复制代码
    // manifest.json配置示例
    {
      "mp-weixin": {
        "appid": "your-appid",
        "setting": {
          "urlCheck": false
        }
      },
      "h5": {
        "title": "待办事项",
        "router": {
          "mode": "hash"
        }
      }
    }
相关推荐
小皮虾2 小时前
小程序全局状态还能这么简单?只需初始化一次,页面自动同步
前端·javascript·微信小程序
daols882 小时前
vue 甘特图组件 vxe-gantt 修改滚动条样式-让滚动条自动隐藏
javascript·vue.js·甘特图
今年下半年3 小时前
【前端】Vue2 项目 ECharts 统计图表组件封装与使用实践(附组件源码)
前端·javascript·echarts
冒险的豆子3 小时前
JS 原型链 通俗易懂 解释说明(类比 关系)
开发语言·javascript·原型模式
西索斯coding3 小时前
在 中接入 教程:p配置、m填写、扩展思考参数一篇就够(2)
java·前端·javascript·ai
特立独行的猫A3 小时前
仓颉UI开发实战:我用仓颉写了个桌面版力扣:从判题机到 100 道题
vue.js
用户1733598075373 小时前
纯前端 PDF 双面打印避坑指南:翻页顺序错了整个白印?
前端·javascript·vue.js
SensorFlow3 小时前
从神策 SDK 迁移到 SensorFlow:采集层平滑迁移实战
javascript
2501_915921434 小时前
iOS应用从开发到上架App Store的完整步骤详解
android·ios·小程序·https·uni-app·iphone·webview
浮生望4 小时前
从作用域链到 React 陈旧闭包:为什么函数式更新能拿到最新 state
javascript