一、项目初始化与基础架构
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中启用响应式语法糖:javascriptimport { defineConfig } from 'vite' import uni from '@dcloudio/vite-plugin-uni' export default defineConfig({ plugins: [ uni({ vueOptions: { reactivityTransform: true // 开启响应式语法糖 } }) ] })1.2 项目结构规划
javascriptsrc/ ├── 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" } } }