Vue基础入门

目录


Vue概述

什么是Vue

Vue是一套用于构建用户界面的渐进式JavaScript框架,由尤雨溪于2014年创建。与其他大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

Vue的特点

特点 说明
渐进式框架 可以逐步引入,无需全部掌握
组件化 允许使用小型、独立和可复用组件构建大型应用
指令系统 提供丰富的指令处理DOM操作
响应式数据绑定 数据变化自动更新视图
单文件组件 将HTML、CSS、JS写在同一个.vue文件中

Vue版本对比

特性 Vue2 Vue3
架构 Options API Composition API + Options API
响应式原理 Object.defineProperty Proxy
TypeScript支持 部分支持 完整支持
体积 约20KB 约10KB(优化后更小)
生命周期 beforeCreate/created等 setup/onMounted等

Vue2与Vue3核心区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Vue2响应式原理
data() {
return {
message: 'Hello Vue'
}
}
// Vue2使用Object.defineProperty实现响应式

// Vue3响应式原理
import { ref, reactive } from 'vue'

// Vue3使用Proxy实现响应式
const state = reactive({
message: 'Hello Vue'
})

const count = ref(0)

Vue实例

创建Vue实例

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
// Vue2创建实例
new Vue({
el: '#app',
data: {
message: 'Hello Vue'
},
methods: {
handleClick() {
console.log(this.message)
}
}
})

// Vue3创建实例
import { createApp } from 'vue'

const app = createApp({
data() {
return {
message: 'Hello Vue'
}
},
methods: {
handleClick() {
console.log(this.message)
}
}
})

app.mount('#app')

Vue实例属性

属性 说明
$data 组件实例观察的数据对象
$props 当前组件接收的props属性
$el Vue实例使用的根DOM元素
$options 当前Vue实例的初始化选项
$parent 父实例,如果当前实例有的话
$root 当前组件树的根实例
$children 当前实例的直接子组件
$refs DOM元素和组件实例的引用

Vue实例生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────┐
│ beforeCreate │ 实例初始化后,数据观测/事件配置之前
├─────────────────────────────┤
│ created │ 实例创建完成后,数据观测/属性方法已配置
├─────────────────────────────┤
│ beforeMount │ 模板编译/渲染函数挂载之前
├─────────────────────────────┤
│ mounted │ 模板已挂载到DOM,可以访问$el/$refs
├─────────────────────────────┤
│ beforeUpdate │ 数据变化后,DOM更新之前
├─────────────────────────────┤
│ updated │ DOM更新完成后
├─────────────────────────────┤
│ beforeDestroy │ 实例销毁之前,清理watcher/子组件/事件
├─────────────────────────────┤
│ destroyed │ 实例销毁后
└─────────────────────────────┘

模板语法

插值表达式

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 文本插值 -->
<span>Message: {{ message }}</span>

<!-- JavaScript表达式 -->
<span>{{ number + 1 }}</span>
<span>{{ ok ? 'YES' : 'NO' }}</span>
<span>{{ message.split('').reverse().join('') }}</span>

<!-- 仅支持单个表达式 -->
<!-- 错误示例 -->
{{ var a = 1 }}
{{ if (ok) { return message } }}

v-text指令

1
2
<!-- 等同于 {{ message }} -->
<span v-text="message"></span>

v-html指令

1
2
<!-- 插入HTML内容,有XSS风险,仅在可信内容使用 -->
<div v-html="htmlContent"></div>

v-bind指令

1
2
3
4
5
6
7
<!-- 动态绑定属性 -->
<img v-bind:src="imageUrl">
<!-- 简写 -->
<img :src="imageUrl">

<!-- 绑定多个属性 -->
<div v-bind="{ id: dynamicId, class: dynamicClass }"></div>

v-on指令

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 绑定事件 -->
<button v-on:click="handleClick">点击</button>
<!-- 简写 -->
<button @click="handleClick">点击</button>

<!-- 事件修饰符 -->
<form @submit.prevent="onSubmit">...</form>
<a @click.stop="handleClick">链接</a>
<input @keyup.enter="submit">

<!-- 绑定多个事件 -->
<button @="{ click: handleClick, mouseenter: handleEnter }">按钮</button>

数据绑定

单向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 数据 -> 视图 -->
<template>
<div>
<input :value="message" @input="message = $event.target.value">
<p>{{ message }}</p>
</div>
</template>

<script>
export default {
data() {
return {
message: 'Hello'
}
}
}
</script>

双向绑定(v-model)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Vue2 -->
<template>
<div>
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>

<script>
export default {
data() {
return {
message: 'Hello'
}
}
}
</script>

v-model原理

1
2
3
4
5
6
7
8
9
10
11
// 文本输入框
// 相当于
<input :value="message" @input="message = $event.target.value">

// 复选框
// 相当于
<input type="checkbox" :checked="isChecked" @change="isChecked = $event.target.checked">

// Select下拉框
// 相当于
<select :value="selected" @change="selected = $event.target.value">...</select>

v-model修饰符

