前言
Vue3 作为目前最流行的前端框架之一,带来了许多激动人心的新特性和改进。无论是性能优化、开发体验还是代码组织方式,Vue3 都有了质的飞跃。本博客将带你从零开始,首先学习如何搭建 Vue 3 项目,然后逐步深入其核心特性,并通过一个实战项目,让你掌握 Vue3 的开发技巧。
第一章:搭建你的第一个 Vue 3 项目
在开始学习 Vue3 的核心特性之前,我们首先需要一个可以运行的项目环境。目前,官方最推荐的方式是使用 Vite 来创建 Vue3 项目,因为它能提供极速的开发体验。
前提条件
确保你的电脑上已经安装了 Node.js (推荐版本为 18.0 或更高)。你可以在终端中运行以下命令来检查 Node.js 是否安装成功:
bash
node -v
# v18.17.1 # 看到类似这样的版本号即表示安装成功
步骤 1: 使用 Vite 创建项目
打开你的终端(在 Windows 上可以使用 CMD 或 PowerShell,在 macOS/Linux 上使用 Terminal),然后运行以下命令:
bash
# npm 方式 (推荐)
npm create vite@latest
# yarn 方式 (如果你习惯使用 yarn)
# yarn create vite
# pnpm 方式 (如果你习惯使用 pnpm)
# pnpm create vite
步骤 2: 配置项目信息
运行命令后,Vite 会引导你进行项目配置:
- 项目名称 : 输入你的项目名称,例如
vue3-todo-app。 - 选择框架 : 上下箭头键选择
Vue,然后按回车。 - 选择变体 : 再次使用上下箭头键,根据你的喜好选择
JavaScript或TypeScript。对于本教程,选择JavaScript即可。
配置过程如下所示:
bash
√ Project name: ... vue3-todo-app
√ Select a framework: >> Vue
√ Select a variant: >> JavaScript
Scaffolding project in /path/to/your/project/vue3-todo-app...
Done. Now run:
cd vue3-todo-app
npm install
npm run dev
步骤 3: 进入项目并安装依赖
根据终端的提示,执行以下命令:
bash
# 进入你刚刚创建的项目目录
cd vue3-todo-app
# 安装项目所需的依赖包
npm install
步骤 4: 启动开发服务器
依赖安装完成后,运行以下命令来启动本地开发服务器:
bash
npm run dev
启动成功后,你会在终端看到类似下面的信息,告诉你项目运行在哪个地址上:
bash
VITE v4.4.9 ready in 300 ms
➜ Local: http://127.0.0.1:5173/
➜ Network: use --host to expose
现在,打开你的浏览器,访问 http://127.0.0.1:5173/,你应该能看到 Vue3 的欢迎页面,这表示你的项目已经成功创建并运行起来了!
项目目录结构
让我们简单了解一下 Vite 创建的 Vue3 项目结构:
bash
vue3-todo-app/
├── node_modules/ # 项目依赖
├── public/ # 静态资源文件夹
├── src/ # 源代码文件夹 (我们主要在这里工作)
│ ├── assets/ # 图片、字体等资源
│ ├── components/ # Vue 组件文件夹
│ │ └── HelloWorld.vue # 一个示例组件
│ ├── App.vue # 根组件
│ ├── main.js # 入口文件,用于创建 Vue 应用
│ └── style.css # 全局样式文件
├── .gitignore # Git 忽略文件配置
├── index.html # 应用的 HTML 入口
├── package.json # 项目配置和依赖管理
└── vite.config.js # Vite 的配置文件
第二章:为什么选择 Vue3?
Vue3 相比 Vue2 有以下几个显著的优势:
- 更好的性能:Vue3 使用了 Proxy 代替 Object.defineProperty 实现响应式,性能更高,并且支持对数组、Map、Set 等数据结构的响应式处理。
- 更小的体积:通过 Tree-shaking 优化,Vue3 的核心库体积更小,只引入需要的功能。
- Composition API:提供了全新的 Composition API,让代码组织更加灵活,更适合大型项目。
- TypeScript 支持:Vue3 完全用 TypeScript 重写,提供了更好的类型推断和类型检查。
- 更好的扩展性:支持自定义渲染器,可以将 Vue 组件渲染到不同的平台。
第三章:Vue3 核心特性
1. Composition API
Composition API 是 Vue3 最重要的新特性之一。它允许我们将组件的逻辑按照功能进行组织,而不是按照选项(data、methods、computed 等)进行分割。
示例代码:
bash
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
// 响应式数据
const count = ref(0);
// 方法
const increment = () => {
count.value++;
};
// 生命周期钩子
onMounted(() => {
console.log('Component mounted');
});
</script>
2. 响应式系统
Vue3 的响应式系统基于 Proxy 实现,相比 Vue2 的 Object.defineProperty 有以下改进:
- 支持对数组索引和 length 属性的响应式处理
- 支持对 Map、Set、WeakMap、WeakSet 的响应式处理
- 不需要手动处理数组的变异方法(push、pop、shift、unshift 等)
示例代码:
javascript
import { reactive, watch } from 'vue';
const state = reactive({
name: 'Vue3',
age: 3,
hobbies: ['reading', 'coding']
});
// 监听响应式数据变化
watch(() => state.name, (newVal, oldVal) => {
console.log(`Name changed from ${oldVal} to ${newVal}`);
});
state.name = 'Vue'; // 触发 watch
state.hobbies.push('running'); // 触发 watch
3. 生命周期钩子
Vue3 的生命周期钩子相比 Vue2 有一些变化,并且可以在 Composition API 中使用。
常用生命周期钩子:
onBeforeMount:组件挂载前调用onMounted:组件挂载后调用onBeforeUpdate:组件更新前调用onUpdated:组件更新后调用onBeforeUnmount:组件卸载前调用onUnmounted:组件卸载后调用
示例代码:
bash
<script setup>
import { onMounted, onUnmounted } from 'vue';
onMounted(() => {
console.log('Component mounted');
});
onUnmounted(() => {
console.log('Component unmounted');
});
</script>
4. 模板语法改进
Vue3 的模板语法相比 Vue2 有一些改进,包括:
- 支持多个根节点
- 支持片段(Fragment)
- 支持 Teleport 组件,可以将组件的内容渲染到 DOM 树的其他位置
- 支持 Suspense 组件,可以处理异步组件的加载状态
示例代码:
bash
<template>
<div>Root node 1</div>
<div>Root node 2</div>
</template>
第四章:实战项目:Todo List
接下来,我们将通过一个简单的 Todo List 项目,来实践 Vue3 的核心特性。
编写 Todo List 组件
在 src/components 目录下创建 TodoList.vue 文件,并填入以下代码:
bash
<template>
<div class="todo-list">
<h1>Todo List</h1>
<!-- 添加 todo -->
<div class="add-todo">
<input
type="text"
v-model="newTodo"
placeholder="Add a new todo..."
@keyup.enter="addTodo"
>
<button @click="addTodo">Add</button>
</div>
<!-- 过滤 todo -->
<div class="filters">
<button
:class="{ active: filter === 'all' }"
@click="setFilter('all')"
>
All
</button>
<button
:class="{ active: filter === 'active' }"
@click="setFilter('active')"
>
Active
</button>
<button
:class="{ active: filter === 'completed' }"
@click="setFilter('completed')"
>
Completed
</button>
</div>
<!-- 显示 todo 列表 -->
<ul class="todo-items">
<li
v-for="todo in filteredTodos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
>
<span>{{ todo.text }}</span>
<button @click="deleteTodo(todo.id)">×</button>
</li>
</ul>
<!-- 清除已完成 todo -->
<div class="clear-completed">
<button
@click="clearCompleted"
:disabled="!hasCompletedTodos"
>
Clear completed
</button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
// 响应式数据
const todos = ref([
{ id: 1, text: 'Learn Vue3', completed: false },
{ id: 2, text: 'Build a todo app', completed: false }
]);
const newTodo = ref('');
const filter = ref('all');
// 计算属性:过滤 todo
const filteredTodos = computed(() => {
switch (filter.value) {
case 'active':
return todos.value.filter(todo => !todo.completed);
case 'completed':
return todos.value.filter(todo => todo.completed);
default:
return todos.value;
}
});
// 计算属性:是否有已完成的 todo
const hasCompletedTodos = computed(() => {
return todos.value.some(todo => todo.completed);
});
// 方法:添加 todo
const addTodo = () => {
if (newTodo.value.trim() !== '') {
todos.value.push({
id: Date.now(),
text: newTodo.value.trim(),
completed: false
});
newTodo.value = '';
}
};
// 方法:删除 todo
const deleteTodo = (id) => {
todos.value = todos.value.filter(todo => todo.id !== id);
};
// 方法:设置过滤条件
const setFilter = (value) => {
filter.value = value;
};
// 方法:清除已完成 todo
const clearCompleted = () => {
todos.value = todos.value.filter(todo => !todo.completed);
};
</script>
<style scoped>
/* 样式部分保持不变 */
.todo-list {
max-width: 600px;
margin: 2rem auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
/* ... (省略其余样式以保持简洁,实际使用时请保留完整样式) ... */
</style>
在 App.vue 中使用 TodoList 组件
修改 src/App.vue 文件,使其内容如下:
bash
<template>
<div id="app">
<TodoList />
</div>
</template>
<script setup>
import TodoList from './components/TodoList.vue';
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
第五章:Vue3 生态系统
Vue3 拥有丰富的生态系统,包括:
- Vue Router:官方路由库,用于构建单页应用。
- Pinia:官方状态管理库,替代了 Vuex。
- Vue Test Utils:官方测试工具库,用于测试 Vue 组件。
- Vue Devtools:浏览器开发者工具,用于调试 Vue 应用。
使用 Pinia 进行状态管理
Pinia 是 Vue3 推荐的状态管理库,相比 Vuex 有以下改进:
- 更简单的 API
- 更好的 TypeScript 支持
- 不需要嵌套模块
- 支持组合式 API
示例代码:
-
安装 Pinia:
bashnpm install pinia -
在
main.js中引入 Pinia:javascriptimport { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' createApp(App) .use(createPinia()) .mount('#app') -
创建 store:
javascript// src/stores/todo.js import { defineStore } from 'pinia' export const useTodoStore = defineStore('todo', { state: () => ({ todos: [ { id: 1, text: 'Learn Vue3', completed: false }, { id: 2, text: 'Build a todo app', completed: false } ] }), getters: { activeTodos: (state) => state.todos.filter(todo => !todo.completed), completedTodos: (state) => state.todos.filter(todo => todo.completed) }, actions: { addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }) }, deleteTodo(id) { this.todos = this.todos.filter(todo => todo.id !== id) }, toggleTodo(id) { const todo = this.todos.find(todo => todo.id === id) if (todo) { todo.completed = !todo.completed } } } }) -
在组件中使用 store:
bash<script setup> import { useTodoStore } from '@/stores/todo' const todoStore = useTodoStore() // 访问 state console.log(todoStore.todos) // 访问 getters console.log(todoStore.activeTodos) // 调用 actions todoStore.addTodo('Learn Pinia') </script>