07.前端框架入门

1. 为什么需要前端框架?

1.1 传统开发的痛点

在之前的章节中,我们使用原生JavaScript实现了待办事项应用。

问题

  • 需要手动操作DOM(创建元素、更新元素)
  • 数据和界面不同步(数据变了,界面不会自动更新)
  • 代码重复(每次都要写createElementappendChild

原生JavaScript需要开发者手动管理数据与DOM的同步,当应用复杂度增加时,这种手动管理方式会导致代码冗余、难以维护,且容易出现状态不一致的问题。


1.2 前端框架的解决方案

前端框架 = 自动管理数据和界面同步的工具

传统方式 前端框架方式
手动操作DOM 自动更新界面
数据和界面不同步 数据和界面自动同步
代码重复 代码简洁
难以维护 易于维护

前端框架通过声明式编程和响应式数据绑定,实现了数据与视图的自动同步,开发者只需关注数据状态,框架负责处理DOM更新。


1.3 主流前端框架

框架 特点 适用场景
Vue.js 易学易用,中文文档完善 中小型项目、快速开发
React 生态丰富,大厂使用多 大型项目、复杂应用
Angular 功能全面,企业级 大型企业应用

本课程选择Vue.js作为前端框架,主要考虑其学习曲线平缓、文档完善(包括中文文档),以及在国内开发者社区中的广泛采用。


2. Vue.js基础:从零开始

2.1 什么是Vue.js?

2.1.1 Vue.js的定义

Vue.js = 一个用于构建用户界面的渐进式JavaScript框架

核心特点

  • 响应式:数据变了,界面自动更新
  • 组件化:把页面拆分成可复用的组件
  • 易学易用:语法简单,上手快

2.1.2 Vue.js的诞生

2014年,尤雨溪(Evan You)创建了Vue.js

时间 事件 意义
2014年 Vue.js 1.0发布 轻量级、易用的前端框架
2016年 Vue.js 2.0发布 性能大幅提升,生态完善
2020年 Vue.js 3.0发布 性能更好,TypeScript支持

Vue.js由尤雨溪(Evan You)于2014年创建,是当前主流的前端框架之一,在全球范围内被广泛采用,GitHub上拥有超过40万star。


2.2 第一个Vue应用

2.2.1 使用CDN引入Vue

最简单的方式:直接在HTML中引入Vue.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>第一个Vue应用</title>
<!-- 引入Vue.js -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
</div>

<script>
// 创建Vue应用
const { createApp } = Vue;

createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app');
</script>
</body>
</html>

效果:页面上显示"Hello Vue!"


2.2.2 代码解析

关键部分

  1. {{ message }} - 插值表达式
    • 显示data中的message
    • 数据变了,界面自动更新
  2. data() - 数据函数
    • 返回一个对象,包含应用的数据
    • 这些数据是响应式
  3. .mount('#app') - 挂载应用
    • 把Vue应用挂载到#app元素上

Vue.js通过响应式系统实现了数据与视图的自动同步。当数据发生变化时,框架会自动更新相关的DOM元素,开发者无需手动操作DOM。


2.3 响应式数据:数据和界面自动同步

2.3.1 响应式数据的概念

Vue.js的响应式系统基于ES6的Proxy实现,能够追踪数据的变化并自动更新相关的视图。当数据对象的属性被访问或修改时,Vue会记录依赖关系,并在数据变化时触发视图更新。

这种响应式机制使得开发者可以专注于数据逻辑,而不需要关心DOM操作的细节。


2.3.2 实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<p>计数器:{{ count }}</p>
<button @click="count++">点击+1</button>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
count: 0
}
}
}).mount('#app');
</script>

效果

  • 点击按钮 → count增加 → 界面自动显示新值
  • 不需要写document.getElementById()textContent等代码!

2.4 指令:Vue的特殊属性

2.4.1 v-if - 条件渲染

作用:根据条件显示/隐藏元素

语法v-if="条件"

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<p v-if="isShow">这段文字会显示</p>
<button @click="isShow = !isShow">切换显示</button>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
isShow: true
}
}
}).mount('#app');
</script>


2.4.2 v-for - 列表渲染

作用:循环渲染数组或对象

语法v-for="item in items"

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
todos: [
{ id: 1, text: '学习Vue' },
{ id: 2, text: '完成作业' },
{ id: 3, text: '写代码' }
]
}
}
}).mount('#app');
</script>

使用v-for时必须为每个列表项设置唯一的:key属性。key值帮助Vue识别每个节点的身份,从而在列表更新时更高效地复用和重排DOM元素,提升渲染性能。


2.4.3 v-model - 双向绑定

作用:实现表单元素和数据的双向绑定

语法v-model="数据"

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<input v-model="message" placeholder="输入内容">
<p>你输入的内容:{{ message }}</p>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: ''
}
}
}).mount('#app');
</script>

