全栈学习 —— 前端(三)Vue框架
目录
一、Vue 框架核心思想与优势
1. 核心设计理念
2. 与其他框架的对比
3. 为什么选择 Vue?
二、Vue 基础环境搭建
1. 快速入门:直接引入 Vue
2. 工程化开发:Vue CLI
(1)安装 Vue CLI
(2)创建项目
(3)运行项目
三、Vue 核心概念与基础语法
1. Vue 实例与数据绑定
2. 指令系统
(1)v-bind:绑定属性
(2)v-model:双向数据绑定
(3)v-for:列表渲染
(4)v-if与v-show:条件渲染
(5)v-on:事件绑定
3. 计算属性与侦听器
(1)计算属性(computed)
(2)侦听器(watch)
四、Vue 组件化开发
1. 组件的定义与注册
(1)全局组件
(2)局部组件
2. 组件通信
(1)父组件向子组件传值(props)
(2)子组件向父组件传值($emit)
3. 插槽(Slot)
五、Vue 底层原理
1. 响应式系统原理
2. 虚拟 DOM 与 Diff 算法
六、Vue Router 路由管理
1. 安装与配置
2. 路由使用
3. 动态路由与参数传递
七、实战项目:待办事项应用(Todo App)
1. 项目功能设计
2. 项目结构
3. 核心代码实现
(1)状态管理(stores/todoStore.js)
(2)输入组件(components/TodoInput.vue)
(3)列表项组件(components/TodoItem.vue)
(4)列表组件(components/TodoList.vue)
(5)筛选组件(components/TodoFilter.vue)
(6)主组件(App.vue)
4. 项目运行与测试
Vue.js(简称 Vue)是一套用于构建用户界面的渐进式 JavaScript 框架。自 2014 年由尤雨溪发布以来,凭借其简洁的 API、优秀的性能和灵活的集成能力,成为前端开发的主流框架之一。本文将从基础概念到实战开发,全面解析 Vue 的核心原理与应用。
一、Vue 框架核心思想与优势
1. 核心设计理念
Vue 的核心思想可以概括为两点:数据驱动和组件化。
数据驱动(Data-Driven):
传统 DOM 操作需要手动将数据同步到视图,而 Vue 通过数据绑定机制,使视图自动响应数据变化。当数据更新时,视图会自动重新渲染,无需开发者编写 DOM 操作代码。组件化(Component-Based):
将页面拆分为独立、可复用的组件(Component),每个组件包含自身的结构(HTML)、样式(CSS)和逻辑(JavaScript)。组件化让代码更易维护、复用性更高。
2. 与其他框架的对比
3. 为什么选择 Vue?
- 易学易用:HTML、CSS、JavaScript 基础开发者可快速上手
- 性能出色:采用虚拟 DOM 和响应式系统,渲染效率高
- 灵活性强:可作为库引入现有项目,也可构建完整单页应用
- 完善生态:配套工具(Vue CLI、Vue Router、Pinia)和社区支持丰富
二、Vue 基础环境搭建
1. 快速入门:直接引入 Vue
适合小型项目或学习测试:
{{ message }} // 创建Vue实例 const { createApp } = Vue createApp({ data() { return { message: \'Hello Vue!\' } } }).mount(\'#app\')
2. 工程化开发:Vue CLI
适合中大型项目,提供完整的开发环境配置:
(1)安装 Vue CLI
# 全局安装Vue
CLI npm install -g @vue/cli
# 检查版本
vue --version
(2)创建项目
# 创建项目vue create my-vue-app# 选择预设(推荐手动选择特性)# 选择Vue 3、Babel、Router、Vuex等必要组件
(3)运行项目
cd my-vue-appnpm run serve
访问http://localhost:8080
即可看到默认页面。
三、Vue 核心概念与基础语法
1. Vue 实例与数据绑定
Vue 应用的入口是通过createApp
创建的应用实例,所有功能都基于这个实例展开。
{{ title }}
{{ user.name }} - {{ user.age }}岁
{{ isActive ? \'在线\' : \'离线\' }}
{{ message.split(\'\').reverse().join(\'\') }}
const app = Vue.createApp({ // 数据对象,返回应用所需的响应式数据 data() { return { title: \'Vue基础示例\', user: { name: \'张三\', age: 25 }, isActive: true, message: \'Hello\' } } }) // 将应用挂载到DOM元素上 app.mount(\'#app\')
{{ }}
:插值表达式,用于将数据渲染到视图中- 支持 JavaScript 表达式(如三元运算、字符串方法)
- 数据是响应式的:当数据变化时,视图会自动更新
2. 指令系统
Vue 提供了一系列特殊属性(指令),用于在模板中实现逻辑控制。
(1)v-bind
:绑定属性
![]()
![]()
data() { return { imageUrl: \'logo.png\', imageAlt: \'网站logo\' }}
(2)v-model
:双向数据绑定
主要用于表单元素,实现数据与视图的双向同步:
你输入的是:{{ username }}
请选择 北京 上海 Vue.createApp({ data() { return { username: \'\', content: \'\', selectedCity: \'\' } } }).mount(\'#app\')
(3)v-for
:列表渲染
用于循环渲染数组或对象:
- {{ index + 1 }}. {{ item }}
姓名 年龄 {{ user.name }} {{ user.age }}
data() { return { fruits: [\'苹果\', \'香蕉\', \'橙子\'], users: [ { id: 1, name: \'张三\', age: 20 }, { id: 2, name: \'李四\', age: 22 } ] }}
:key
:提高渲染性能,确保每个节点的唯一性
(4)v-if
与v-show
:条件渲染
欢迎回来,{{ username }}!请先登录= 90\">优秀= 60\">及格不及格这是可以显示/隐藏的内容
data() { return { isLogin: true, username: \'张三\', score: 85, isActive: false }}
- 区别:
v-if
有更高的切换开销,v-show
有更高的初始渲染开销- 频繁切换用
v-show
,条件很少改变用v-if
(5)v-on
:事件绑定
data() { return { count: 0 }},methods: { handleClick() { this.count++ }, sayHello(name) { alert(`Hello ${name}!`) }, handleSubmit() { console.log(\'提交表单\') }}
- 事件处理函数定义在
methods
选项中this
指向当前 Vue 实例- 事件修饰符:
.stop
(阻止冒泡)、.prevent
(阻止默认行为)、.enter
(按键修饰符)等
3. 计算属性与侦听器
(1)计算属性(computed)
用于处理复杂逻辑计算,具有缓存特性:
原始价格:{{ price }}
折扣价:{{ discountedPrice }}
总价:{{ totalPrice }}
Vue.createApp({ data() { return { price: 100, quantity: 3, discount: 0.8 } }, computed: { // 计算折扣价 discountedPrice() { return this.price * this.discount }, // 计算总价 totalPrice() { return this.discountedPrice * this.quantity } } }).mount(\'#app\')
- 计算属性会基于依赖的数据自动更新
- 相比方法(methods),计算属性有缓存,多次访问不会重复计算
(2)侦听器(watch)
用于监听数据变化并执行副作用操作:
{{ message }}
Vue.createApp({ data() { return { username: \'\', message: \'\' } }, watch: { // 监听username变化 username(newVal, oldVal) { if (newVal.length < 3) { this.message = \'用户名长度不能少于3个字符\' } else { this.message = \'用户名可用\' } } } }).mount(\'#app\')
- 适合处理异步操作或复杂逻辑(如数据请求、定时器等)
四、Vue 组件化开发
组件是 Vue 的核心概念,将页面拆分为独立可复用的模块。
1. 组件的定义与注册
(1)全局组件
在整个应用中都可使用:
// 定义全局组件const app = Vue.createApp({})app.component(\'my-component\', { template: ` {{ title }}
{{ content }}
`, data() { return { title: \'我是全局组件\', content: \'这是全局组件的内容\' } }})app.mount(\'#app\')
使用组件:
(2)局部组件
仅在注册它的父组件中可用:
// 定义局部组件const MyComponent = { template: ` {{ message }}
`, data() { return { message: \'我是局部组件\' } }}// 在父组件中注册const app = Vue.createApp({ components: { \'my-component\': MyComponent }})app.mount(\'#app\')
2. 组件通信
(1)父组件向子组件传值(props)
javascript
// 子组件const ChildComponent = { // 声明接收的props props: [\'title\', \'userInfo\', \'isActive\'], template: ` {{ title }}
{{ userInfo.name }} - {{ userInfo.age }}岁
激活状态
`}// 父组件const app = Vue.createApp({ components: { \'child-component\': ChildComponent }, data() { return { parentTitle: \'来自父组件的标题\', user: { name: \'张三\', age: 25 }, active: true } }})
使用时传递数据:
- props 命名:在组件中使用驼峰式(camelCase),在模板中使用短横线式(kebab-case)
- 可以指定 props 类型和验证:
props: { // 基础类型检查 age: Number, // 多个可能的类型 id: [String, Number], // 必传且是字符串 name: { type: String, required: true }, // 带默认值的数字 count: { type: Number, default: 0 }}
(2)子组件向父组件传值($emit)
// 子组件const ChildComponent = { template: ` `, methods: { handleClick() { // 触发自定义事件,并传递数据 this.$emit(\'send-message\', \'来自子组件的消息\', 123) } }}// 父组件const app = Vue.createApp({ components: { \'child-component\': ChildComponent }, methods: { // 接收子组件传递的消息 receiveMessage(msg, num) { console.log(\'收到消息:\', msg, num) } }})
父组件监听事件:
3. 插槽(Slot)
用于组件内容分发,使组件更灵活:
// 子组件(MyButton)const MyButton = { template: ` `}// 父组件使用const app = Vue.createApp({ components: { \'my-button\': MyButton }})
使用带插槽的组件:
点击我 提交
具名插槽(多个插槽区分):
// 子组件const Card = { template: ` `}
使用具名插槽:
卡片标题
这是卡片内容...
五、Vue 底层原理
1. 响应式系统原理
Vue 的响应式系统基于数据劫持和依赖收集实现:
初始化阶段:
- Vue 会遍历 data 中的所有属性,使用
Object.defineProperty
(Vue 2)或Proxy
(Vue 3)进行劫持- 为每个属性创建对应的
Dep
(依赖管理器),用于收集依赖依赖收集:
- 当组件渲染时,会触发属性的 getter 方法
- getter 方法会将当前组件的
Watcher
(观察者)添加到Dep
中数据更新:
- 当属性被修改时,会触发 setter 方法
- setter 方法会通知
Dep
中的所有Watcher
Watcher
触发组件重新渲染
Vue 3 使用 Proxy 替代 Object.defineProperty 的优势:
- 支持监听数组变化
- 支持监听新增 / 删除属性
- 性能更好,劫持整个对象而非单个属性
2. 虚拟 DOM 与 Diff 算法
Vue 通过虚拟 DOM 提高渲染性能:
虚拟 DOM:
- 用 JavaScript 对象模拟真实 DOM 结构
- 例如:
{ tag: \'div\', props: { id: \'app\' }, children: [] }
Diff 算法:
- 当数据变化时,Vue 会生成新的虚拟 DOM
- 通过 Diff 算法对比新旧虚拟 DOM 的差异
- 只更新差异部分对应的真实 DOM,减少 DOM 操作
key 的作用:
- 在列表渲染中,key 帮助 Vue 识别元素的唯一性
- 提高 Diff 算法的效率,避免不必要的元素重绘
六、Vue Router 路由管理
Vue Router 是 Vue 官方的路由管理器,用于构建单页应用(SPA)。
1. 安装与配置
# 安装Vue Router(Vue 3对应版本)npm install vue-router@4
创建路由配置(router/index.js
):
import { createRouter, createWebHistory } from \'vue-router\'import Home from \'../views/Home.vue\'import About from \'../views/About.vue\'const routes = [ { path: \'/\', name: \'Home\', component: Home }, { path: \'/about\', name: \'About\', component: About }]const router = createRouter({ history: createWebHistory(), // HTML5 history模式 routes})export default router
在 main.js 中引入:
import { createApp } from \'vue\'import App from \'./App.vue\'import router from \'./router\'createApp(App) .use(router) .mount(\'#app\')
2. 路由使用
在 App.vue 中添加路由出口和导航:
3. 动态路由与参数传递
// 路由配置{ path: \'/user/:id\', name: \'User\', component: User}
在组件中获取参数:
// User.vueexport default { mounted() { // 通过$route获取路由参数 console.log(this.$route.params.id) }}
编程式导航:
// 导航到用户页面this.$router.push({ name: \'User\', params: { id: 123 }})// 后退this.$router.go(-1)
七、实战项目:待办事项应用(Todo App)
1. 项目功能设计
- 添加待办事项
- 标记待办事项为已完成
- 删除待办事项
- 筛选待办事项(全部 / 已完成 / 未完成)
- 统计待办事项数量
2. 项目结构
todo-app/├── public/├── src/│ ├── components/│ │ ├── TodoInput.vue // 输入组件│ │ ├── TodoList.vue // 列表组件│ │ ├── TodoItem.vue // 列表项组件│ │ └── TodoFilter.vue // 筛选组件│ ├── stores/│ │ └── todoStore.js // 状态管理│ ├── App.vue│ └── main.js└── package.json
3. 核心代码实现
(1)状态管理(stores/todoStore.js)
import { defineStore } from \'pinia\'export const useTodoStore = defineStore(\'todo\', { state: () => ({ todos: [ { id: 1, text: \'学习Vue\', completed: false }, { id: 2, text: \'完成项目\', completed: true } ], filter: \'all\' // all, active, completed }), getters: { // 筛选后的待办事项 filteredTodos(state) { switch (state.filter) { case \'active\': return state.todos.filter(todo => !todo.completed) case \'completed\': return state.todos.filter(todo => todo.completed) default: return state.todos } }, // 未完成数量 activeCount(state) { return state.todos.filter(todo => !todo.completed).length } }, actions: { // 添加待办 addTodo(text) { if (!text.trim()) return this.todos.push({ id: Date.now(), text, completed: false }) }, // 切换完成状态 toggleTodo(id) { const todo = this.todos.find(todo => todo.id === id) if (todo) { todo.completed = !todo.completed } }, // 删除待办 deleteTodo(id) { this.todos = this.todos.filter(todo => todo.id !== id) }, // 设置筛选条件 setFilter(filter) { this.filter = filter } }})
(2)输入组件(components/TodoInput.vue)
import { useTodoStore } from \'../stores/todoStore\'export default { data() { return { newTodo: \'\' } }, methods: { handleAdd() { const todoStore = useTodoStore() todoStore.addTodo(this.newTodo) this.newTodo = \'\' // 清空输入框 } }}
(3)列表项组件(components/TodoItem.vue)
{{ todo.text }} export default { props: { todo: { type: Object, required: true } }}.completed { text-decoration: line-through; color: #999;}
(4)列表组件(components/TodoList.vue)
没有待办事项
import TodoItem from \'./TodoItem.vue\'import { useTodoStore } from \'../stores/todoStore\'export default { components: { TodoItem }, computed: { filteredTodos() { return useTodoStore().filteredTodos } }, methods: { toggleTodo(id) { useTodoStore().toggleTodo(id) }, deleteTodo(id) { useTodoStore().deleteTodo(id) } }}
(5)筛选组件(components/TodoFilter.vue)
筛选: 剩余:{{ activeCount }} 项 import { useTodoStore } from \'../stores/todoStore\'export default { props: { currentFilter: { type: String, required: true } }, computed: { activeCount() { return useTodoStore().activeCount } }}.active { background-color: #42b983; color: white;}
(6)主组件(App.vue)
待办事项
import TodoInput from \'./components/TodoInput.vue\'import TodoList from \'./components/TodoList.vue\'import TodoFilter from \'./components/TodoFilter.vue\'import { useTodoStore } from \'./stores/todoStore\'export default { components: { TodoInput, TodoList, TodoFilter }, computed: { filter() { return useTodoStore().filter } }, methods: { handleFilter(filter) { useTodoStore().setFilter(filter) } }}
4. 项目运行与测试
# 安装依赖npm install# 运行项目npm run serve
访问http://localhost:8080
即可使用待办事项应用,测试各功能是否正常工作:
- 输入框中输入内容,按回车或点击添加按钮
- 点击待办事项文本切换完成状态
- 点击删除按钮移除待办事项
- 切换筛选按钮查看不同状态的待办事项
- 检查剩余数量统计是否正确