修饰符 说明
.lazy 切换为change事件触发
.number 自动转换为数字
.trim 自动去除首尾空白字符
1
2
3
<input v-model.lazy="message">
<input v-model.number="age">
<input v-model.trim="username">

条件渲染

v-if与v-show

1
2
3
4
5
<!-- v-if: 条件不满足时元素不存在于DOM -->
<div v-if="show">v-if条件渲染</div>

<!-- v-show: 条件不满足时元素display:none -->
<div v-show="show">v-show条件渲染</div>

v-if vs v-show

特性 v-if v-show
原理 动态添加/删除DOM元素 切换display属性
初始渲染 不满足条件不渲染 不满足条件也渲染(隐藏)
切换开销 高(涉及DOM操作) 低(仅切换样式)
适用场景 运行时很少改变 频繁切换

v-else-if与v-else

1
2
3
4
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">及格</div>
<div v-else>不及格</div>

key值的作用

1
2
3
4
5
6
7
<!-- 使用key区分元素,实现强制重渲染 -->
<template v-if="loginType === 'username'">
<input key="username-input" placeholder="请输入用户名">
</template>
<template v-else>
<input key="email-input" placeholder="请输入邮箱">
</template>

列表渲染

v-for基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 遍历数组 -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>

<!-- 带索引 -->
<ul>
<li v-for="(item, index) in items" :key="index">
{{ index }} - {{ item.name }}
</li>
</ul>

<!-- 遍历对象 -->
<ul>
<li v-for="(value, key, index) in object" :key="key">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>

<!-- 遍历数字 -->
<span v-for="n in 10" :key="n">{{ n }}</span>

v-for与key

1
2
3
4
5
6
<!-- 列表数据变更时,key帮助Vue识别哪些元素发生了变化 -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>

key值选择原则

场景 推荐key值
数组有唯一标识 使用数组元素的id
数组无唯一标识 使用数组索引index
列表顺序会改变 不推荐使用index
列表会被增删 使用唯一标识

数组更新检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Vue2 - 可以触发视图更新的方法
this.items.push(newItem) // 末尾添加
this.items.pop() // 末尾删除
this.items.shift() // 开头删除
this.items.unshift(newItem) // 开头添加
this.items.splice(index, 1) // 删除/插入/替换
this.items.sort() // 排序
this.items.reverse() // 反转

// Vue2 - 无法触发视图更新的情况
this.items[0] = newValue // 直接通过索引修改
this.items.length = 0 // 直接修改length

// 解决Vue2直接修改索引的问题
Vue.set(this.items, 0, newValue)
this.items.splice(0, 1, newValue)

事件处理

基本用法

1
2
3
4
5
6
7
<button @click="handleClick">点击</button>

<!-- 内联处理器访问事件对象 -->
<button @click="handleClick($event)">点击</button>

<!-- 同时传递参数和事件对象 -->
<button @click="handleClick(arg, $event)">点击</button>

事件修饰符

修饰符 说明
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 使用事件捕获模式
.self 仅当事件作用在自身时触发
.once 只触发一次
.passive 提升移动端滚动性能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 阻止冒泡 -->
<button @click.stop="handleClick">

<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit">

<!-- 捕获模式 -->
<div @click.capture="handleCapture">

<!-- 仅自身触发 -->
<div @click.self="handleSelf">

<!-- 只触发一次 -->
<button @click.once="handleOnce">

<!-- 被动模式 -->
<div @touchstart.passive="handleTouch">

按键修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 按键码 -->
<input @keyup.enter="submit">
<input @keyup.esc="cancel">
<input @keyup.space="confirm">

<!-- 常用按键码 -->
.enter / .tab / .delete / .esc
.space / .up / .down / .left / .right

<!-- 组合键 -->
<input @keyup.ctrl.enter="send">
<button @click.ctrl="copy">复制</button>

<!-- .exact修饰符 - 精确组合 -->
<button @click.ctrl.exact="copy">只有Ctrl点击</button>

鼠标按钮修饰符

1
2
3
<div @mousedown.left="handleLeft">左键</div>
<div @mousedown.right="handleRight">右键</div>
<div @mousedown.middle="handleMiddle">中键</div>

表单绑定

文本输入

1
2
<input v-model="message" placeholder="请输入">
<p>输入的内容: {{ message }}</p>

多行文本

1
<textarea v-model="message"></textarea>

复选框

1
2
3
4
5
6
7
8
9
<!-- 单个复选框 -->
<input type="checkbox" v-model="checked">
<label>{{ checked }}</label>

<!-- 多个复选框,绑定到数组 -->
<input type="checkbox" value="足球" v-model="hobbies">
<input type="checkbox" value="篮球" v-model="hobbies">
<input type="checkbox" value="羽毛球" v-model="hobbies">
<p>选择: {{ hobbies }}</p>

单选按钮

1
2
3
<input type="radio" value="男" v-model="gender">
<input type="radio" value="女" v-model="gender">
<p>性别: {{ gender }}</p>

Select下拉框

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 单选 -->
<select v-model="selected">
<option value="">请选择</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="深圳">深圳</option>
</select>

