Vue2 于 2023年12月31日停止维护,官网:https://v2.cn.vuejs.org/
一、Vue 是什么
Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。渐进式可以理解为 Vue 可以自底向上逐层应用,从一个轻量小巧的核心库逐渐递进到可以引入各种 Vue 插件。
二、Vue 的特点
1、采用组件化模式,提高代码复用率,且让代码更好维护。
2、声明式渲染,Vue 的核心是一个允许采用模板语法来声明式地将数据渲染进 DOM 的系统。让编码人员无需直接操作 DOM,提高开发效率。
3、使用虚拟 DOM 和 Diff 算法,尽量复用 DOM 节点。
三、Vue 实例和容器只能一一对应
一个 Vue 应用由一个通过new Vue创建的根实例,以及可选的嵌套的、可复用的组件树组成。所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象(一些根实例特有的选项除外)。
四、Vue 模板语法
1、插值表达式:用于解析标签体内容,{{data}}。
2、指令语法:用于解析标签,例如v-bind:href="xxx"。
五、Vue 内置指令
1、v-bind:单向数据绑定,数据变化能改变视图,但视图变化不能改变数据。
2、v-model:双向数据绑定,只能应用在表单类元素上。v-model在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
(1)text 和 textarea 元素使用value property 和input事件。
(2)checkbox 和 radio 使用checked property 和change事件。
(3)select 字段将value作为 prop 并将change作为事件。
v-model:value可简写为v-model,因为v-model默认收集的是 value 值。v-model的修饰符:.number(输入值转为数值类型)、.lazy(在 “change” 时而非 “input” 时更新)、.trim(自动过滤输入的首尾空白字符)。
<!-- 文本 -->
<input v-model="message"></input>
<!-- 多行文本 -->
<textarea v-model="message"></textarea>
<!-- 单个复选框,绑定到布尔值 -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<!-- 多个复选框,绑定到同一个数组 -->
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
<!-- 单选按钮 -->
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
<!-- 选择框 -->
<select v-model="selected">
...
</select>
3、v-on:用于监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
4、条件渲染指令:v-show、v-if、v-else-if、v-else,<template>标签不影响 HTML 结构,但只能和v-if使用,v-show不支持<template>和v-else。
5、列表渲染指令:v-for,可遍历数组、对象、字符串、数字,可以在<template>标签上使用v-for。v-for具有比v-if更高的优先级,不推荐一起使用。
6、v-text:渲染普通文本。
7、v-html:更新元素的innerHTML,有安全性问题,容易导致 XSS 攻击。
8、v-once:元素或组件只渲染一次。
9、v-slot:提供具名插槽或需要接收 prop 的插槽。
<!-- 具名插槽 -->
<base-layout>
<template v-slot:header>
Header content
</template>
Default slot content
<template v-slot:footer>
Footer content
</template>
</base-layout>
<!-- 接收 prop 的具名插槽 -->
<infinite-scroll>
<template v-slot:item="slotProps">
<div class="item">
{{ slotProps.item.text }}
</div>
</template>
</infinite-scroll>
<!-- 接收 prop 的默认插槽,使用了解构 -->
<mouse-position v-slot="{ x, y }">
Mouse position: {{ x }}, {{ y }}
</mouse-position>
10、v-cloak:这个指令保持在元素上直到关联实例结束编译。
11、v-pre:跳过这个元素和它的子元素的编译过程,加快编译。
12、自定义指令:
(1)定义语法:
// 局部指令
new Vue({
directives: { 指令名: 配置对象 }
directives () {} // 函数式
})
// 全局指令
Vue.directive(指令名, 配置对象)
Vue.directive(指令名, 回调函数)
(2)配置对象中常用的三个回调:
①bind:只调用一次,指令第一次绑定到元素时调用。
②inserted:指令所在元素被插入页面时调用。
③update:指令所在模板结构被重新解析时调用。
④componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
⑤unbind:只调用一次,指令与元素解绑时调用。
(3)钩子函数参数:

(4)注意事项:自定义指令里的 this 指向 window。指令名如果是多个单词,用kebab-case形式。
六、v-if 和 v-show 的区别
v-if是条件渲染指令,它根据表达式的真假来删除和插入元素;v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
v-show也是条件渲染指令,不管初始条件是什么,元素始终会被渲染到 HTML,它只是简单地为元素设置 CSS 的 style 属性。
一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
七、v-on 和 v-bind 的区别
v-bind用于设置 HTML 属性,如v-bind:class=”exp”、v-bind:href=”{{url}}”,缩写为 :class=”exp”、:href=”{{url}}”。
v-on用于绑定 HTML 事件,如v-on:click="get()",缩写为@click="get()",v-on 可以绑定多个方法。
八、v-text 和 v-html 的区别
v-text渲染普通文本,会覆盖原来的内容,不解析标签。
v-html输出真正的 HTML,能解析标签,使用v-html的元素内部的其它元素不显示。
<body>
<div id="app">
<p v-text="msg">不显示</p>
<p v-text="html"></p>
<p v-html="html"></p>
<div v-html="html">
哈哈哈哈!
<input type="color"> //都不显示
</div>
</div>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 'v-text显示内容',
html: '<strong>Hello</strong> Vue!'
}
});
</script>
</body>
v-text 显示效果:
v-html 显示效果:Hello Vue!
九、v-once 有什么作用?在哪些场景下可以使用 v-once?
v-once只渲染元素和组件一次,之后的渲染被当作静态内容跳过,用于优化更新性能。
<div id="app">
<p v-once>{{msg}}</p> // msg 不会改变
<p>{{msg}}</p> // msg 随 input 值动态变化
<input type="text" v-model="msg">
</div>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 'hello!'
}
});
</script>
十、v-for 中 key 值的作用
v-for="(item, index) in arr" // 遍历数组
v-for="(value, name, index) in object" // 遍历对象:值,键名,索引
v-for="n in 10" // 遍历数字
例子:选中一个复选框时添加一个对象
1、key 的作用主要是为了高效的更新虚拟 DOM。key 作为虚拟 DOM 对象的标识,当数据发生变化时,Vue 会根据“新数据”生成“新的虚拟 DOM”,随后 Vue 进行“新虚拟 DOM”与“旧虚拟 DOM”的差异对比。比较规则如下:
(1)旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key,对比发现新旧虚拟 DOM 中内容没变时直接使用之前的真实 DOM,也就是就地复用;对比发现内容变了,则生成新的真实 DOM,随后替换页面中之前的真实 DOM。
(2)旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key,则会创建新的真实 DOM,随后渲染到页面。
2、用index作为 key 值可能会引发的问题:若对数据进行增删改查等破坏顺序的操作,可能导致所有元素的 key 值发生改变,diff 算法无法关联到旧虚拟 DOM 中一样的数据,会产生没有必要的真实 DOM 更新。
十一、Class 与 Style 绑定
1、绑定 HTML Class:v-bind:class
(1)对象语法:可以在对象中传入更多字段来动态切换多个 class,v-bind:class指令可以与普通的 class 共存。
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
(2)数组语法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<!-- 在数组语法中使用对象语法 -->
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
(3)在组件上使用:
①对于只有一个根元素的组件,当使用了class attribute 时,这些 class 会被添加到根元素上,并与该元素上已有的 class 合并。
<!-- 子组件模板 -->
<p class="foo bar">Hi!</p>
<!-- 在使用组件时添加一些 class -->
<MyComponent class="baz boo" />
<!-- 渲染结果 -->
<p class="foo bar baz boo">Hi!</p>
②组件有多个根元素,需要通过组件的$attrs属性来指定哪个根元素接收这个 class。
<!-- MyComponent 模板使用 $attrs 时 -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
<MyComponent class="baz" />
<!-- 渲染结果 -->
<p class="baz">Hi!</p>
<span>This is a child component</span>
2、绑定内联样式:v-bind:style
(1)对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
(2)数组语法:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
(3)自动添加前缀:当v-bind:style使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue 会自动侦测并添加相应的前缀。
(4)多重值:从 2.3.0 起你可以为style绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
十二、Vue 实现双向绑定的原理
目前前端框架基本上都是采用 MVVM 模式(Model-View-ViewModel)实现双向绑定,MVVM 模式在于数据与视图保持同步,意思是说数据改变时会自动更新视图,视图发生变化时会更新数据。ViewModel是 Vue.js 的核心,它是一个 Vue 实例。Vue 实例是作用于某一个 HTML 元素上的。
Vue 采用的是数据劫持与发布订阅相结合的方式实现双向绑定。
数据劫持是利用Object.defineProperty()方法重新定义了对象的get和set操作来实现。