v-model实现了表单元素与数据的双向绑定:用户输入会更新数据,数据变化也会更新表单显示。这消除了手动监听事件和更新DOM的代码。


2.4.4 @click - 事件监听

作用:监听点击事件(@v-on:的简写)

语法@click="方法名"

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<button @click="sayHello">点击我</button>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: 'Hello'
}
},
methods: {
sayHello() {
alert('Hello Vue!');
}
}
}).mount('#app');
</script>


2.5 方法:methods

2.5.1 定义方法

语法:在methods对象中定义方法

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div id="app">
<p>计数器:{{ count }}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
}
}
}).mount('#app');
</script>

methods中访问data中定义的数据时,需要通过this关键字引用,因为Vue会将data返回的对象挂载到组件实例上。


3. 实战案例:用Vue重写待办事项应用

3.1 需求分析

我们要用Vue.js重写之前的待办事项应用:

  • 添加待办事项
  • 删除待办事项
  • 切换完成状态
  • 使用LocalStorage保存数据

3.2 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue待办事项</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
body {
font-family: '微软雅黑', Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input {
flex: 1;
padding: 10px;
}
button {
padding: 10px 20px;
cursor: pointer;
}
.todo-item {
display: flex;
justify-content: space-between;
padding: 10px;
margin-bottom: 10px;
background: #f0f0f0;
border-radius: 4px;
}
.todo-item.completed {
opacity: 0.6;
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="app">
<h1>📝 待办事项</h1>

<div class="input-group">
<input
v-model="newTodo"
@keypress.enter="addTodo"
placeholder="输入待办事项...">
<button @click="addTodo">添加</button>
<button @click="clearAll">清除全部</button>
</div>

<ul>
<li
v-for="todo in todos"
:key="todo.id"
class="todo-item"
:class="{ completed: todo.completed }">
<span @click="toggleTodo(todo.id)">{{ todo.text }}</span>
<button @click="deleteTodo(todo.id)">删除</button>
</li>
</ul>

<p>总计:{{ todos.length }},已完成:{{ completedCount }}</p>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
newTodo: '',
todos: []
}
},
computed: {
completedCount() {
return this.todos.filter(todo => todo.completed).length;
}
},
methods: {
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
id: Date.now(),
text: this.newTodo,
completed: false
});
this.newTodo = '';
this.saveTodos();
}
},
deleteTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
this.saveTodos();
},
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
this.saveTodos();
}
},
clearAll() {
if (confirm('确定要清除所有待办事项吗?')) {
this.todos = [];
this.saveTodos();
}
},
saveTodos() {
localStorage.setItem('todos', JSON.stringify(this.todos));
},
loadTodos() {
const todosStr = localStorage.getItem('todos');
if (todosStr) {
this.todos = JSON.parse(todosStr);
}
}
},
mounted() {
this.loadTodos();
}
}).mount('#app');
</script>
</body>
</html>

3.3 代码解析

关键点

  1. v-model="newTodo" - 双向绑定输入框
  2. v-for="todo in todos" - 循环渲染列表
  3. @click="addTodo" - 点击事件
  4. computed - 计算属性(自动计算已完成数量)
  5. mounted() - 生命周期钩子(页面加载时执行)

对比原生JavaScript

  • 原生:需要手动操作DOM、手动更新界面
  • Vue:数据和界面自动同步,代码更简洁

4. 组件化:可复用的代码块

4.1 什么是组件?

4.1.1 组件的概念

组件是Vue.js中可复用的代码单元,它将HTML、CSS和JavaScript封装在一起,形成一个独立的、可复用的模块。组件化开发使得大型应用可以被拆分为多个功能独立的组件,每个组件负责特定的功能或视图部分。

组件的特点: - 可复用:同一组件可以在多处使用 - 独立性:每个组件拥有自己的数据和方法 - 可组合:组件可以嵌套组合成更复杂的应用


4.1.2 组件的优势

优势 说明 实际效果
代码复用 一个组件可以在多处使用 减少重复代码
易于维护 修改组件,所有使用的地方都更新 维护更方便
团队协作 不同人负责不同组件 开发效率更高

4.2 创建组件

4.2.1 全局组件

语法

1
2
3
4
5
6
7
8
9
10
11
app.component('组件名', {
template: 'HTML模板',
data() {
return {
// 数据
}
},
methods: {
// 方法
}
});

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<div id="app">
<todo-item></todo-item>
<todo-item></todo-item>
</div>

<script>
const { createApp } = Vue;

const app = createApp({});

// 定义全局组件
app.component('todo-item', {
template: `
<div class="todo-item">
<span>{{ text }}</span>
<button @click="deleteItem">删除</button>
</div>
`,
data() {
return {
text: '待办事项'
}
},
methods: {
deleteItem() {
alert('删除');
}
}
});

