1. Vue3 极简入门:从零到精通的完整指南
作为2023年最主流的前端框架之一,Vue3凭借其组合式API和更好的性能表现,已经成为企业级应用开发的首选。我在实际项目中从Vue2迁移到Vue3的过程中,发现很多开发者虽然对Vue3感兴趣,但面对全新的生态系统往往不知从何入手。本文将带你从零开始,用最直接的方式构建一个完整的Vue3项目,涵盖项目搭建、组件开发到状态管理的全流程。
这个教程特别适合:
- 刚接触Vue3需要快速上手的开发者
- 从Vue2迁移到Vue3遇到困惑的工程师
- 想系统学习现代前端工程化实践的入门者
我们将使用Vite作为构建工具(比传统webpack快10倍以上),配合Pinia实现状态管理,最终完成一个具备完整功能的前端应用骨架。所有代码都经过实际项目验证,你可以直接应用到自己的开发中。
2. 环境准备与项目初始化
2.1 开发环境配置
在开始之前,请确保你的系统已经安装:
- Node.js 16.x或更高版本(推荐使用18.x LTS)
- npm 8.x或yarn 1.22+(本文使用npm演示)
- VS Code或其他现代IDE
提示:可以通过命令
node -v和npm -v检查当前版本。如果版本过低,建议使用nvm(Mac/Linux)或nvm-windows(Windows)进行版本管理。
2.2 使用Vite创建项目
Vite是Vue作者尤雨溪开发的下一代前端工具,启动速度极快。我们用它来初始化项目:
npm create vite@latest vue3-demo --template vue cd vue3-demo npm install这个命令会:
- 创建一个名为vue3-demo的文件夹
- 使用vue模板初始化项目结构
- 自动安装基础依赖
项目结构说明:
vue3-demo ├── public/ # 静态资源 ├── src/ │ ├── assets/ # 图片等资源 │ ├── components/ # 公共组件 │ ├── App.vue # 根组件 │ └── main.js # 应用入口 ├── vite.config.js # Vite配置 └── package.json # 项目配置2.3 启动开发服务器
执行以下命令启动开发服务器:
npm run dev正常情况下,你会看到控制台输出:
VITE v4.1.0 ready in 320 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose访问http://localhost:5173/,你应该能看到Vue的欢迎页面。这个热重载的开发服务器会在你修改代码时立即更新页面,无需手动刷新。
3. Vue3组件开发实战
3.1 理解组合式API
Vue3最大的变化就是引入了组合式API(Composition API),它解决了Options API在复杂组件中代码组织混乱的问题。我们通过一个计数器组件来对比两种写法:
<!-- Options API写法 (Vue2风格) --> <script> export default { data() { return { count: 0 } }, methods: { increment() { this.count++ } } } </script> <!-- Composition API写法 (Vue3推荐) --> <script setup> import { ref } from 'vue' const count = ref(0) const increment = () => count.value++ </script>组合式API的特点:
- 使用
<script setup>语法糖,更简洁 - 通过
ref和reactive创建响应式数据 - 逻辑可以提取到单独的函数中,便于复用
- 类型推断更友好,适合TypeScript
3.2 构建第一个业务组件
让我们创建一个真实的Todo组件,展示Vue3的核心特性:
<!-- src/components/TodoList.vue --> <template> <div class="todo-container"> <h2>我的待办事项</h2> <div class="input-group"> <input v-model="newTodo" @keyup.enter="addTodo" placeholder="输入任务后按回车" /> <button @click="addTodo">添加</button> </div> <ul> <li v-for="(todo, index) in todos" :key="todo.id"> <input type="checkbox" v-model="todo.completed" /> <span :class="{ completed: todo.completed }">{{ todo.text }}</span> <button @click="removeTodo(index)">×</button> </li> </ul> <p>剩余任务: {{ remaining }} / {{ todos.length }}</p> </div> </template> <script setup> import { ref, computed } from 'vue' const newTodo = ref('') const todos = ref([ { id: 1, text: '学习Vue3', completed: false }, { id: 2, text: '掌握Pinia', completed: false } ]) const remaining = computed(() => { return todos.value.filter(todo => !todo.completed).length }) function addTodo() { if (newTodo.value.trim()) { todos.value.push({ id: Date.now(), text: newTodo.value, completed: false }) newTodo.value = '' } } function removeTodo(index) { todos.value.splice(index, 1) } </script> <style scoped> .todo-container { max-width: 500px; margin: 0 auto; } .completed { text-decoration: line-through; color: #888; } </style>这个组件展示了Vue3的几个关键特性:
ref创建响应式数据computed创建计算属性v-model实现双向绑定v-for渲染列表- 方法直接定义在setup中
scoped样式确保组件样式隔离
3.3 组件间通信
在真实项目中,组件之间需要共享数据和事件。Vue3提供了多种通信方式:
父子组件通信
<!-- 父组件 --> <template> <ChildComponent :title="parentTitle" @update-title="handleUpdate" /> </template> <script setup> import ChildComponent from './ChildComponent.vue' import { ref } from 'vue' const parentTitle = ref('初始标题') const handleUpdate = (newTitle) => { parentTitle.value = newTitle } </script> <!-- 子组件 ChildComponent.vue --> <template> <div> <h3>{{ title }}</h3> <button @click="$emit('update-title', '新标题')"> 修改标题 </button> </div> </template> <script setup> defineProps(['title']) defineEmits(['update-title']) </script>使用provide/inject跨层级通信
<!-- 祖先组件 --> <script setup> import { provide, ref } from 'vue' const theme = ref('dark') provide('theme', theme) </script> <!-- 后代组件 --> <script setup> import { inject } from 'vue' const theme = inject('theme') </script>4. Pinia状态管理实战
4.1 为什么需要Pinia?
在大型应用中,组件之间共享状态会变得复杂。Pinia是Vue官方推荐的状态管理库,相比Vuex有以下优势:
- 更简单的API,去掉mutations
- 完整的TypeScript支持
- 组合式API风格
- 模块化设计,自动代码分割
- 体积仅1KB左右
4.2 安装与配置Pinia
首先安装Pinia:
npm install pinia然后在main.js中初始化:
import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const app = createApp(App) app.use(createPinia()) app.mount('#app')4.3 创建第一个Store
我们创建一个管理用户状态的store:
// src/stores/user.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ name: '访客', isLogin: false, token: null }), actions: { login(username, password) { // 模拟登录API调用 return new Promise((resolve) => { setTimeout(() => { this.name = username this.isLogin = true this.token = 'mock-token' resolve(true) }, 500) }) }, logout() { this.name = '访客' this.isLogin = false this.token = null } }, getters: { welcomeMessage: (state) => { return state.isLogin ? `欢迎回来, ${state.name}!` : '请先登录' } } })4.4 在组件中使用Store
<!-- src/components/UserProfile.vue --> <template> <div class="user-profile"> <p>{{ userStore.welcomeMessage }}</p> <button v-if="!userStore.isLogin" @click="handleLogin" > 登录 </button> <button v-else @click="userStore.logout()" > 退出 </button> </div> </template> <script setup> import { useUserStore } from '../stores/user' const userStore = useUserStore() const handleLogin = async () => { await userStore.login('张三', '123456') console.log('登录成功:', userStore.name) } </script>4.5 Pinia持久化存储
为了防止页面刷新后状态丢失,我们可以使用pinia-plugin-persistedstate插件:
npm install pinia-plugin-persistedstate配置修改:
// main.js import { createApp } from 'vue' import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import App from './App.vue' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) createApp(App).use(pinia).mount('#app')然后在store中启用持久化:
export const useUserStore = defineStore('user', { // ...其他配置 persist: { enabled: true, strategies: [ { key: 'user', storage: localStorage } ] } })5. 项目优化与部署
5.1 配置Vite环境变量
创建.env文件管理环境变量:
# .env.development VITE_API_BASE=http://localhost:3000/api VITE_DEBUG=true # .env.production VITE_API_BASE=https://api.example.com在代码中访问:
const apiBase = import.meta.env.VITE_API_BASE5.2 按需加载组件
对于大型组件库,推荐按需导入:
// 错误方式:全量导入 import ElementPlus from 'element-plus' // 正确方式:按需导入 import { ElButton, ElInput } from 'element-plus'5.3 生产环境构建
执行构建命令:
npm run build构建完成后,dist目录就是可以部署的静态文件。可以使用以下命令预览生产版本:
npm run preview5.4 性能优化建议
- 使用
<script setup>语法减少代码量 - 大型列表使用
v-for时添加:key - 复杂计算属性使用
computed缓存结果 - 使用
v-memo优化静态内容 - 第三方库按需引入
- 使用异步组件延迟加载非关键组件
6. 常见问题与解决方案
6.1 Vite启动时报错
问题:Failed to resolve import "vue"
原因:依赖未正确安装
解决:
rm -rf node_modules package-lock.json npm install6.2 Pinia状态不更新
问题:修改state后视图不更新
原因:直接解构会失去响应性
错误示范:
const { name } = useUserStore() // 失去响应性正确做法:
const userStore = useUserStore() // 方式1:直接使用store userStore.name // 方式2:使用storeToRefs保持响应性 import { storeToRefs } from 'pinia' const { name } = storeToRefs(userStore)6.3 组件样式不生效
问题:scoped样式不应用
原因:深度选择器需要特殊语法
解决方案:
/* 常规选择器 */ .parent /deep/ .child { color: red; } /* 或使用新语法 */ .parent :deep(.child) { color: red; }6.4 开发服务器代理配置
在vite.config.js中配置API代理:
export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } } })7. 进阶技巧与最佳实践
7.1 自定义Hooks复用逻辑
将可复用的逻辑提取到自定义Hook中:
// src/hooks/useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue' export function useMousePosition() { const x = ref(0) const y = ref(0) const update = (e) => { x.value = e.pageX y.value = e.pageY } onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) return { x, y } }在组件中使用:
<script setup> import { useMousePosition } from '../hooks/useMousePosition' const { x, y } = useMousePosition() </script> <template> <p>鼠标位置: {{ x }}, {{ y }}</p> </template>7.2 使用Teleport实现模态框
<!-- src/components/Modal.vue --> <template> <teleport to="body"> <div class="modal-mask" v-if="show"> <div class="modal-container"> <slot></slot> <button @click="$emit('close')">关闭</button> </div> </div> </teleport> </template> <script setup> defineProps(['show']) defineEmits(['close']) </script> <style> .modal-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; } .modal-container { background: white; padding: 20px; border-radius: 8px; } </style>7.3 性能监控与错误捕获
在main.js中添加全局错误处理:
app.config.errorHandler = (err, vm, info) => { console.error('全局错误:', err) // 可以上报到错误监控系统 }使用性能API监控组件:
import { onMounted, onUnmounted } from 'vue' export function usePerformance() { onMounted(() => { const start = performance.now() onUnmounted(() => { const duration = performance.now() - start console.log(`组件存活时间: ${duration.toFixed(2)}ms`) }) }) }在实际项目中,我发现组合式API最大的优势是逻辑复用能力。通过自定义Hook,可以把业务逻辑像积木一样组合起来。比如用户认证、数据获取、表单验证等逻辑都可以抽象成独立的Hook,然后在多个组件中复用。这种开发模式让代码更清晰,也更容易维护。