当前位置: 首页 > news >正文

Vue2 基础用法

Vue2 基础用法

目录

  1. 创建Vue实例
  2. 插值表达式
  3. 响应式原理
  4. Vue指令
  5. 条件渲染 v-show 和 v-if
  6. v-else 和 v-else-if
  7. 事件处理 v-on
  8. v-on调用传参
  9. 双向绑定 v-bind
  10. 列表渲染 v-for
  11. v-for的key属性
  12. v-model双向绑定
  13. 指令修饰符
  14. v-bind操作class
  15. v-model应用于其他表单元素
  16. 计算属性
  17. 计算属性的完整写法
  18. watch监视器
  19. watch完整写法
  20. 生命周期
  21. 初始化渲染和获取焦点
  22. mounted获取焦点

1. 创建Vue实例

Vue.js的核心是创建一个Vue实例来管理应用程序的数据和行为。

基本语法

<!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://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body><div id="app"><h1>{{ message }}</h1><p>{{ user.name }}</p></div><script>// 创建Vue实例const vm = new Vue({el: '#app',  // 挂载点data: {      // 数据对象message: 'Hello Vue.js!',user: {name: '张三',age: 25}},methods: {   // 方法对象greet() {alert('欢迎使用Vue.js!');}}});</script>
</body>
</html>

Vue实例的选项

new Vue({// 1. 挂载点 - 指定Vue实例管理的DOM元素el: '#app',// 2. 数据 - 定义响应式数据data: {message: 'Hello World',count: 0},// 3. 方法 - 定义事件处理函数methods: {increment() {this.count++;}},// 4. 计算属性computed: {doubleCount() {return this.count * 2;}},// 5. 侦听器watch: {count(newVal, oldVal) {console.log(`count从${oldVal}变为${newVal}`);}}
});

2. 插值表达式

插值表达式(双花括号语法)是Vue.js中最基本的数据绑定方式。

基本用法

<div id="app"><!-- 文本插值 --><p>{{ message }}</p><!-- 表达式计算 --><p>{{ number + 1 }}</p><!-- 函数调用 --><p>{{ message.split('').reverse().join('') }}</p><!-- 三元运算符 --><p>{{ ok ? 'YES' : 'NO' }}</p><!-- 对象属性访问 --><p>{{ user.name }} - {{ user.age }}岁</p>
</div><script>
new Vue({el: '#app',data: {message: 'Hello Vue',number: 10,ok: true,user: {name: '李四',age: 30}}
});
</script>

插值表达式注意事项

<div id="app"><!-- ✅ 正确:简单表达式 --><p>{{ message.toUpperCase() }}</p><!-- ❌ 错误:语句 --><p>{{ var a = 1 }}</p><!-- ❌ 错误:流程控制 --><p>{{ if (ok) { return message } }}</p><!-- ✅ 正确:使用三元运算符 --><p>{{ ok ? message : 'Not OK' }}</p>
</div>

3. 响应式原理

Vue.js的响应式系统是其核心特性,能够自动追踪依赖并在数据变化时更新视图。

响应式数据