<!-- 多选 -->
<select v-model="selectedMulti" multiple>
<option value="北京">北京</option>
<option value="上海">上海</option>
</select>

值绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 复选框值绑定到对象 -->
<input type="checkbox" v-model="toggle" :true-value="yes" :false-value="no">
<script>
export default {
data() {
return {
toggle: 'no',
yes: 'yes',
no: 'no'
}
}
}
</script>

<!-- 单选按钮值绑定 -->
<input type="radio" v-model="pick" :value="first">
<input type="radio" v-model="pick" :value="second">

<!-- Select选项值绑定 -->
<select v-model="selected">
<option :value="{ number: 123 }">选项</option>
</select>

计算属性与侦听器

计算属性

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
<template>
<div>
<p>原价格: {{ price }}</p>
<p>折后价: {{ discountedPrice }}</p>
<p>总价: {{ totalPrice }}</p>
</div>
</template>

<script>
export default {
data() {
return {
price: 100,
discount: 0.8,
items: [
{ name: '商品1', price: 100 },
{ name: '商品2', price: 200 }
]
}
},
computed: {
// 简写形式(只有getter)
discountedPrice() {
return this.price * this.discount
},

// 完整形式(getter和setter)
totalPrice: {
get() {
return this.items.reduce((sum, item) => sum + item.price, 0)
},
set(value) {
// 当设置totalPrice时会调用setter
console.log('新总价:', value)
}
}
}
}
</script>

计算属性 vs 方法

特性 计算属性 方法
缓存 有缓存,基于响应式依赖 无缓存,每次调用都执行
触发时机 响应式依赖变化时 每次调用都执行
使用方式 当属性使用 函数调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 计算属性示例 - 有缓存
computed: {
// 只要message不变,多次访问cachedMessage会返回缓存结果
cachedMessage() {
console.log('计算中...')
return this.message.split('').reverse().join('')
}
}

// 方法示例 - 无缓存
methods: {
getMessage() {
console.log('计算中...')
return this.message.split('').reverse().join('')
}
}

侦听器

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
<template>
<div>
<input v-model="question">
<p>回答: {{ answer }}</p>
</div>
</template>

<script>
export default {
data() {
return {
question: '',
answer: '请输入问题...'
}
},
watch: {
// 基础用法 - 侦听question变化
question(newQuestion, oldQuestion) {
this.answer = '等待输入...'
this.debouncedGetAnswer()
},

// 深度侦听对象变化
user: {
handler(newVal, oldVal) {
console.log('user变化:', newVal)
},
deep: true // 深度侦听
},

// 立即执行
immediateValue: {
handler(newVal) {
console.log('立即执行:', newVal)
},
immediate: true // 立即执行一次
},

// 侦听嵌套属性
'user.name': function(newName) {
console.log('用户名变化:', newName)
}
},
methods: {
debouncedGetAnswer() {
// 防抖逻辑
}
}
}
</script>

computed vs watch

特性 computed watch
适用场景 依赖其他数据计算得出结果 数据变化时执行异步/开销大的操作
响应方式 自动响应变化 手动监听特定数据变化
缓存 有缓存 无缓存
返回值 必须有返回值 可以没有

过滤器

本地过滤器

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
<template>
<div>
<!-- 过滤器串联 -->
<p>{{ message | filterA | filterB }}</p>

<!-- 过滤器带参数 -->
<p>{{ date | formatDate('YYYY-MM-DD') }}</p>
</div>
</template>

<script>
export default {
data() {
return {
message: 'hello',
date: new Date()
}
},
filters: {
filterA(value) {
return value.toUpperCase()
},
filterB(value) {
return value + '!!!'
},
formatDate(value, format) {
// 日期格式化逻辑
return format.replace('YYYY', value.getFullYear())
}
}
}
</script>

全局过滤器

1
2
3
4
Vue.filter('capitalize', function(value) {
if (!value) return ''
return value.charAt(0).toUpperCase() + value.slice(1)
})

过滤器与计算属性

1
2
3
4
5
6
7
// 推荐使用计算属性替代过滤器
computed: {
// 过滤器功能用计算属性实现
upperCaseMessage() {
return this.message ? this.message.toUpperCase() : ''
}
}

总结

本篇文档涵盖了Vue的基础知识,包括:

  1. Vue实例的创建和生命周期 - 理解了Vue实例从创建到销毁的完整过程
  2. 模板语法 - 插值、指令、事件绑定等核心语法
  3. 数据绑定 - 单向绑定和双向绑定的原理与使用
  4. 条件渲染和列表渲染 - v-if/v-show/v-for的使用场景
  5. 事件处理 - 事件绑定、修饰符、按键修饰符等
  6. 表单处理 - 各种表单元素的v-model使用
  7. 计算属性和侦听器 - 数据计算和监听的核心功能
  8. 过滤器 - 数据的格式化处理

这些是Vue开发的基础,掌握好这些内容才能更好地深入学习Vue的其他高级特性。


相关链接