app.mount('#app');
</script>


4.2.2 组件通信:props

作用:父组件向子组件传递数据

语法

1
2
3
4
5
6
7
8
// 子组件
app.component('todo-item', {
props: ['text'], // 接收props
template: '<div>{{ text }}</div>'
});

// 父组件使用
<todo-item text="学习Vue"></todo-item>

实际例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div id="app">
<todo-item
v-for="todo in todos"
:key="todo.id"
:text="todo.text">
</todo-item>
</div>

<script>
const { createApp } = Vue;

const app = createApp({
data() {
return {
todos: [
{ id: 1, text: '学习Vue' },
{ id: 2, text: '完成作业' }
]
}
}
});

app.component('todo-item', {
props: ['text'],
template: '<div class="todo-item">{{ text }}</div>'
});

app.mount('#app');
</script>


5. AI实战:使用AI生成Vue组件

5.1 任务描述

目标:使用AI工具生成一个Vue组件

功能要求

  • 创建一个用户卡片组件
  • 显示用户名、头像、简介
  • 可以点击关注/取消关注
  • 使用props传递数据

5.2 第一步:设计Prompt

Prompt示例

1
2
3
4
5
6
7
用Vue.js 3创建一个用户卡片组件,要求:
1. 使用props接收用户数据(name、avatar、bio)
2. 显示用户名、头像、简介
3. 有关注/取消关注按钮
4. 点击按钮切换关注状态
5. 使用CDN方式引入Vue
6. 代码注释清晰,样式简洁美观


5.3 AI生成的代码(示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Vue用户卡片组件</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px;
max-width: 300px;
}
.avatar {
width: 60px;
height: 60px;
border-radius: 50%;
margin-bottom: 10px;
}
.name {
font-size: 20px;
font-weight: bold;
margin-bottom: 5px;
}
.bio {
color: #666;
margin-bottom: 15px;
}
.follow-btn {
padding: 8px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #3498db;
color: white;
}
.follow-btn.following {
background-color: #95a5a6;
}
</style>
</head>
<body>
<div id="app">
<user-card
v-for="user in users"
:key="user.id"
:name="user.name"
:avatar="user.avatar"
:bio="user.bio">
</user-card>
</div>

<script>
const { createApp } = Vue;

const app = createApp({
data() {
return {
users: [
{
id: 1,
name: '张三',
avatar: 'https://via.placeholder.com/60',
bio: '前端开发工程师'
},
{
id: 2,
name: '李四',
avatar: 'https://via.placeholder.com/60',
bio: 'UI设计师'
}
]
}
}
});

// 定义用户卡片组件
app.component('user-card', {
props: ['name', 'avatar', 'bio'],
data() {
return {
isFollowing: false
}
},
template: `
<div class="user-card">
<img :src="avatar" :alt="name" class="avatar">
<div class="name">{{ name }}</div>
<div class="bio">{{ bio }}</div>
<button
class="follow-btn"
:class="{ following: isFollowing }"
@click="toggleFollow">
{{ isFollowing ? '已关注' : '关注' }}
</button>
</div>
`,
methods: {
toggleFollow() {
this.isFollowing = !this.isFollowing;
}
}
});

app.mount('#app');
</script>
</body>
</html>

5.4 代码解析

核心思路

  1. 使用props接收父组件传递的数据
  2. 使用data()定义组件内部状态(isFollowing
  3. 使用methods定义组件方法(toggleFollow
  4. 使用v-bind绑定动态属性(:src:class

关键点

  • 组件是可复用
  • 每个组件有独立的数据和方法
  • 通过props实现组件通信

6. Vue.js的优势总结

优势 说明 实际效果
响应式 数据变了,界面自动更新 不需要手动操作DOM
组件化 代码可复用、易维护 开发效率提升
易学易用 语法简单,上手快 适合初学者
生态丰富 有大量插件和工具 开发更方便

7. 本节总结

你已经学会了:

Vue.js基础

  • 什么是Vue.js
  • 如何创建Vue应用
  • 响应式数据的概念

Vue指令

  • v-if - 条件渲染
  • v-for - 列表渲染
  • v-model - 双向绑定
  • @click - 事件监听

Vue方法

  • methods - 定义方法
  • computed - 计算属性
  • mounted - 生命周期钩子

组件化

  • 什么是组件
  • 如何创建组件
  • 组件通信(props)

实战技能

  • 用Vue重写待办事项应用
  • 使用AI生成Vue组件

Vue.js通过声明式编程和响应式系统,将前端开发从命令式的DOM操作转变为数据驱动的开发模式。掌握Vue.js的核心概念和组件化思想,是进行现代前端开发的基础。建议通过实际项目练习,逐步熟悉框架的使用模式和最佳实践。