<div id="app"><h2>响应式数据演示</h2><p>计数器: {{ count }}</p><p>用户名: {{ user.name }}</p><p>数组: {{ items.join(', ') }}</p><button @click="updateData">更新数据</button>
</div><script>
new Vue({el: '#app',data: {count: 0,user: {name: '王五'},items: ['苹果', '香蕉', '橙子']},methods: {updateData() {// 这些操作都会触发视图更新this.count++;this.user.name = '赵六';this.items.push('葡萄');}}
});
</script>

响应式原理说明

Vue.js通过以下机制实现响应式:

  1. 数据劫持: 使用Object.defineProperty()重新定义data中的属性
  2. 依赖收集: 在getter中收集依赖
  3. 派发更新: 在setter中通知所有依赖进行更新
// 简化版响应式原理
function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {get() {console.log('获取值:', val);return val;},set(newVal) {if (newVal !== val) {console.log('设置值:', newVal);val = newVal;// 触发更新updateView();}}});
}

4. Vue指令

Vue指令是带有v-前缀的特殊属性,用于在模板中声明式地将数据绑定到DOM。

常用指令概览

<div id="app"><!-- v-text: 更新元素的文本内容 --><p v-text="message"></p><!-- v-html: 更新元素的innerHTML --><div v-html="htmlContent"></div><!-- v-show: 条件性显示元素 --><p v-show="isVisible">这段文字是否显示</p><!-- v-if: 条件性渲染元素 --><p v-if="isShow">条件渲染</p><!-- v-for: 基于数据多次渲染元素 --><ul><li v-for="item in list" :key="item.id">{{ item.name }}</li></ul><!-- v-on: 监听DOM事件 --><button v-on:click="handleClick">点击我</button><!-- v-bind: 动态绑定属性 --><img v-bind:src="imageSrc" v-bind:alt="imageAlt"><!-- v-model: 表单元素双向绑定 --><input v-model="inputValue">
</div><script>
new Vue({el: '#app',data: {message: 'Hello Vue',htmlContent: '<strong>粗体文字</strong>',isVisible: true,isShow: true,list: [{ id: 1, name: '项目1' },{ id: 2, name: '项目2' }],imageSrc: 'https://vuejs.org/images/logo.png',imageAlt: 'Vue Logo',inputValue: ''},methods: {handleClick() {console.log('按钮被点击了!');}}
});
</script>

5. 条件渲染

Vue提供了v-showv-if两种条件渲染方式。

v-show vs v-if

<div id="app"><h2>条件渲染演示</h2><!-- v-show: 通过CSS display属性控制显示隐藏 --><p v-show="showWithVShow">v-show控制的内容(切换display属性)</p><!-- v-if: 条件性地渲染元素 --><p v-if="showWithVIf">v-if控制的内容(条件性渲染)</p><!-- 性能对比 --><div><button @click="toggleShow">切换显示状态</button><button @click="toggleIf">切换渲染状态</button></div><!-- 复杂条件 --><div v-if="user.isVip && user.score > 100">VIP用户且积分大于100</div><!-- 模板包装 --><template v-if="showGroup"><h3>标题</h3><p>内容1</p><p>内容2</p></template>
</div><script>
new Vue({el: '#app',data: {showWithVShow: true,showWithVIf: true,showGroup: true,user: {isVip: true,score: 150}},methods: {toggleShow() {this.showWithVShow = !this.showWithVShow;},toggleIf() {this.showWithVIf = !this.showWithVIf;}}
});
</script>

v-show和v-if的区别

特性v-showv-if
渲染方式始终渲染,切换CSS display条件性渲染
切换开销
初始开销
适用场景频繁切换条件很少改变

6. v-else和v-else-if

配合v-if使用的条件渲染指令。

<div id="app"><!-- 基本 v-else --><div v-if="isLoggedIn"><h3>欢迎回来!</h3><p>用户:{{ username }}</p></div><div v-else><h3>请登录</h3><button @click="login">登录</button></div><!-- v-else-if 链式条件 --><div v-if="score >= 90">优秀</div><div v-else-if="score >= 80">良好</div><div v-else-if="score >= 60">及格</div><div v-else>不及格</div><!-- 复杂条件判断 --><div v-if="weather === 'sunny'"><p>今天阳光明媚</p></div><div v-else-if="weather === 'rainy'"><p>今天下雨了</p></div><div v-else-if="weather === 'cloudy'"><p>今天多云</p></div><div v-else><p>天气未知</p></div><!-- 使用template避免额外包装元素 --><template v-if="userType === 'admin'"><h4>管理员面板</h4><p>用户管理</p><p>系统设置</p></template><template v-else-if="userType === 'editor'"><h4>编辑器面板</h4><p>内容管理</p></template><template v-else><h4>普通用户</h4><p>个人中心</p></template><!-- 控制按钮 --><div><button @click="toggleLogin">切换登录状态</button><button @click="changeScore">改变分数</button><button @click="changeWeather">改变天气</button><button @click="changeUserType">切换用户类型</button></div>
</div><script>
new Vue({el: '#app',data: {isLoggedIn: false,username: '张三',score: 85,weather: 'sunny',userType: 'admin'},methods: {login() {this.isLoggedIn = true;},toggleLogin() {this.isLoggedIn = !this.isLoggedIn;},changeScore() {this.score = Math.floor(Math.random() * 100);},changeWeather() {const weathers = ['sunny', 'rainy', 'cloudy', 'unknown'];this.weather = weathers[Math.floor(Math.random() * weathers.length)];},changeUserType() {const types = ['admin', 'editor', 'user'];const currentIndex = types.indexOf(this.userType);this.userType = types[(currentIndex + 1) % types.length];}}
});
</script>

7. 事件处理

使用v-on指令监听DOM事件。

基本事件处理

<div id="app"><h2>事件处理演示</h2><!-- 基本点击事件 --><button v-on:click="sayHello">点击问候</button><!-- 简写语法 @ --><button @click="count++">计数: {{ count }}</button><!-- 方法调用 --><button @click="increment">增加</button><button @click="decrement">减少</button><!-- 内联处理器 --><button @click="message = '按钮被点击了'">{{ message }}</button><!-- 多个事件 --><button @click="handleClick" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">悬停效果按钮</button><!-- 表单事件 --><input @input="handleInput" @focus="handleFocus" @blur="handleBlur" :value="inputText"><p>输入的内容: {{ inputText }}</p><!-- 键盘事件 --><input @keyup.enter="submitForm" @keyup.esc="clearInput" placeholder="按Enter提交,Esc清空"><!-- 鼠标事件 --><div @click="handleDivClick" style="padding: 20px; background: #f0f0f0; margin: 10px;"><button @click="handleButtonClick">按钮</button><p>点击区域测试事件冒泡</p></div>
</div><script>
new Vue({el: '#app',data: {count: 0,message: '点击按钮改变我',inputText: ''},methods: {sayHello() {alert('Hello Vue.js!');},increment() {this.count++;},decrement() {this.count--;},handleClick() {console.log('按钮被点击');},handleMouseEnter() {console.log('鼠标进入');},handleMouseLeave() {console.log('鼠标离开');},handleInput(event) {this.inputText = event.target.value;},handleFocus() {console.log('输入框获得焦点');},handleBlur() {console.log('输入框失去焦点');},submitForm() {console.log('表单提交:', this.inputText);},clearInput() {this.inputText = '';},handleDivClick() {console.log('DIV被点击');},handleButtonClick() {console.log('按钮被点击');}}
});
</script>

8. v-on调用传参

在事件处理函数中传递参数。

<div id="app"><h2>事件传参演示</h2><!-- 无参数调用 --><button @click="sayHello">无参数</button><!-- 传递静态参数 --><button @click="greet('张三')">问候张三</button><button @click="greet('李四')">问候李四</button><!-- 传递动态参数 --><button @click="greet(currentUser)">问候当前用户</button><!-- 传递多个参数 --><button @click="calculate(5, 3, '+')">5 + 3</button><button @click="calculate(10, 4, '-')">10 - 4</button><!-- 传递事件对象 $event --><button @click="handleWithEvent('参数', $event)">获取事件对象</button><!-- 在列表中传参 --><ul><li v-for="item in items" :key="item.id">{{ item.name }}<button @click="deleteItem(item.id)">删除</button><button @click="updateItem(item.id, '新名称')">更新</button></li></ul><!-- 传递索引 --><div><button v-for="(color, index) in colors" :key="index"@click="selectColor(color, index)":style="{ backgroundColor: color, margin: '5px', padding: '10px' }">{{ color }}</button></div><p>选中的颜色: {{ selectedColor }} (索引: {{ selectedIndex }})</p><p>计算结果: {{ result }}</p>
</div><script>
new Vue({el: '#app',data: {currentUser: '王五',result: '',selectedColor: '',selectedIndex: -1,items: [{ id: 1, name: '项目一' },{ id: 2, name: '项目二' },{ id: 3, name: '项目三' }],colors: ['red', 'green', 'blue', 'yellow', 'purple']},methods: {sayHello() {alert('Hello!');},greet(name) {alert(`你好, ${name}!`);},calculate(a, b, operator) {let result;switch (operator) {case '+':result = a + b;break;case '-':result = a - b;break;case '*':result = a * b;break;case '/':result = a / b;break;}this.result = `${a} ${operator} ${b} = ${result}`;},handleWithEvent(param, event) {console.log('参数:', param);console.log('事件对象:', event);console.log('按钮文本:', event.target.textContent);},deleteItem(id) {this.items = this.items.filter(item => item.id !== id);console.log(`删除了ID为${id}的项目`);},updateItem(id, newName) {const item = this.items.find(item => item.id === id);if (item) {item.name = newName;}},selectColor(color, index) {this.selectedColor = color;this.selectedIndex = index;}}
});
</script>

9. 双向绑定

Vue的双向绑定主要通过v-model实现。

v-bind单向绑定

<div id="app"><h2>v-bind单向绑定演示</h2><!-- 绑定属性 --><img v-bind:src="imageSrc" v-bind:alt="imageAlt" width="100"><!-- 简写语法 --><a :href="linkUrl" :title="linkTitle">{{ linkText }}</a><!-- 绑定class --><div :class="cssClass">动态CSS类</div><!-- 绑定style --><p :style="textStyle">动态样式文本</p><!-- 绑定布尔属性 --><button :disabled="isDisabled">{{ buttonText }}</button><!-- 表单元素单向绑定 --><input :value="inputValue" @input="updateInput" placeholder="单向绑定"><p>输入值: {{ inputValue }}</p><!-- 控制按钮 --><div><button @click="toggleDisabled">切换按钮状态</button><button @click="changeStyle">改变样式</button><button @click="changeImage">切换图片</button></div>
</div><script>
new Vue({el: '#app',data: {imageSrc: 'https://via.placeholder.com/100x100/ff0000/ffffff?text=Red',imageAlt: '红色占位图',linkUrl: 'https://vuejs.org',linkTitle: 'Vue.js官网',linkText: '访问Vue.js',cssClass: 'highlight',textStyle: {color: 'blue',fontSize: '18px',fontWeight: 'bold'},isDisabled: false,buttonText: '可用按钮',inputValue: ''},methods: {updateInput(event) {this.inputValue = event.target.value;},toggleDisabled() {this.isDisabled = !this.isDisabled;this.buttonText = this.isDisabled ? '禁用按钮' : '可用按钮';},changeStyle() {this.textStyle.color = this.textStyle.color === 'blue' ? 'red' : 'blue';this.textStyle.fontSize = this.textStyle.fontSize === '18px' ? '24px' : '18px';},changeImage() {const colors = ['ff0000', '00ff00', '0000ff', 'ffff00'];const color = colors[Math.floor(Math.random() * colors.length)];this.imageSrc = `https://via.placeholder.com/100x100/${color}/ffffff?text=${color}`;}}
});
</script><style>
.highlight {background-color: yellow;padding: 10px;border: 2px solid orange;border-radius: 5px;
}
</style>

10. 列表渲染

使用v-for指令进行列表渲染。

基本列表渲染

<div id="app"><h2>v-for列表渲染演示</h2><!-- 渲染数组 --><h3>水果列表</h3><ul><li v-for="fruit in fruits">{{ fruit }}</li></ul><!-- 带索引的数组渲染 --><h3>带索引的列表</h3><ol><li v-for="(fruit, index) in fruits">{{ index + 1 }}. {{ fruit }}</li></ol><!-- 渲染对象数组 --><h3>用户列表</h3><div v-for="user in users" :key="user.id" class="user-card"><h4>{{ user.name }}</h4><p>年龄: {{ user.age }}</p><p>城市: {{ user.city }}</p></div><!-- 渲染对象 --><h3>用户信息</h3><ul><li v-for="(value, key) in userInfo">{{ key }}: {{ value }}</li></ul><!-- 带索引的对象渲染 --><h3>带索引的对象渲染</h3><ul><li v-for="(value, key, index) in userInfo">{{ index }}. {{ key }}: {{ value }}</li></ul><!-- 渲染数字 --><h3>数字列表</h3><span v-for="n in 10" :key="n">{{ n }} </span><!-- 嵌套v-for --><h3>嵌套列表</h3><div v-for="category in categories" :key="category.name"><h4>{{ category.name }}</h4><ul><li v-for="item in category.items" :key="item">{{ item }}</li></ul></div><!-- template包装 --><h3>使用template包装</h3><template v-for="product in products" :key="product.id"><dt>{{ product.name }}</dt><dd>价格: ¥{{ product.price }}</dd></template>
</div><script>
new Vue({el: '#app',data: {fruits: ['苹果', '香蕉', '橙子', '葡萄'],users: [{ id: 1, name: '张三', age: 25, city: '北京' },{ id: 2, name: '李四', age: 30, city: '上海' },{ id: 3, name: '王五', age: 28, city: '广州' }],userInfo: {name: '赵六',age: 32,email: 'zhaoliu@example.com',phone: '13800138000'},categories: [{name: '水果',items: ['苹果', '香蕉', '橙子']},{name: '蔬菜',items: ['白菜', '萝卜', '土豆']}],products: [{ id: 1, name: '笔记本电脑', price: 5999 },{ id: 2, name: '智能手机', price: 2999 },{ id: 3, name: '平板电脑', price: 3999 }]}
});
</script><style>
.user-card {border: 1px solid #ddd;padding: 15px;margin: 10px 0;border-radius: 5px;background-color: #f9f9f9;
}
</style>

11. v-for的key属性

key属性是Vue进行列表更新优化的重要机制。

key属性的作用与原理

在Vue的列表渲染(v - for)中,key属性是一个非常关键的存在,主要有以下重要作用:

(1)高效更新虚拟DOM(diff算法优化)

Vue在更新列表时,会使用虚拟DOM的diff算法来比较新旧节点。当列表数据发生变化(比如新增、删除、重新排序等)时,key能帮助Vue准确识别哪些节点是新增的、哪些是被删除的、哪些是需要移动位置的,从而避免对整个列表进行无意义的重新渲染,大大提升渲染效率。

举个简单例子,假设原来列表是[A, B, C]key分别对应为1、2、3,后来变成[B, A, C] 。如果没有key,Vue可能会把这三个节点都重新创建、替换;但有了key,Vue能快速识别出AB只是位置交换,只需要移动它们的DOM位置,而不用重新销毁和创建,减少了操作开销。

(2)维持组件状态与避免错误

当列表中的项对应着Vue组件时,key能确保组件的状态(比如组件内部的data、生命周期状态等)被正确维护。如果没有合适的key,Vue可能会错误地复用组件实例,导致组件状态混乱,出现一些难以排查的显示或交互问题。

比如列表里的每个项是一个包含输入框的自定义组件,用户在输入框输入了内容后,如果列表数据变化,没有正确key的话,输入框的内容可能会“乱跑”,因为Vue错误复用了组件,把原本对应其他数据项的组件实例错误匹配,导致显示异常。

key属性的使用规范与示例

(1)使用唯一且稳定的值

key的值应该是唯一且稳定的,一般推荐使用数据本身的唯一标识,比如从后端获取的数据通常会有id字段(如商品的productId、用户的userId )。尽量避免使用索引(index)作为key,虽然在简单场景下用索引好像也能“跑起来”,但在列表有增删操作时,索引会变化,就会让key失去稳定、唯一的意义,引发前面提到的渲染问题。

不推荐用索引的示例(反例)

<ul><!-- 危险做法!列表增删时索引变化会导致key失效 --><li v-for="(item, index) in list" :key="index"> {{ item.name }}</li>
</ul>

推荐用唯一id的示例(正例)
假设list里的每个对象都有唯一id,像这样:

data() {return {list: [{ id: 1, name: '商品A' },{ id: 2, name: '商品B' },{ id: 3, name: '商品C' }]}
}
<ul><!-- 正确做法,用数据唯一id做key --><li v-for="item in list" :key="item.id"> {{ item.name }}</li>
</ul>
(2)在template上使用key(处理分组渲染等场景)

当使用<template>标签配合v-for进行分组或批量渲染时,key要绑定在<template>上,确保这一组节点能被正确识别和更新。

示例:

<template v-for="(group, groupKey) in groupedData" :key="groupKey"><h3>{{ group.title }}</h3><ul><li v-for="item in group.items" :key="item.id">{{ item.name }}</li></ul>
</template>

这里外层<template>keygroupKey(假设是分组的唯一标识,如'fruits''vegetables' ),内层likeyitem.id,保证不同层级的渲染更新都能正确进行。

(3)与Vue组件结合的示例

如果列表渲染的是自定义Vue组件,key同样要遵循上述规则,保证组件实例能正确关联数据,维持状态。

比如有一个<ProductItem>组件,渲染商品列表:

<ProductItem v-for="product in productList" :key="product.id" :product="product" 
/>

这样,当productList数据变化时,Vue能根据product.id精准判断组件的创建、销毁和更新,避免组件状态错乱。

总之,合理使用key属性,是保障Vue列表渲染高效、正确运行的关键一环,开发中要养成优先用数据唯一标识作为key的习惯,避开因key使用不当带来的各种潜在问题。

错误使用key的常见问题及表现

  • 问题1:输入框内容“串位”
    若列表项里有输入框,用索引当key,删除某一项后,输入框内容会异常“移动”。比如原列表[A, B, C]对应输入框填了a、b、c,删除A后,原本B的输入框内容会跑到A的位置显示,因为Vue错误复用了组件,key(索引)变化导致匹配混乱。
  • 问题2:组件生命周期异常
    列表项是自定义组件时,错误key可能让组件的createdmounted等生命周期钩子不按预期执行,比如应该重新创建组件实例时,因key复用导致钩子不触发,组件初始化逻辑失效,出现数据没正确初始化、监听事件丢失等问题。
  • 问题3:DOM更新不及时或错误
    没有keykey不稳定时,diff算法无法准确识别节点变化,可能使DOM更新滞后、重复渲染,页面出现闪烁、内容显示错误(比如新数据没渲染出来,旧数据没及时清除)等现象,影响用户体验和功能正确性。

所以,开发中一定要重视key的合理设置,遵循“唯一、稳定”原则,规避这些常见问题 。

12. v-model双向绑定

基础用法(输入框示例)

v-model 是 Vue 实现表单元素双向数据绑定的指令,它本质上是 v-bind:value(或其他属性,依表单元素而定)和 v-on:input(或对应事件)的语法糖 。

以文本输入框为例:

<div id="app"><!-- v-model 绑定数据 --><input type="text" v-model="message"> <p>你输入的内容是:{{ message }}</p>
</div>
<script>
new Vue({el: '#app',data: {message: ''}
});
</script>

当在输入框输入内容时,message 数据会实时更新;同时,若通过代码修改 message 的值(比如在方法里改变 this.message ),输入框的显示也会同步变化,实现了视图和数据的双向联动。

不同表单元素的v-model

(1)复选框(单个)

单个复选框,v-model 绑定的是布尔值,控制是否选中:

<div id="app"><input type="checkbox" v-model="isChecked"> 是否同意<p>选中状态:{{ isChecked }}</p>
</div>
<script>
new Vue({el: '#app',data: {isChecked: false}
});
</script>

勾选或取消勾选复选框,isChecked 的值会在 truefalse 间切换。

(2)复选框(多个)

多个复选框,v-model 绑定一个数组,数组元素是选中项的 value

<div id="app"><input type="checkbox" value="篮球" v-model="hobbies"> 篮球<input type="checkbox" value="足球" v-model="hobbies"> 足球<input type="checkbox" value="羽毛球" v-model="hobbies"> 羽毛球<p>你的爱好:{{ hobbies }}</p>
</div>
<script>
new Vue({el: '#app',data: {hobbies: []}
});
</script>

选中对应复选框,hobbies 数组会添加对应的 value;取消选中则移除,实现多选数据的双向绑定。

(3)单选按钮

单选按钮组里,v-model 绑定选中项的 value,同一组按钮通过 name 关联(也可依靠 v-model 自动管理互斥):

<div id="app"><input type="radio" value="" v-model="gender"><input type="radio" value="" v-model="gender"><p>你的性别:{{ gender }}</p>
</div>
<script>
new Vue({el: '#app',data: {gender: ''}
});
</script>

点击不同单选按钮,gender 会被设置为对应 value,保证同一时间只有一个选中。

(4)下拉选择框(单选)

下拉框单选时,v-model 绑定选中项的 valueoption 标签的 value 属性,若没设置则取文本内容 ):

<div id="app"><select v-model="city"><option value="beijing">北京</option><option value="shanghai">上海</option><option value="guangzhou">广州</option></select><p>你选择的城市:{{ city }}</p>
</div>
<script>
new Vue({el: '#app',data: {city: 'beijing' // 可设置默认选中}
});
</script>

选择下拉项,city 会更新为对应 optionvalue

(5)下拉选择框(多选)

selectmultiple 属性开启多选,v-model 绑定数组,存储选中项的 value

<div id="app"><select v-model="cities" multiple><option value="beijing">北京</option><option value="shanghai">上海</option><option value="guangzhou">广州</option></select><p>你选择的城市:{{ cities }}</p>
</div>
<script>
new Vue({el: '#app',data: {cities: []}
});
</script>

按住 Ctrl(Windows)或 Command(Mac)可多选,选中项的 value 会存入 cities 数组。

v-model 修饰符

Vue 为 v-model 提供了一些修饰符,方便处理特殊需求:

(1).lazy

默认 v-model 是在 input 事件触发时更新数据(输入即同步),加 .lazy 后,会在 change 事件触发时(比如输入框失去焦点)才更新数据:

<input type="text" v-model.lazy="message">
<!-- 输入时数据不实时变,离开输入框(change事件)才更新 -->
(2).number

自动将用户输入的值转为数值类型,若无法转为数字则保持原值。常用于处理需要数字输入的场景,比如年龄、数量:

<input type="text" v-model.number="age">
<!-- 输入“123”,age 是数字123;输入“abc”,age 还是字符串"abc"(若原本类型允许) -->
(3).trim

自动去除用户输入内容首尾的空白字符,适合处理用户名、密码等不想有多余空格的场景:

<input type="text" v-model.trim="username">
<!-- 输入“  张三  ”,username 会变成“张三” -->

通过 v-model ,Vue 能简洁高效地实现表单数据和视图的双向绑定,搭配不同表单元素和修饰符,满足多样化的交互需求,让表单开发更轻松 。

13. 指令修饰符

Vue 的指令修饰符是对指令功能的扩展,让指令能更灵活地处理场景,以常见指令(如 v-onv-bind 等)的修饰符为例说明:

(1)v-on 修饰符

.stop

阻止事件冒泡。在嵌套的 DOM 元素事件中,点击子元素时,父元素的同名事件默认会被触发(事件冒泡机制),用 .stop 可阻断冒泡:

<div id="app"><div @click="parentClick">父元素<button @click.stop="childClick">子按钮</button></div>
</div>
<script>
new Vue({el: '#app',methods: {parentClick() {console.log('父元素点击事件触发');},childClick() {console.log('子按钮点击事件触发');}}
});
</script>

点击子按钮,只会触发 childClick,父元素的 parentClick 不会因冒泡执行。

.prevent

阻止默认事件,比如阻止表单提交的默认跳转、链接的默认跳转等:

<!-- 阻止表单默认提交(跳转页面等行为) -->
<form @submit.prevent="handleSubmit"><button type="submit">提交</button>
</form>
<!-- 阻止a标签默认跳转 -->
<a href="https://www.example.com" @click.prevent="linkClick">点我</a>
<script>
new Vue({el: '#app',methods: {handleSubmit() {console.log('表单提交逻辑执行,不会跳转');},linkClick() {console.log('链接点击,不会跳转');}}
});
</script>
.capture

让事件采用捕获模式触发。默认事件是冒泡模式(从目标元素向父级传播),捕获模式则是从父级到目标元素方向触发:

<div id="app"><div @click.capture="parentCapture">父元素<div @click="childClick">子元素</div></div>
</div>
<script>
new Vue({el: '#app',methods: {parentCapture() {console.log('父元素捕获阶段触发');},childClick() {console.log('子元素点击触发');}}
});
</script>

点击子元素时,会先触发父元素的 parentCapture(捕获阶段),再触发子元素的 childClick

.self

只有事件目标是当前元素本身时,才触发事件。可避免因子元素冒泡导致父元素事件误触发:

<div id="app"><div @click.self="parentSelfClick">父元素<button @click="childClick">子按钮</button></div>
</div>
<script>
new Vue({el: '#app',methods: {parentSelfClick() {console.log('父元素自身被点击才触发');},childClick() {console.log('子按钮点击触发');}}
});
</script>

点击子按钮,父元素的 parentSelfClick 不会触发;只有点击父元素的非子元素区域,才会触发。

.once

事件只会触发一次,后续点击等操作不再响应:

<button @click.once="onceClick">点击一次</button>
<script>
new Vue({el: '#app',methods: {onceClick() {console.log('只会触发一次');}}
});
</script>

第一次点击按钮执行 onceClick,之后再点击无效。

按键修饰符(如 .enter.esc 等 )

针对键盘事件(keyupkeydown 等),只在按下特定按键时触发事件:

<input type="text" @keyup.enter="enterHandler">
<input type="text" @keydown.esc="escHandler">
<script>
new Vue({el: '#app',methods: {enterHandler() {console.log('按下回车键触发');},escHandler() {console.log('按下Esc键触发');}}
});
</script>

输入框中,按下 Enter 触发 enterHandler,按下 Esc 触发 escHandler ,常用的按键修饰符还有 .tab.delete(删除键或退格键 )、.space 等,也可自定义按键别名 。

(2)v-bind 修饰符

.prop

强制将绑定的值作为 DOM 属性 而非 HTML 属性。在有些场景下,DOM 属性和 HTML 属性行为不同,需明确区分时使用。

HTML 属性(attribute)是元素初始渲染时设置的“原始值”,DOM 属性(property)是 JavaScript 中操作 DOM 元素时实际生效的属性,部分属性名、行为有差异(如 value 对应输入框初始值,defaultValue 是 DOM 属性;class 是 HTML 属性,className 是 DOM 属性等 )。

示例:区分 DOM 属性与 HTML 属性

<!-- 场景:设置 input 的默认值(DOM 的 defaultValue 属性) -->
<input v-bind:default-value.prop="defaultInputValue"> 
<script>
new Vue({el: '#app',data: {defaultInputValue: '初始默认值'}
});
</script>

若不加 .prop,Vue 会尝试把 default-value 当作 HTML 属性设置(但 HTML 中无 default-value 属性,实际无效 );加 .prop 后,Vue 会明确设置 DOM 的 defaultValue 属性,让输入框初始值正确生效。

.camel

将连字符命名的属性名转为驼峰命名。HTML 属性名不区分大小写,且习惯用连字符(如 stroke-width ),但在 JavaScript 的 DOM 属性中,对应驼峰命名(strokeWidth )。使用 .camel 可自动转换命名风格。

示例:SVG 图形属性绑定

<svg width="100" height="100"><!-- 绑定 SVG 的 stroke-width 属性(连字符命名) --><line x1="0" y1="0" x2="100" y2="100" v-bind:stroke-width.camel="lineWidth" stroke="black"/>
</svg>
<script>
new Vue({el: '#app',data: {lineWidth: 2 // 对应 DOM 属性 strokeWidth}
});
</script>

不加 .camel 时,stroke-width 会被当作普通 HTML 属性,可能无法正确映射到 DOM 的 strokeWidth 属性;加 .camel 后,Vue 自动转为驼峰命名,确保属性正确设置。


14. v-bind 操作 class

v-bind:class(简写 :class )可动态操作元素的 CSS 类,支持对象语法数组语法,让类名根据数据动态添加/移除。

(1)对象语法

对象的键是类名,值是布尔值,决定是否添加该类。

示例:动态切换样式类

<style>
.active { color: red; font-weight: bold; }
.disabled { opacity: 0.5; cursor: not-allowed; }
</style><div id="app"><button :class="{ active: isActive, disabled: isDisabled }"@click="toggleActive">操作按钮</button>
</div>
<script>
new Vue({el: '#app',data: {isActive: true,isDisabled: false},methods: {toggleActive() {this.isActive = !this.isActive;this.isDisabled = !this.isDisabled;}}
});
</script>
  • isActivetrue 时,按钮添加 active 类;
  • isDisabledtrue 时,按钮添加 disabled 类;
    点击按钮可动态切换类,实现样式变化。

(2)数组语法

直接通过数组指定要添加的类名,类名可动态拼接(结合三元表达式、变量等 )。

示例:根据条件动态选类

<style>
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
</style><div id="app"><p :class="[status === 'success' ? 'success' : status === 'error' ? 'error' : 'warning']">{{ status }} 提示</p><button @click="changeStatus">切换状态</button>
</div>
<script>
new Vue({el: '#app',data: {status: 'success'},methods: {changeStatus() {const statusList = ['success', 'error', 'warning'];const currentIndex = statusList.indexOf(this.status);this.status = statusList[(currentIndex + 1) % statusList.length];}}
});
</script>

根据 status 变量的值,动态选择对应的类名,改变文本颜色。

(3)对象语法 + 数组语法 混用

复杂场景下,可同时用对象语法和数组语法,灵活组合静态类与动态类。

示例:混合使用语法

<style>
.base { font-size: 16px; }
.highlight { background: yellow; }
.theme-dark { color: #333; }
</style><div id="app"><div :class="['base', // 静态类{ highlight: isHighlight, 'theme-dark': isDarkMode } // 动态类对象]">混合语法示例文本</div><button @click="toggleHighlight">切换高亮</button><button @click="toggleDarkMode">切换深色模式</button>
</div>
<script>
new Vue({el: '#app',data: {isHighlight: false,isDarkMode: false},methods: {toggleHighlight() {this.isHighlight = !this.isHighlight;},toggleDarkMode() {this.isDarkMode = !this.isDarkMode;}}
});
</script>
  • base 是固定添加的静态类;
  • highlighttheme-dark 按需动态添加,实现更灵活的样式控制。

15. v-model 应用于其他表单元素

除文本输入框,v-model 还支持 单选框、复选框、下拉框 等表单元素,自动关联数据与视图。

(1)单选框(Radio)

v-model 绑定选中项的 value,同一组单选框通过 name 隐式分组(或直接靠 v-model 管理互斥 )。

示例:性别选择

<div id="app"><label>男:</label><input type="radio" value="male" v-model="gender"><label>女:</label><input type="radio" value="female" v-model="gender"><p>你选择的性别:{{ gender }}</p>
</div>
<script>
new Vue({el: '#app',data: {gender: 'male' // 默认选中}
});
</script>

点击不同单选框,gender 自动更新为对应 value,保证同一时间仅一个选中。

(2)复选框(Checkbox)

  • 单个复选框v-model 绑定布尔值(true/false ),控制是否选中。
  • 多个复选框v-model 绑定数组,存储选中项的 value

示例:单个复选框(协议勾选)

<div id="app"><input type="checkbox" v-model="isAgree"> 同意协议<button :disabled="!isAgree">下一步</button>
</div>
<script>
new Vue({el: '#app',data: {isAgree: false}
});
</script>

勾选后,isAgree 变为 true,按钮可用;取消勾选则反之。

示例:多个复选框(兴趣选择)

<div id="app"><label>篮球:</label><input type="checkbox" value="basketball" v-model="hobbies"><label>足球:</label><input type="checkbox" value="football" v-model="hobbies"><label>游泳:</label><input type="checkbox" value="swimming" v-model="hobbies"><p>你的爱好:{{ hobbies.join(', ') }}</p>
</div>
<script>
new Vue({el: '#app',data: {hobbies: []}
});
</script>

选中复选框,value 自动加入 hobbies 数组;取消选中则移除。

(3)下拉框(Select)

  • 单选下拉v-model 绑定选中项的 valueoptionvalue,无 value 则取文本 )。
  • 多选下拉:给 selectmultiplev-model 绑定数组,存储选中项 value

示例:单选下拉(城市选择)

<div id="app"><select v-model="city"><option value="beijing">北京</option><option value="shanghai">上海</option><option value="guangzhou">广州</option></select><p>你选择的城市:{{ city }}</p>
</div>
<script>
new Vue({el: '#app',data: {city: 'beijing' // 默认选中}
});
</script>

选择下拉项,city 自动更新为对应 value

示例:多选下拉(城市选择)

<div id="app"><select v-model="cities" multiple><option value="beijing">北京</option><option value="shanghai">上海</option><option value="guangzhou">广州</option></select><p>你选择的城市:{{ cities.join(', ') }}</p>
</div>
<script>
new Vue({el: '#app',data: {cities: []}
});
</script>

按住 Ctrl(Windows)/Command(Mac)多选,选中项 value 存入 cities 数组。


16. 计算属性

计算属性(computed )是基于已有数据动态派生新数据的方式,依赖数据变化时自动更新,且有缓存机制(依赖不变时,直接复用结果,提升性能 )。

(1)基本用法

示例:全名拼接

<div id="app"><input v-model="firstName" placeholder=""><input v-model="lastName" placeholder=""><p>全名:{{ fullName }}</p>
</div>
<script>
new Vue({el: '#app',data: {firstName: '张',lastName: '三'},computed: {fullName() {// 依赖 firstName 和 lastNamereturn this.lastName + this.firstName; }}
});
</script>
  • fullName 依赖 firstNamelastName,任一变化时,fullName 自动重新计算;
  • 若无计算属性,需在模板写表达式(如 {{ lastName + firstName }} ),但模板中逻辑复杂时难维护,计算属性让逻辑更清晰。

(2)缓存机制

计算属性会缓存结果,依赖不变时,多次访问直接用缓存,避免重复计算。

示例:模拟耗时计算

<div id="app"><p>计算结果:{{ expensiveResult }}</p><button @click="noop">无操作(触发重新渲染)</button>
</div>
<script>
new Vue({el: '#app',data: {baseNum: 10},computed: {expensiveResult() {console.log('计算属性执行...'); // 仅依赖变化时打印// 模拟耗时计算(如循环、复杂逻辑)let result = 0;for (let i = 0; i < 1000000; i++) {result += this.baseNum;}return result;}},methods: {noop() {// 空方法,仅触发视图重新渲染}}
});
</script>
  • 初始加载或修改 baseNum 时,expensiveResult 重新计算,控制台打印;
  • 点击“无操作”按钮(仅触发重新渲染),expensiveResult 依赖未变,直接用缓存,控制台不重复打印,体现缓存优化。

(3)与方法的区别

计算属性 vs 方法(methods ):

  • 计算属性:有缓存,依赖不变时复用结果,适合基于已有数据派生新数据的场景。
  • 方法:无缓存,每次调用都重新执行,适合需主动触发、不依赖响应式数据的逻辑。

示例:对比两者

<div id="app"><p>计算属性:{{ reversedMsgComputed }}</p><p>方法:{{ reversedMsgMethod() }}</p><button @click="noop">无操作(触发重新渲染)</button>
</div>
<script>
new Vue({el: '#app',data: {message: 'Hello Vue'},computed: {reversedMsgComputed() {console.log('计算属性执行');return this.message.split('').reverse().join('');}},methods: {reversedMsgMethod() {console.log('方法执行');return this.message.split('').reverse().join('');},noop() {}}
});
</script>
  • 初始加载:两者都执行,控制台打印;
  • 点击“无操作”:计算属性因依赖未变,不执行;方法每次调用都执行,控制台重复打印。

17. 计算属性的完整写法

计算属性默认是“getter”(仅获取值 ),也可显式定义 getter + setter,支持手动修改计算属性的值(触发 setter 逻辑 )。

(1)完整语法(get + set)

computed: {// 计算属性名fullName: { // 获取值(默认触发)get() { return this.lastName + this.firstName;},// 设置值(手动修改时触发)set(newValue) { // 解析 newValue,更新依赖数据const [last, first] = newValue.split(' '); this.lastName = last;this.firstName = first;}}
}

示例:双向操作计算属性

<div id="app"><input v-model="firstName" placeholder=""><input v-model="lastName" placeholder=""><!-- 绑定计算属性 --><input v-model="fullName" placeholder="全名"> <p>firstName: {{ firstName }}</p><p>lastName: {{ lastName }}</p>
</div>
<script>
new Vue({el: '#app',data: {firstName: '张',lastName: '三'},computed: {fullName: {get() {return this.lastName + this.firstName;},set(newValue) {// 假设输入格式为“姓 名”,拆分更新数据const parts = newValue.split(' ');if (parts.length === 2) {this.lastName = parts[0];this.firstName = parts[1];}}}}
});
</script>
  • 修改 firstNamelastNamefullNameget 触发,自动更新;
  • 修改 fullName 输入框:set 触发,按规则拆分值并更新 firstNamelastName,实现计算属性的“双向绑定”。

(2)使用场景

手动修改计算属性时(如表单回显、复杂数据同步 ),用完整写法。若仅需“获取”,用简写的 getter 即可。


18. watch 监视器

watch 用于监听响应式数据变化,执行自定义逻辑(如异步请求、复杂操作 )。

(1)基本用法

示例:监听单个数据

<div id="app"><input v-model="username" placeholder="输入用户名"><p>用户名状态:{{ usernameStatus }}</p>
</div>
<script>
new Vue({el: '#app',data: {username: '',usernameStatus: '' // 用于显示用户名校验状态},watch: {// 监听 username 数据变化username(newVal, oldVal) {console.log(`用户名从 "${oldVal}" 变为 "${newVal}"`);// 模拟异步校验(如调用后端接口)if (newVal.length < 3) {this.usernameStatus = '用户名长度不能少于3位';} else {this.usernameStatus = '用户名可用';}}}
});
</script>
  • username 变化时,watch 回调自动执行,参数 newVal 是变化后的值,oldVal 是变化前的值;
  • 适合处理数据变化后的异步操作(如接口请求、定时器等),这是计算属性难以直接实现的场景。

(2)监听对象属性

若需监听对象内部属性的变化,需用 字符串路径深度监听deep: true )。

① 字符串路径(监听单个属性)
<div id="app"><input v-model="user.name" placeholder="输入姓名"><p>姓名变化日志:{{ nameLog }}</p>
</div>
<script>
new Vue({el: '#app',data: {user: { name: '' },nameLog: ''},watch: {// 用字符串路径监听 user 对象的 name 属性'user.name'(newVal, oldVal) {this.nameLog = `姓名从 "${oldVal}" 改为 "${newVal}"`;}}
});
</script>
② 深度监听(监听对象所有属性)

若对象多个属性可能变化,且需统一处理,可开启 deep: true

<div id="app"><input v-model="user.name" placeholder="姓名"><input v-model="user.age" type="number" placeholder="年龄"><p>用户信息变化:{{ userLog }}</p>
</div>
<script>
new Vue({el: '#app',data: {user: { name: '', age: '' },userLog: ''},watch: {user: {handler(newVal, oldVal) {// newVal 和 oldVal 是变化后的完整 user 对象this.userLog = `姓名:${newVal.name},年龄:${newVal.age}`;},deep: true // 开启深度监听,对象任一属性变化都会触发}}
});
</script>
  • handler 是监听的核心回调函数,deep: true 表示递归监听对象内部所有属性;
  • 注意:深度监听会消耗更多性能,仅在必要时使用(如对象属性频繁变化且需统一响应)。

(3)监听数组

Vue 能自动监听数组的 变异方法(如 pushpopsplice 等),无需额外配置;但直接修改数组索引或长度时,需用 Vue.set 或开启深度监听。

① 监听数组变异方法
<div id="app"><button @click="addHobby">添加爱好</button><p>爱好列表:{{ hobbies.join(', ') }}</p><p>变化日志:{{ hobbyLog }}</p>
</div>
<script>
new Vue({el: '#app',data: {hobbies: [],hobbyLog: ''},methods: {addHobby() {const newHobby = ['篮球', '游泳', '阅读'][Math.floor(Math.random() * 3)];this.hobbies.push(newHobby); // 调用数组变异方法}},watch: {hobbies(newVal) {this.hobbyLog = `新增爱好:${newVal[newVal.length - 1]}`;}}
});
</script>
② 监听数组索引修改(需 Vue.set 或深度监听)

直接修改索引(如 this.hobbies[0] = '足球' )无法触发 watch,需用 Vue.set

<script>
new Vue({el: '#app',data: {hobbies: ['篮球'],hobbyLog: ''},methods: {updateFirstHobby() {// 用 Vue.set 修改数组索引,确保触发响应式Vue.set(this.hobbies, 0, '足球');}},watch: {hobbies(newVal) {this.hobbyLog = `第一个爱好改为:${newVal[0]}`;}}
});
</script>

19. watch 完整写法

watch 的完整配置包含 handler(核心回调)、deep(深度监听)、immediate(初始触发),适用于复杂场景。

(1)完整配置项说明

配置项类型作用
handler函数数据变化时执行的回调,接收 newVal(新值)和 oldVal(旧值)两个参数
deep布尔值是否深度监听(对象/数组内部属性变化是否触发),默认 false
immediate布尔值是否在初始化时立即执行一次 handler,默认 false(仅数据变化时触发)

(2)示例:完整配置的使用

场景:初始化时加载用户信息,且监听用户信息变化时重新请求数据。

<div id="app"><input v-model="user.name" placeholder="姓名"><input v-model="user.age" type="number" placeholder="年龄"><p>用户信息:{{ userInfo }}</p>
</div>
<script>
new Vue({el: '#app',data: {user: { name: '张三', age: 25 },userInfo: '' // 存储格式化后的用户信息},watch: {user: {// 核心回调函数handler(newVal) {this.userInfo = `姓名:${newVal.name},年龄:${newVal.age}`;},deep: true, // 深度监听用户对象immediate: true // 初始化时立即执行一次,加载初始信息}}
});
</script>
  • 页面加载时,immediate: truehandler 立即执行,userInfo 显示初始用户信息;
  • 修改 user.nameuser.age 时,deep: true 触发 handleruserInfo 实时更新。

(3)注意事项

  1. 避免滥用深度监听:深度监听会遍历对象所有属性,对象复杂时影响性能,优先用字符串路径监听单个属性;

  2. immediate 初始化触发:此时 oldValundefined(初始化无旧值),需在回调中处理边界情况;

  3. 注销监听:组件销毁时,Vue 会自动注销 watch,无需手动清理;但手动创建的监听(如 vm.$watch )需手动注销:

    // 手动创建监听,返回注销函数
    const unwatch = this.$watch('user.name', (newVal) => {console.log('姓名变化:', newVal);
    });
    // 组件销毁前注销监听
    this.$on('hook:beforeDestroy', () => {unwatch();
    });
    

20. 生命周期

Vue 实例从创建到销毁的整个过程称为生命周期,Vue 提供了多个生命周期钩子函数,允许开发者在不同阶段执行自定义逻辑(如初始化数据、操作 DOM、清理资源等)。

(1)生命周期阶段与钩子函数

Vue2 生命周期分为 创建、挂载、更新、销毁 四个阶段,核心钩子函数如下:

阶段钩子函数执行时机常用场景
创建阶段beforeCreate实例创建前,datamethods 等未初始化几乎不用(无可用数据和方法)
created实例创建完成,datamethods 已初始化,但 DOM 未挂载($el 不存在)初始化数据(如调用接口获取初始数据)、绑定事件监听
挂载阶段beforeMount挂载前,模板已编译,但未渲染到 DOM($el 已存在但未挂载到页面)查看编译后的模板,不操作 DOM
mounted挂载完成,DOM 已渲染到页面($el 存在且挂载)操作 DOM(如获取元素、初始化第三方插件)、发起 DOM 相关请求
更新阶段beforeUpdate数据变化后,DOM 更新前查看数据变化前的状态
updated数据变化后,DOM 更新完成操作更新后的 DOM(如重新计算元素位置)
销毁阶段beforeDestroy实例销毁前,datamethods 仍可用清理资源(如清除定时器、注销事件监听、销毁第三方插件实例)
destroyed实例销毁完成,datamethods 不可用几乎不用(实例已销毁,无可用资源)

(2)示例:生命周期钩子的使用

<div id="app"><p>计数器:{{ count }}</p><button @click="count++">增加</button><button @click="destroyVm">销毁实例</button>
</div>
<script>
const vm = new Vue({el: '#app',data: {count: 0,timer: null // 存储定时器},// 创建阶段beforeCreate() {console.log('beforeCreate:', this.count, this.increment); // 输出:undefined undefined(data、methods 未初始化)},created() {console.log('created:', this.count, this.increment); // 输出:0 函数(data、methods 已初始化)// 初始化定时器(示例:每2秒增加count)this.timer = setInterval(() => {this.count++;}, 2000);},// 挂载阶段beforeMount() {console.log('beforeMount:', document.querySelector('p').textContent); // 输出:{{ count }}(DOM 未渲染)},mounted() {console.log('mounted:', document.querySelector('p').textContent); // 输出:计数器:0(DOM 已渲染)},// 更新阶段beforeUpdate() {console.log('beforeUpdate(数据):', this.count);console.log('beforeUpdate(DOM):', document.querySelector('p').textContent);// 数据已变,DOM 未更新(如 count=1 时,DOM 仍显示 0)},updated() {console.log('updated(数据):', this.count);console.log('updated(DOM):', document.querySelector('p').textContent);// 数据和 DOM 都已更新(如 count=1 时,DOM 显示 1)},// 销毁阶段beforeDestroy() {console.log('beforeDestroy:', this.count); // 输出当前 count(data 仍可用)// 清理资源:清除定时器clearInterval(this.timer);},destroyed() {console.log('destroyed:', this.count); // 输出当前 count(但实例已销毁,操作无意义)},methods: {increment() {this.count++;},destroyVm() {// 手动销毁 Vue 实例this.$destroy();}}
});
</script>

(3)核心钩子函数使用场景总结

  1. created:优先处理数据初始化(如接口请求),此时 DOM 未挂载,不可操作 DOM;
  2. mounted:唯一可安全操作 DOM 的钩子(如初始化地图、图表等第三方插件);
  3. beforeDestroy:必须在此清理资源(定时器、事件监听、WebSocket 连接等),避免内存泄漏;
  4. 更新阶段钩子:尽量避免在 updated 中修改数据(可能导致无限循环),仅用于 DOM 操作。

21. 初始化渲染和获取焦点

初始化渲染是 Vue 实例挂载后将模板渲染为 DOM 的过程;获取焦点是常见的 DOM 操作需求(如页面加载后让输入框自动聚焦),需在 DOM 渲染完成后执行。

(1)初始化渲染的核心逻辑

Vue 初始化渲染流程:

  1. 解析模板(将 {{ }}、指令等转换为渲染函数);
  2. 执行渲染函数,生成虚拟 DOM;
  3. 将虚拟 DOM 转换为真实 DOM,挂载到 el 指定的容器中(mounted 钩子触发时完成)。

示例:初始化渲染文本和指令

<div id="app"><h1>{{ title }}</h1><p v-if="showDesc">{{ desc }}</p><input v-model="inputVal" placeholder="输入内容">
</div>
<script>
new Vue({el: '#app',data: {title: 'Vue 初始化渲染',showDesc: true,desc: '页面加载时自动渲染此文本',inputVal: ''},mounted() {console.log('初始化渲染完成,DOM 可用');}
});
</script>
  • 页面加载后,titledesc 自动渲染到 DOM,v-if="showDesc" 控制 p 标签显示,v-model 绑定输入框;
  • mounted 触发时,所有 DOM 已渲染完成。

(2)初始化获取焦点(基础方式)

获取焦点需操作 DOM,必须在 mounted 钩子中执行(DOM 已挂载),通过 document.querySelectorthis.$el 获取元素。

示例:输入框初始化自动聚焦

<div id="app"><input type="text" id="username" placeholder="自动聚焦">
</div>
<script>
new Vue({el: '#app',mounted() {// 方式1:通过 document 获取元素const input = document.getElementById('username');input.focus(); // 触发聚焦// 方式2:通过 this.$el 获取组件根元素,再查找子元素// const input = this.$el.querySelector('#username');// input.focus();}
});
</script>
  • mounted 中,document.getElementById('username') 能正确获取输入框元素,调用 focus() 实现自动聚焦;
  • 若输入框是动态渲染的(如 v-if 控制显示),需确保元素已存在(可在 updated 中处理,或用 nextTick )。

(3)动态元素的焦点获取(nextTick

若元素通过 v-ifv-for 等动态渲染,mounted 中可能尚未生成该元素,需用 this.$nextTick 确保元素渲染完成后再执行聚焦。

示例:动态显示的输入框获取焦点

<div id="app"><button @click="showInput = true">显示输入框</button><input v-if="showInput" ref="dynamicInput" placeholder="动态聚焦">
</div>
<script>
new Vue({el: '#app',data: {showInput: false},methods: {showAndFocus() {this.showInput = true;// 错误:此时 input 尚未渲染,focus 无效// this.$refs.dynamicInput.focus();// 正确:nextTick 等待 DOM 更新完成this.$nextTick(() => {this.$refs.dynamicInput.focus();});}},// 初始化时自动显示并聚焦(可选)mounted() {this.showAndFocus();}
});
</script>
  • this.$nextTick 的回调会在 DOM 更新循环完成后执行,确保 v-if="showInput" 渲染的输入框已存在;
  • ref 引用元素(ref="dynamicInput"),通过 this.$refs.dynamicInput 获取元素,比 document 更灵活(组件内有效)。

22. mounted 获取焦点

mounted 是 Vue 实例挂载完成的钩子,此时 DOM 已完全渲染,是执行 DOM 操作(如获取焦点)的最佳时机。本节详细说明 mounted 中获取焦点的多种场景和最佳实践。

(1)通过 ref 获取元素(推荐)

ref 是 Vue 提供的引用机制,用于在组件内快速获取 DOM 元素或子组件,比 document 更简洁、更具组件封装性。

示例:ref 实现输入框聚焦

<div id="app"><!-- 用 ref 标记输入框 --><input type="text" ref="usernameInput" placeholder="请输入用户名"><input type="password" ref="passwordInput" placeholder="请输入密码">
</div>
<script>
new Vue({el: '#app',mounted() {// 通过 this.$refs 获取输入框并聚焦this.$refs.usernameInput.focus();// 延迟聚焦到密码框(示例:2秒后切换焦点)setTimeout(() => {this.$refs.passwordInput.focus();}, 2000);}
});
</script>
  • ref="usernameInput" 给输入框添加引用标识,this.$refs.usernameInput 直接获取该元素;
  • 优点:不依赖 idclass(避免全局冲突),仅在当前组件内有效,适合组件化开发。

(2)多个动态元素的焦点管理

若页面有多个动态生成的输入框(如 v-for 渲染的列表),需通过 ref 数组和索引控制焦点。

示例:列表输入框的焦点切换

<div id="app"><div v-for="(item, index) in inputs" :key="index"><!-- ref 绑定为数组,存储所有输入框 --><input type="text" :ref="`input-${index}`" :placeholder="`输入框 ${index + 1}`"@keyup.enter="focusNext(index)"></div><button @click="addInput">添加输入框</button>
</div>
<script>
new Vue({el: '#app',data: {inputs: [{}, {}, {}] // 初始3个输入框},mounted() {// 初始化聚焦第一个输入框this.$refs['input-0'].focus();},methods: {// 按 Enter 键聚焦下一个输入框focusNext(index) {const nextIndex = index + 1;// 检查下一个输入框是否存在if (this.$refs[`input-${nextIndex}`]) {this.$refs[`input-${nextIndex}`].focus();} else {// 最后一个输入框按 Enter 时添加新输入框并聚焦this.addInput(nextIndex);}},// 添加新输入框并聚焦addInput(focusIndex) {this.inputs.push({});// 等待 DOM 更新后聚焦新输入框this.$nextTick(() => {this.$refs[`input-${focusIndex}`].focus();});}}
});
</script>
  • :ref="input-index‘"‘动态生成引用名(如‘input−0‘、‘input−1‘),‘this.{index}`"` 动态生成引用名(如 `input-0`、`input-1` ),`this.index‘"‘动态生成引用名(如input0‘input1‘),this.refs` 存储为对象;
  • Enter 键时,focusNext 方法切换到下一个输入框,最后一个输入框按 Enter 时自动添加新输入框并聚焦。

(3)第三方组件的焦点控制

若使用第三方 UI 组件(如 Vue 插件的输入框),需通过组件暴露的方法或 $el 获取底层 DOM 元素。

示例:第三方输入框组件聚焦

<!-- 假设使用第三方输入框组件 <custom-input> -->
<div id="app"><custom-input ref="customInput" placeholder="第三方组件输入框"></custom-input>
</div>
<script>
// 模拟第三方组件
Vue.component('custom-input', {template: `<div class="custom-input"><input type="text" :value="value" @input="$emit('input', $event.target.value)"></div>`,props: ['value']
});new Vue({el: '#app',mounted() {// 方式1:通过组件的 $el 获取底层 DOM 元素const input = this.$refs.customInput.$el.querySelector('input');input.focus();// 方式2:若组件暴露 focus 方法,直接调用(推荐)// this.$refs.customInput.focus();}
});
</script>
  • 第三方组件通常会封装 DOM 结构,需通过 this.$refs.customInput.$el 获取组件根元素,再查找内部输入框;
  • 若组件提供了 focus 等方法(如 Element UI 的 el-input ),直接调用 this.$refs.customInput.focus() 更优雅,避免依赖内部 DOM 结构。

(4)注意事项

  1. mounted 中确保元素存在:若元素通过 v-if="false" 初始隐藏,mounted 中无法获取,需在 v-if 变为 true 后(如 updatednextTick )执行聚焦;
  2. 避免频繁操作 DOM:焦点切换等操作尽量通过用户交互触发,避免在 mounted 中执行大量 DOM 操作;
  3. 组件销毁时清理:若聚焦逻辑依赖定时器等,需在 beforeDestroy 中清理,避免内存泄漏。
http://www.dtcms.com/a/354113.html

相关文章:

  • CVPR深度学习研究指南:特征提取模块仍是论文创新难点
  • 吴恩达机器学习作业二:线性可分逻辑回归
  • CMake构建学习笔记21-通用的CMake构建脚本
  • Liunx内核驱动
  • Java中StringBuilder原理以及使用
  • D4145低功耗GFCI接地故障控制芯片介绍
  • 题目—移除元素
  • 作业帮,途虎养车,得物,途游游戏,三七互娱,汤臣倍健,游卡,快手26届秋招内推
  • JUC多线程个人笔记
  • 【DC工具GUI入门】
  • APP测试全流程以及测试点
  • 【开题答辩全过程】以 基于SpringBoot的流浪动物领养系统的设计与实现 为例,包含答辩的问题和答案
  • 从Java到Go:初遇Go语言的震撼体验
  • 力扣 30 天 JavaScript 挑战 第41天 (第十二题)对异步操作,promise,async/await有了更深理解
  • 【Linux实时内核机制】ww_rt_mutex 的contending_lock异常问题
  • android/java中主线程和子线程的详解
  • Nano Banana揭秘:Google Gemini 2.5 Flash Image正式发布 | AI图像编辑新时代
  • 内网应用如何实现外网访问?外地通过公网地址访问内网服务器的设置方法
  • 动态规划:青蛙跳台阶实践
  • H20 性能表现之 Kimi-K2
  • 【git】:gitee项目管理vs2019
  • 装饰器进阶与设计模式
  • Linux入门教程 第十五章 Linux 系统调优工具
  • 【工具篇】github/huggingface 镜像源总结
  • 嵌入式系统学习Day24(线程)
  • Custom SRP - Shadow Masks
  • Axure:如何将SVG转换为形状
  • leetcode 155 官方golang标准答案错误
  • Java Lambda 处理日期时间 根据区间找出区间内集合
  • Linux程序与进程:核心概念与管理全解析