Object.defineProperty() 三个参数:要操作的对象,要定义或修改的对象属性名,属性描述符。
其中,属性描述符是一个对象,主要有两种形式:数据描述符和存取描述符(get、set)。
var obj = {};
var name;
Object.defineProperty(obj, "data", {
// 获取值
get: function () {
return name;
},
// 设置值
set: function (val) {
name = val;
}
});
obj.data = 'aaa'; //赋值调用 set
console.log(obj.data); //取值调用 get
Object.defineProperty() 里的其他属性:
let person = { name: "张三", sex: "男" };
Object.defineProperty(person, "age", {
value: 18, // 设置 age 属性的数据值为 18,默认值为 undefined
enumerable: true, // 控制属性是否可以枚举,默认值为 false
writable: true, // 控制属性是否可以被修改,默认值为 false
configurable: true // 控制属性是否可以被删除,默认值为 false
});
发布订阅模式实现:
- Observer 监听器:用来监听属性的变化通知订阅者
- Watcher 订阅者:收到属性变化,然后更新视图
- Compile 解析器:解析指令,初始化模板,绑定订阅者
参考文章:知乎-Vue双向绑定原理
十三、Vue 中的数据代理
1、数据代理:通过一个对象代理对另一个对象中的数据的操作(读/写)。
2、Vue 中的数据代理就是通过Object.defineProperty()把data 对象中所有的属性添加到vm上,且数据从data到vm中的过程,又为每一个属性添加上getter和setter方法,用户修改或者读取时,也是通过getter或setter方法访问_data,_data返回或操作data中的数据实现的。
十四、事件处理
<button @click="showInfo">不传参,默认参数有原生事件对象event</button>
<button @click="showInfo(66, $event)">传参,并传入原生事件对象event</button>
十五、事件修饰符
1、.stop:阻止事件冒泡。
2、.prevent:阻止默认事件,比如阻止 a 标签默认的跳转事件。
3、.capture:使用事件的捕获模式。
4、.self:只有 event.target 是当前操作的元素时才触发事件。
5、.once:事件只触发一次。
6、.passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
7、.native:监听组件根元素的原生事件。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联,要注意先后顺序 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 onScroll 完成 -->
<!-- 这其中包含 event.preventDefault() 的情况 -->
<!-- scroll 是滚动条事件,wheel 是滚轮事件 -->
<div v-on:scroll.passive="onScroll">...</div>
十六、按键修饰符
<!-- 只有在 key 是 Enter 时调用 vm.submit() -->
<input v-on:keyup.enter="submit">
1、Vue 中常用的按键码的别名:.enter、.tab(配合 keyup 不生效,要配合 keydown)、.delete(捕获“删除”和“退格”键)、.esc、.space、.up、.down、.left、.right。
2、Vue 未提供别名的按键,可以使用按键原始的 key 值去绑定,注意像 CapsLock 键要写为kebab-case形式:@keyup.caps-lock。
3、系统修饰键:.ctrl、.alt、.shift、.meta。
(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2)配合keydown使用:正常触发事件。
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
(3).exact修饰符:允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
(4)鼠标按钮修饰符:.left、.right、.middle,这些修饰符会限制处理函数仅响应特定的鼠标按钮。
4、自定义按键修饰符别名:Vue.config.keyCodes.自定义键名 = 键码。
十七、为什么在 HTML 中监听事件
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:
1、扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
2、因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
3、当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。