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
| data() { return { message: 'Hello Vue' } }
import { ref, reactive } from 'vue'
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
| new Vue({ el: '#app', data: { message: 'Hello Vue' }, methods: { handleClick() { console.log(this.message) } } })
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>
<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
| <span v-text="message"></span>
|
v-html指令
1 2
| <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
| <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
| <div v-if="show">v-if条件渲染</div>
<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
| <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
| <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
| 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()
this.items[0] = newValue this.items.length = 0
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>
<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 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: { discountedPrice() { return this.price * this.discount },
totalPrice: { get() { return this.items.reduce((sum, item) => sum + item.price, 0) }, set(value) { console.log('新总价:', value) } } } } </script>
|
计算属性 vs 方法
| 特性 |
计算属性 |
方法 |
| 缓存 |
有缓存,基于响应式依赖 |
无缓存,每次调用都执行 |
| 触发时机 |
响应式依赖变化时 |
每次调用都执行 |
| 使用方式 |
当属性使用 |
函数调用 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| computed: { 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(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的基础知识,包括:
- Vue实例的创建和生命周期 - 理解了Vue实例从创建到销毁的完整过程
- 模板语法 - 插值、指令、事件绑定等核心语法
- 数据绑定 - 单向绑定和双向绑定的原理与使用
- 条件渲染和列表渲染 - v-if/v-show/v-for的使用场景
- 事件处理 - 事件绑定、修饰符、按键修饰符等
- 表单处理 - 各种表单元素的v-model使用
- 计算属性和侦听器 - 数据计算和监听的核心功能
- 过滤器 - 数据的格式化处理
这些是Vue开发的基础,掌握好这些内容才能更好地深入学习Vue的其他高级特性。
相关链接