第11章 Vue¶
第 11 章¶
Vue¶
本章要学习的 Vue 是一套用于构建用户界面的渐进式框架,同时也是一个 JavaScript 框架。任何编程语言在最初的时候都是没有框架的,随着在实际开发过程中不断总结经验,积累最佳实践,慢慢地,人们发现很多特定场景下的特定问题总是可以套用固定的解决方案。于是有人把成熟的固定解决方案收集起来,整合在一起就成了框架。在使用框架的过程中,我们往往只需要告诉框架做什么(声明),而不需要关心框架怎么做(编程)。
对于 Java 程序来说,我们使用框架就是导入那些封装了固定解决方案的 jar 包,然后通过配置文件告诉框架做什么,从而大大简化编码,提高开发效率。例如,JUnit 其实就是一款单元测试框架。而对于 JavaScript 程序来说,我们使用框架就是导入那些封装了固定解决方案的 JS 文件,然后在框架的基础上编写代码实现业务逻辑。
Vue 框架提供了一套开发规则,按照这个开发规则可提高开发效率。Vue 框架是轻量级的,有很多独立的功能或库,使用 Vue 时可以根据项目需求来选用它的一些功能。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。本章主要介绍 Vue 的基本语法和生命周期,以及如何应用,如声明式渲染、条件渲染、列表渲染、事件驱动等。
11.1 Vue 简介与入门案例¶
Vue 的作者是尤雨溪,Vue 最早发布于 2014 年 2 月。作者在 Hacker News、Echo JS 与 Reddit 的 JavaScript 版块发布了最早的版本。一天之内,Vue 就登上了这三个网站的首页。Vue 是 Github 上最受欢迎的开源项目之一。
Vue 最初的目标是成为大型项目的一个良好补充,“渐进式框架” 的设计思想是为了遵循淡化框架本身的主张,从而降低框架作为工具的复杂度,以及对使用者的要求。
Vue 是为了实现前后端分离的开发理念,开发前端 SPA(single page web application)项目,实现数据绑定、路由配置、项目编译打包等一系列工作的技术框架。Vue 有著名的全家桶系列,包含了 vue-router、vuex、vue-resource,再加上构建工具 vue-cli、sass 样式,就是一个完整的 vue 项目的核心构成。概括起来就是项目构建工具、路由、状态管理、HTTP 请求工具。
由于 Vue.js 只聚焦视图层,本质上说是一个构建数据驱动的 Web 界面的库。Vue 通过简单的 API(应用程序编程接口)提供高效的数据绑定和灵活的组件系统。Vue 的特点如下。
Vue.js 的两大核心要素是数据驱动和组件化。数据驱动,简单来说就是修改绑定的数据(页面上依赖的数据)就能对应地更新视图(页面),这样一来极大地解放了 DOM 操作的工作,提高开发效率。组件化开发通常是将一个应用以一棵嵌套组件树的形式来组织,把页面按照页面功能(如导航、侧边栏、下拉框)拆分业务,每个组件代表一个独立的功能,从而大大提高了代码可维护性和复用性。
Vue 数据驱动采用 MVVM 模式,M(Model)指的是模型层,这里表示 JavaScript 对象,V(View)指的是视图层,这里表示 DOM(HTML 操作的元素),VM(ViewModel)指的是连接视图和数据的中间
件,Vue.js 就是 MVVM 中的 VM 层的实现者。Vue 与 MVVM 模式的关系如图 11-1 所示。
在 MVVM 架构中,是不允许数据和视图直接通信的,只能通过 VM 层来通信,而 VM 本质就是一个观察者。VM 能够观察到数据的变化,并对视图下对应的内容进行更新;还能够监听到视图的变化,并通知数据发生改变。综上,Vue.js 就是一个 MVVM 的实现者,它的核心就是实现了 DOM 监听和数据绑定。

下面通过入门案例,来简单了解一下 Vue 的使用。
Vue 是 JavaScript 的框架,使用 Vue 开发需要引入该框架的源码,Java 的框架源码是 JAR 包形式,那么 JS 框架的源码是什么类型的文件呢?答案一目了然,就是 JS 文件。接下来以 Vue2.7.14 为例进行演示。首先搭建 Vue 框架,具体步骤如下。
搭建完成后的 HTML 文件如下。
例如,创建一个 span 标签和一个按钮,单击按钮修改 span 标签的内容。接下来分别用 JavaScript 代码和 Vue 代码实现该案例效果。
使用 JavaScript 实现的代码如下。
查看页面效果,如图 11-2 所示。
单击该按钮,发现值发生变化,如图 11-3 所示。


使用 Vue 实现的代码如下。
查看页面单击按钮,同样能实现相同的效果。Vue 的实现原理是将 span 的标签体内容和 Vue 的数据模型做一个绑定关系,一旦绑定成功,要想操作 span 的内容,直接操作 msg 的值即可。显而易见,如果页面内有很多位置,都需要操作 span 内容时,Vue 代码比 JS 更方便。
11.2 模板语法¶
随着前端交互复杂度的不断提升,各种字符拼接、循环遍历、DOM 节点的操作也都复杂多变,当数据量大,交互频繁的时候,不论是从用户体验还是性能优化上,都会面临前端渲染的问题。
Vue 使用了一种基于 HTML 的模板语法,使我们能够声明式地将应用或组件实例的数据绑定到呈现的 DOM 上。模板中除了 HTML 的结构,还包含两个 Vue 模板的特有语法:插值语法和指令语法。插值语法只有一个功能,就是向标签体插入一个动态的值。指令语法用来操作所在的标签,比如指定动态属性值、绑定事件监听、控制显示隐藏等。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态数据变更时,Vue 能够自动更新需要变化的 DOM 节点,并做到最小化更新。
下面分别对插值和指令进行具体讲解。
11.2.1 插值语法¶
插值语法是用来向标签体插入一个动态的数据值。插值语法的结构很固定,是用双大括号包含一个 JavaScript 表达式,像这样:{{JavaScript 表达式}}。值得一提的是,插值语法只可以作为一个标签的标签体文本或者文本的一部分,不能作为标签的一个属性值。双大括号中可以包含任意 JavaScript 表达式,除此之外,它包含的也可以是一个常量值、一个变量,还可以是一个变量对象的方法调用,甚至可以是一个三目表达式。
但是需要注意的是,模板中变量读取数据的来源都是配置指定的 data 对象。虽然还有其他的数据来源,在这里我们暂时只需要理解为 data 对象。
例如,来看一段简单的 Vue 使用实例。
上面代码使用插值语法分别包含了常量 “123”、变量 “msg”、变量对象的方法调用 “msg.toUpperCase ()”,以及三目表达式 “score<60?‘入学测试未通过,暂时不可以来尚硅谷学习’:‘入学测试通过,欢迎来尚硅谷学习’”。由于模板中的所有变量读取的都是 data 中的数据,除了常量 “123” 不需要读取数据,后三者都会读取 data 中对应的值。此时页面中输出的效果如图 11-4 所示。
11.2.2 指令语法¶
指令(Directives)是带有 “v-” 前缀的标签属性,其属性值一般是一个 JavaScript 表达式。Vue 中包含了一些不同功能的指令,比如 v-bind 用来给标签指定动态属性值,v-on 用来给标签绑定事件监听,v-if 和 v-show 用来控制标签是否显示。但要注意,不管是什么功能的指令,它们操作的都是指令属性所在的标签。
下面以 v-bind 与 v-on 为例来演示一下指令语法的使用,v-bind 与 v-on 两个属性的语法格式如下。
示例代码如下。
上面这段代码使用了 v-bind 指令为 标签指定了动态属性值 url,此时 标签的 href 的值就是 data 中定义的 url 的值,上面代码还在
单击 F12 键,可以看到 标签的 href 属性值被替换成了 data 中定义的动态值,如图 11-5 所示。单击 “button” 按钮,先弹出如下对话框,如图 11-6 所示。


然后单击 “确定” 按钮后跳转尚硅谷官网,如图 11-7 所示。

Vue 允许将 “v-bind: 属性名” 简化为 “: 属性名”、“v-on: 事件名” 简化为 “@事件名” 的形式。此时,我们可以将上面代码简化为如下代码。
11.2.3 data 属性和 methods 方法¶
从入门案例中看出,在创建 Vue 对象时,其内部包含几个属性,这里重点介绍一下 data 和 methods。
data 的返回值是一个包含 n 个可变属性的对象,一般称此对象为 data 对象。此对象中的属性,即称为 data 属性。模板中可以读取任意 data 属性进行动态显示。
而 methods 是一个包含 n 个方法的对象。在方法中可以通过 this.xxx 来读取或更新 data 对象中对应的属性。当更新了对应的 data 属性后,界面会自动更新。
有一点需要特别说明,data 对象和 method 方法中的 this 本质上是一个代理(Proxy)对象。它代理了 data 对象所有属性的读写操作,也就是说,我们可以通过 this 来读取或更新 data 中的属性。在 methods 中定义的所有方法最终也添加到了代理对象中,同理也可以在方法中通过 this 来更新 data 属性,从而触发界面自动更新。至于模板中的表达式读取变量或函数,本质上也都是从代理对象上查找的。
11.3 声明式渲染¶
声明式渲染是指使用简洁的模板语法,声明式的方式将数据渲染进 DOM 系统。声明式是相对于编程式而言,声明式是面向对象的,告诉框架做什么,具体操作由框架完成。编程式是面向过程思想,需要手动编写代码完成具体操作。渲染通俗来讲就是,将 Vue 数据模型中的值显示在网页上的过程。
例如,使用 Vue 模板语法 “{{message}}” 来声明式地将 message 数据显示在网页上,而使用模板语法的这种方式就叫作声明式渲染。HTML 文档渲染前后的效果如图 11-8 所示。

其中,方框表示 HTML 标签。空心圆表示动态、尚未确定的数据。实心圆表示经过程序运算以后,计算得到的具体的、可以直接在页面上显示的数据。渲染就是程序计算动态数据得到具体数据的过程。下面看一个具体的例子。
HTML 代码如下。使用插值表达式 “{{message}}”,指定要被渲染的数据。
Vue 代码如下。创建 Vue 对象,并传入所需要的参数。
或者分两步创建,首先创建 JSON 对象,指定 Vue 对象要关联的 HTML 元素,以及 message 对应的具体数据,然后将该 JSON 对象作为参数传入 Vue 对象。
通过验证 Vue 对象的响应式效果,可以看到 Vue 对象和页面上的 HTML 标签始终保持着关联的关系,同时可知 Vue 框架在背后也做了大量的工作。
接下来,我们继续介绍不同类型的数据渲染,包括标签绑定、条件渲染、列表渲染、事件驱动等。
11.4 标签绑定¶
Vue 数据模型 data 与标签绑定包括两种:与标签体内容绑定和与标签属性值绑定。Vue 的数据模型和标签体内容进行绑定,只发生在双标签上,通常借助插值表达式绑定,语法格式如下。
<标签名> 插值表达式:{{数据模型中定义的 key 值}}</ 标签名>
示例代码参见 11.2.1 插值语法的应用。
而 Vue 的数据模型和标签的属性值进行绑定,可以发生在双标签,也可以发生在单标签,包括单向绑定和双向绑定两种。
11.4.1 单向绑定¶
单向绑定通常借助指令语法 v-bind 实现,语法格式如下。
v-bind: 原始属性名 = “数据模型中定义的 key 值”
下面通过具体的例子进行演示。
在 HTML 代码如下,为 标签中 value 属性绑定 Vue 数据模型 data 中定义的 msg 值。
在 Vue 代码如下,创建 Vue 对象,指定 msg 对应的值。
单向绑定页面效果如图 11-9 所示。

值得一提的是,v-bind 可以省略,其语法格式简化为如下形式。
: 原始属性名 =“数据模型的 key 值”
与 value 绑定类似,style 绑定同样是通过 “v-bind:style="数据模型中定义的 key 值"” 来绑定动态 style 样式,当然我们一般也会使用 “:style="数据模型中定义的 key 值"” 的简写方式。
HTML 代码如下。
Vue 代码如下。
另外,对于 value 属性值的绑定,我们需要思考一个问题,当修改 Vue 数据模型中 msg 的值时,对应 标签中的 value 属性值会随之改变,那么用户修改文本框的 value 属性时,msg 的值会跟着变化吗?答案是并不会改变。修改数据模型的值,网页会跟着发生变化,反之不变,我们把这种现象称为单向绑定。那么如何实现双向绑定呢?我们继续往下看。
11.4.2 双向绑定¶
双向绑定需要借助另一个指令语法 v-model 实现,语法格式如下。
当然,并不是所有的标签属性都能够双向绑定,只有用户可以修改的属性能够满足双向绑定。例如,表单项里标签的 value 属性,用户是可以修改的,可以进行双向绑定,而 id、name 等属性都不适用。
同样地,将前面单向绑定中的示例,使用双向绑定重新实现。HTML 代码如下。
Vue 代码如下。
双向绑定的页面效果如图 11-10 所示。
可以看出,
标签内的数据能够和文本框中的数据实现同步修改。这也说明,页面上数据被修改后,Vue 对象中的数据属性也跟着被修改。
另外,v-model 还可以省略属性名,即默认 value 属性,简化后的语法格式如下。

在实际开发中,要考虑到用户在输入数据时,有可能会包含前后空格。而这些前后的空格对于程序运行来说都是干扰因素,需要去掉不必要的空格。
我们可以通过在 v-model 后面加上.trim 修饰符来实现去空格。
11.5 条件渲染¶
在项目开发中,有两种场景极为常见:第一种局部界面需要满足某种条件后显示,第二种是在多个不同的界面效果中间进行切换显示。无论哪一种,都需要满足某种条件才能显示渲染。在 Vue 中将其称为条件渲染,可以通过 v-if 相关指令或 v-show 指令实现。
11.5.1 v-if 相关指令¶
v-if 指令用于控制元素显示或者隐藏,其相关指令包含 v-if、v-else 和 v-else-if。v-if 与 v-else-if 指令的表达式需要一个布尔型的表达式,结果是 true,表示满足条件,显示对应的元素;v-else 是不需要指定表达式的,只需要指定属性名,当其前面的 v-if 和 v-else-if 指令的表达式都不满足时才会显示。
如果只有一个界面的条件渲染,推荐选择使用 v-if 指令。如果有两个界面的条件渲染,推荐选择使用 v-if 与 v-else 指令。如果超过两个界面的条件渲染,那么推荐选择使用 v-if、v-else-if 和 v-else 指令来实现。
阅读下方代码并思考下面代码的运行效果,分析如何在下面代码的基础上进行优化。
这段代码比较简单,页面上会显示 data 中的 score 的值和一个按钮,当单击按钮后,score 的值增加 10。如图 11-11、图 11-12 所示。


但如果现在的需求改为:只有当入学得分小于 60 的时候才会显示该按钮。此时我们就可以通过 v-if 指令来实现。在
你的入学测试得分: 65¶
当 v-if 指定的表达式的值为 true 时,
图 11-13 得分大于 60 时按钮消失
现在再次添加一个新需求:当得分大于等于 60 时,显示绿色的 h3 文本 “欢迎来到尚硅谷学习!”;当得分小于 60 时,显示红色的 h4 文本 “很遗憾,你的入学测试没有通过...”。
根据前面的学习,相信大家已经想到使用 v-if 对两个标签进行控制。但实际上,Vue 提供了更便捷的方式,前面提及过 v-if 的相关指令,对于这个需求可以使用 v-if 和 v-else 配合实现。不过需要注意,v-else 不用指定表达式。
此时修改代码,具体如下。
v-if 的条件与上一段代码的页面效果相同,这里不做过多讲解,但当不满足 v-if 的条件时,就会显示 v-else 所对应的标签体内容。当 data 中的 score 值为 55,不满足条件,则会显示 “很遗憾,你的入学测试没有通过...”,如图 11-14 所示。
单击按钮,得分变成 65,大于 60,显示 “欢迎来到尚硅谷学习!”,如图 11-15 所示。


用户的需求总是多变的。如果需求再次更改:当得分大于等于 60 时,显示绿色的 h2 文本 “欢迎来到尚硅谷学习!”;当得分小于 50 时,显示红色的 h3 文本 “很遗憾,你的入学测试没有通过,可以考虑 UI 或测试”;如果在 50\~60,则显示紫色的 h4 文本 “此次测试没有完全通过,可选择复试”。此时可以使用 v-if、v-else-if 和 v-else 配合使用实现。需要注意的是,v-else-if 指定的表达式为 true 时,其对应的标签体内容才会显示。
再次修改代码,如下所示。
当 score 的值大于等于 60 时,显示 “欢迎来到尚硅谷学习!”;当 score 的值小于 50 时,会显示 “此次测试没有完全通过,可选择复试”;其余情况显示 “很遗憾,你的入学测试没有通过,可以考虑 UI 或测试”。分别如图 11-16、图 11-17 和图 11-18 所示。



前面介绍的案例都是针对单个标签进行条件渲染,那么如果是多个标签又要怎么处理呢?我们可以使用 标签来包含需要条件渲染的多个标签,并在 标签上使用 v-if 指令来判断是否显示。需要注意的是, 标签只存在于模板中,并不会在页面中产生任何标签与之对应,如下所示。
运行代码后,由于 score 的值初始为 55,不满足 v-if 的条件,所以只显示入学得分和 “学习后再复试一次” 的按钮。当单击 “学习后再复试一次” 按钮后,score 的值更改为 65,满足 v-if 的条件,显示其余三个按钮,如图 11-19 所示。
此时观察页面结构,发现没有 标签,验证了 标签只在模板中存在,并不会产生任何对应的 HTML 标签的说法,如图 11-20 所示。


11.5.2 v-show¶
在 Vue 中,v-show 指令也可以实现条件渲染。当指定的表达式为 true 时,对应的内容才会显示。下面我们将 v-if 中涉及的案例再次使用 v-show 进行实现。
代码片段 1 如下。
当 score 的值小于 60 时,显示 “学习后再复试一次” 按钮。
代码片段 2 如下。
当 score 的值大于等于 60 时,显示 “欢迎来到尚硅谷学习!”;当 score 的值小于 60 时,显示 “很遗憾,你的入学测试没有通过...”。
代码片段 3 如下。
当 score 的值大于等于 60 时,显示 “欢迎来到尚硅谷学习!”;当不满足大于等于 60 和小于 50 时,会显示 “此次测试没有完全通过,可选择复试”;其余情况显示 “很遗憾,你的入学测试没有通过,可以考虑 UI 或测试”。
需要注意的是,v-show 是不能与 v-else 和 v-else-if 配合使用的。当需要在多个标签间切换显示时,要使用多个 v-show 来实现。还有一点,v-show 不能用在 标签上。
11.5.3 比较 v-if 和 v-show¶
通过前面学习的内容可知,v-if 和 v-show 两者实现的功能效果是一致的,但实际上二者内部的实现机制截然不同,这一点在理论面试时也是经常提问到的点,因此对于两者的区别,大家也需要了解一二。
在初始化解析显示时,如果表达式为 true,那么二者的内部处理相同;如果表达式为 false,处理完全不同。v-if 对应的模板标签结构不会解析,也就不会产生对应的 HTML 标签结构;而 v-show 则会解析模板,生成 HTML 标签结构,只不过会通过指定样式 display 为 none 来隐藏标签结构。
在更新数据后,表达式变为 true 时,就需要显示标签结构。v-if 的标签会重新创建 HTML 标签结构并显示,而 v-show 只需要去除 display 为 none 的样式让元素重新绘制出来。
当再次更新数据后,表达式变为 false 时,需要隐藏标签结构。v-if 的标签直接被删除,内存中不再有对应的 DOM 结构;v-show 的标签则通过 display 为 none 来隐藏标签结构。
接下来通过代码进行验证。
HTML 代码如下。
Vue 代码如下。
代码中通过 v-if 指令控制
标签的显示或隐藏,通过 v-show 来控制 标签的显示或隐藏。
初始显示时,由于 isShow 为 false,页面中
与 对应的两个标签体都不可见。查看元素可以发现 标签确实不存在,但 标签是存在的,只是通过 display 为 none 的样式将其隐藏了,如图 11-21 所示。
标签确实不存在,但 标签是存在的,只是通过 display 为 none 的样式将其隐藏了,如图 11-21 所示。
单击按钮对 isShow 标识进行取反,isShow 更新为 true。此时
和 对应的两个标签体都会显示,但通过 v-if 控制的 标签是新创建的,而 标签是通过删除 display 为 none 的样式将其显示,如图 11-22 所示。
标签是新创建的,而 标签是通过删除 display 为 none 的样式将其显示,如图 11-22 所示。


再次单击按钮时,isShow 更新为 false。此时
和 对应的两个标签体都会消失,但 v-if 的 标签被删除,不存在了,但通过 v-show 控制的 标签还存在,只是添加了 display 为 none 的样式。
标签被删除,不存在了,但通过 v-show 控制的 标签还存在,只是添加了 display 为 none 的样式。
最后简单总结 v-if 与 v-show 的区别,在隐藏时,v-if 会删除标签,而 v-show 只是通过样式控制标签不显示;再次显示时,v-if 需要重新创建标签,而 v-show 只需要更新样式就可以显示。
对于使用场景的选择上,如果条件渲染的条件变化相对频繁,或要控制的标签结构比较大,那么选择 v-show 比较合适。因为 v-if 在从隐藏变为显示时,需要重新创建 DOM 结构,效率相对低些。但 v-show 在隐藏时,标签结构没有删除,比 v-if 占用的内存更大一些。这也就是编程中经常说到的,以空间换时间的技术,即用更大的空间占用,换取后面更少的时间执行”。
还有一点需要注意,如果初始渲染条件值为 false 时,v-if 是不会解析模板产生 HTML 标签的,有时也称它为懒加载,但 v-show 则不是懒加载。
11.6 列表渲染¶
在项目开发的界面中,有很多列表效果需要动态显示。这在 Vue 中称为列表渲染,可以通过 v-for 指令来实现。通过 v-for 指令可以遍历多种不同类型的数据,数组是较为常见的一种数据,当然数据还可以是对象或数值。
动态显示列表后,我们可以对列表进行添加、删除、修改、过滤、排序等操作。另外,在通过 v-for 进行列表渲染时,有一个特别重要的属性 ——key 属性,这点需要注意。
11.6.1 列表的动态渲染¶
下面分别介绍 v-for 作用于数组、对象,以及整数的具体操作。
- 通过 v-for 遍历数组显示列表,其语法格式如下。
其中,item 为遍历 array 数组的元素别名,index 为数组元素的下标。如果遍历不需要下标,可以简化为如下格式。
另外值得注意的是,item 和 index 的名称不是固定的,可以自定义其他合法名称。
- 通过 v-for 遍历一个对象时,遍历的是对象自身可遍历的所有属性,其语法格式如下。
其中,value 为遍历的 obj 对象的属性值,而 name 是属性名。如果只需要属性值,可以简化为如下格式。
同样,value 和 name 的名称不是固定的,可以使用其他合法名称。
- 当 v-for 的目标是一个正整数 n 时,一般用于将当前模板在 1~n 的取值范围内重复产生 n 次,其语法格式如下。
其中,value 为从 1 开始到 n 为止,依次递增的数值。接下来,通过代码分别演示 v-for 作用于 persons 数组、car 对象,以及 num 数字的 3 种情况。
HTML 代码如下。
Vue 代码如下,提供 persons 数组、car 对象,以及 num 数值的信息。
// 创建 Vue 对象,挂载 #app 这个 div 标签
v-for 作用于单个标签的页面效果,如图 11-23 所示。

与 v-if 类似,我们也可以利用在 标签上使用 v-for 指令,实现对多个标签的列表渲染。示例代码如下。
上面代码利用了在 标签上使用 v-for 的方式,实现了标签列表循环,此时页面中每个
综上,我们已经可以利用 v-for 指令对数据进行循环了。
11.6.2 列表的增删改¶
动态显示数组列表后,一般不会只作为展示存在,可能还需要对列表进行增删改操作。下面展示一个简易的列表,并演示对列表的增删改操作。如图 11-25 所示,用户对于该列表有如下需求。
v-for 同时作用于多个标签¶

根据上述用户需求,编写代码实现其功能。
HTML 代码如下。
Vue 代码如下。
上面代码中为每个按钮都封装了相应的方法来实现对应的功能,从而实现对数组元素的增删改操作。其实每个功能方法内部都是通过调用数组变更内部元素的方法(包括 unshift、push 和 splice 方法)来实现的。
11.7 事件驱动¶
前面已经多次演示在
11.7.1 事件绑定方式¶
我们可以在 HTML 标签上使用 v-on 指令语法来绑定原生 DOM 事件,语法格式如下。
通常情况下,我们一般用简化语法,具体如下。
handler 为事件处理器,Vue 中支持多种 handler 的写法。下面我们会对 handler 常用的两种写法进行讲解。
- handler 是一个方法名,对应 methods 配置中的一个同名方法。
- handler 是一条语句,此时这条语句有两种可能性,既可以是调用 methods 中某个方法的语句,也可以是直接更新 data 数据的语句。
例如,在如下初始代码的基础上,演示 handler 的使用。HTML 代码如下。
Vue 代码如下。
1. handler 是一个方法名¶
当事件触发时,对应的方法就会自动调用。这也是使用最多的一种,我们可以使用它来实现第一个按钮的单击功能。
HTML 代码如下。
Vue 代码如下。
对于上面代码来说,当单击按钮时触发 handleClick1 方法。需要特别说明的是,handleClick1 方法中定义的形参 event,接收到的就是事件对象,这就是原生 DOM 事件监听回调原本的特性。
2. handler 是一条语句¶
handler 作为一条语句,包括调用 methods 中某个方法的语句和直接更新 data 数据的语句。下面具体说明。
(1)调用 methods 中某个方法的语句。
例如,借助 handler 作为调用方法的语句,并传入自定义数据,实现单击初始代码中的第二个按钮,提示指定的特定内容。
HTML 代码如下。
Vue 代码如下。
需要特别说明的是,方法调用的语句在事件发生前不会执行,只有在事件发生后,才会自动调用。
(2) 更新 data 数据的语句。
例如,借助 handler 作为直接更新 data 数据中 num 的语句,实现单击第三个按钮,让 num 的数量加 3。
HTML 代码如下。
11.7.2 事件的默认行为¶
比如单击超链接后会直接跳转页面、单击表单提交按钮后会直接提交表单等,是单击超链接和按钮后的默认行为。但是,如果现在我们希望单击超链接或按钮之后,根据判断的结果再决定是否要跳转,此时其默认行为就不符合我们的预期了,这种情况下,我们需要取消事件的默认行为。
在 JavaScript 代码中,可以调用事件对象的 preventDefault () 方法取消事件的默认行为。
(1) 取消超链接的默认行为。
HTML 代码如下。
JavaScript 代码如下。
运行代码后发现,单击超链接并不会跳转百度网页。
(2) 取消表单提交的默认行为。
HTML 代码如下。

运行代码后发现,填写完表单单击表单 “提交” 按钮后并不会跳转提交。
另外还有一个常见的事件冒泡行为。事件冒泡是指当一个元素上的事件被触发的时候,比如鼠标单击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡。这个事件从原始元素开始一直冒泡到 DOM 树的最上层。
如图 11-26 所示,由两个
示例代码如下。
单击内部
事件冒泡就是一个事件会不断向父元素传递,直到最顶层的 window 对象。如果这不是我们想要的效果,那么可以使用事件对象的 stopPropagation () 方法来阻止事件冒泡行为。
11.7.3 Vue 事件修饰符¶
在原生 DOM 事件处理中,我们可以在事件监听回调函数中通过 event.preventDefault () 来阻止事件默认行为,还可以通过 event.stopPropagation () 来阻止事件冒泡。Vue 主张在方法中最好是只有纯粹的数据逻辑,而不是处理 DOM 事件细节,因此设计了更简洁的事件修饰符,来实现对事件的这两个操作及事件的其他操作。
事件修饰符是用点表示的事件指令后缀,比如 “@事件名。修改符名 ="handler"”。常用的事件修饰符如下。
下面通过案例来演示几个常见事件修饰符的使用。
HTML 代码如下。
Vue 代码如下。

演示事件修饰符的页面效果如图 11-27 所示。
通常情况下,单击链接默认会跳转页面,但是上面代码通过 “.prevent” 阻止默认行为,最终就不会跳转了。
在外部 div 和内部 div 上都绑定单击事件。当单击内部 div 的绿色区域,默认内部 div 会响应,但是由于事件冒泡,外部 div 也会响应。上面代码加上了 “.stop” 停止事件冒泡,那么外部 div 就不会响应了。
另外,因为上面代码为按钮指定了 “.once” 修饰符,所以单击下面的按钮时,只会响应一次。
11.8 侦听属性¶
下面请看一段代码并思考一个问题:如果我们希望 firstName 或 lastName 属性发生变化时,fullName 属性也能随之做出相应改变,那么应该如何实现呢?
正确的做法是,此时需要对 firstName 或 lastName 属性进行侦听。具体来说,所谓侦听就是对属性进行监控,通过 watch 选项配置一个函数来监视某个响应式属性的变化。监视函数默认在数据变化时回调,且接收新值和旧值两个参数,其语法格式如下。
对于上述案例,就是当 firstName 或 lastName 属性的值发生变化时,直接调用准备好的函数,对 fullName 属性做出相应修改。
在 watch 中声明对 firstName 和 lastName 属性进行侦听的函数。Vue 代码如下。
侦听前的初始页面如图 11-28 所示。
设置 watch 侦听后,修改 firstName 和 lastName 属性值,发现 fullName 属性值也随之改变,如图 11-29 所示。


11.9 Vue 的生命周期¶
Vue 实例在应用过程中会有初始、挂载、更新,以及卸载的运行过程,我们将这个过程称为 Vue 实例的生命周期。对应在开发中,不同阶段程序会主动通过调用特定的回调函数来通知我们,我们将其称为 “生命周期钩子函数”。
11.9.1 生命周期流程图¶
Vue 实例的生命周期主要包括初始、挂载、更新、销毁几个不同的阶段,它们是按照顺序依次执行的。不是所有阶段的执行次数和目标都是相同的,其中初始、挂载和销毁这三个阶段都只执行一次;更新阶段是在不断发展和变化的,会不断地被触发。下面通过一个图来演示 Vue 实例的生命周期,如图 11-30 所示。

11.9.2 Vue 生命周期钩子函数¶
我们把一个对象从创建到被销毁的过程称为生命周期。而生命周期函数,就是在某个时刻会自动执行的函数。每个 Vue 实例在被创建时都要经过一系列的初始化过程:创建实例、装载模板、渲染模板等。Vue 为生命周期中的每个状态都设置了钩子函数(监听函数)。当 Vue 实例处于不同阶段的生命周期时,对应的函数就会被触发调用。Vue 中常见的八大生命周期钩子函数如表 11-1 所示。
表 11-1 Vue 中常见的八大生命周期钩子函数
| 钩子函数 | 调用时间 |
| beforeCreate() | Vue 实例初始化前调用 |
| created() | Vue 实例初始化后调用 |
| beforeMount() | 挂载到 DOM 树前调用 |
| mounted() | 挂载到 DOM 树后调用 |
| beforeUpdate() | 数据更新前调用 |
| updated() | 数据更新后调用 |
| beforeDestroy() | Vue 实例销毁前调用 |
| destroyed() | Vue 实例销毁后调用 |
Vue 生命周期钩子函数的代码书写位置与 data、methods、watch 等配置属于同级并列关系。下面结合代码分析每个生命周期钩子函数并了解它们之间的差异。
需要注意的是,ref 属性用于普通的 DOM 元素,表示引用指向的是该 DOM 元素,通过 $refs 可以获取该 DOM 的属性集合,作用与 JavaScript 的选择器类似。上述代码中,在
元素标签上使用 ref 属性,通过 “this.$refs.ref. 属性值” 可以获取整个
标签。
代码中演示了生命周期的 8 个钩子函数,下面分别进行分析。
- beforeCreate()
这是 Vue 实例的第一个生命周期钩子函数。在该钩子函数中出现了 this 对象,这个 this 对象指向的是当前 Vue 的实例对象。如果实例对象上有数据或者方法等内容,后续可以通过 this.xxx 的方式调用。值得一提的是,这个钩子函数是在 Vue 实例完成后立即触发的,但此时像 data 数据初始化、methods 方法调用,以及 watch 监控等内容都还没有进行初始化,更不用说 $el 的 DOM 元素对象了。因此在该生命周期钩子函数中,this.message 数据内容和 this.$refs.pRef 获取的 DOM 对象都为 undefined 未定义。
- created()
该钩子函数是在处理完所有和状态相关的选项后调用的,这就意味着调用钩子函数之前 data、methods、watch 等内容都已经设置完成。因此 this.message 内容会打印出 “欢迎来到尚硅谷” 字符串信息,但是由于挂载阶段还没有开始,所以 this.$refs.pRef 属性仍旧为 undefined。
- beforeMount()
这是在挂载 DOM 之前触发的生命周期钩子函数。如果有 template 模板内容,则会将其编译成 render 函数并调用。因为这一阶段还没有获取 DOM 对象,所以相关的内容都停留在虚拟 DOM 的阶段。因此在这一阶段,this.message 内容会打印出 “欢迎来到尚硅谷” 字符串信息,但 this.$refs.pRef 仍旧为 undefined。
- mounted()
在这一阶段的网页中,el 元素对象最终被实例的 $el 元素对象内容所代替,成功地将虚拟 DOM 内容渲染到了真实 DOM 对象上。用户现在可以查看到网页元素最终渲染的效果,因此当前 this.$refs.pRef 打印出来的结果是一个 DOM 元素渲染后的
标签。
到这里前面出现的生命周期钩子函数都只会按照讲解顺序执行一次。我们可以将其称为 “初始挂载阶段”,生命周期顺序以及相关内容输出打印的结果如图 11-31 所示。

- beforeUpdate()
由于组件的响应式状态变更,在即将更新 DOM 树前,会调用该生命周期钩子函数。比如示例代码中可以通过按钮修改 message 信息,这就产生了一个响应式数据的变更。在代码中的钩子函数中设置一个 debugger 断点进行效果的查看,最终会发现界面中显示的仍旧是数据修改之前的 DOM 内容,如图 11-32 所示。
- updated()
该生命周期钩子函数与 beforeUpdate 钩子函数类似,一个在更新 DOM 树前调用,一个在更新 DOM 树后调用。将之前的断点调试继续运行,则会发现界面中 message 内容已经被替换成了 “尚硅谷拥有最好的互联网技术培训” 字符串内容,如图 11-33 所示。


- beforeDestroy()
当调用当前的生命周期钩子函数时,组件实例未被销毁,依然还保有全部的功能。值得注意的是,打开 F12 控制台,输入 app.$destroy()主动销毁 Vue 实例,该函数才能被调用。同样在该生命周期钩子函数中进行 debugger 断点调试,就可以查看 beforeDestroy 生命周期钩子函数的调用结果。此时界面中仍将保留 DOM 显示的状态,控制台中的 this.$refs.pRef 仍旧显示有元素对象,如图 11-34 所示。

- destroyed()
与 beforeDestroy () 一样,需要在控制台输入 app.$destroy(),该函数才能被调用。该生命周期钩子函数在一个组件实例被卸载后调用,此时对应实例的 DOM 对象也不复存在。如图 11-35 所示,程序进入 destroyed 生命周期,显然 this.$refs.pRef 对象内容已经被清空,而网页中也不再显示任何的 DOM 元素。
图 11-35 网页中不显示任何的 DOM 元素
| beforeCreate undefined undefined | demo01.html:23 |
| created 欢迎来到尚硅谷 undefined | demo01.html:26 |
| beforeMount 欢迎来到尚硅谷 undefined | demo01.html:29 |
| mounted 欢迎来到尚硅谷 <p>欢迎来到尚硅谷</p> | demo01.html:32 |
| beforeUpdate | demo01.html:35 |
| updated | demo01.html:39 |
| > app.$destroy() | |
| beforeDestroy <p>尚硅谷拥有最好的互联网技术培训</p> | demo01.html:42 |
| destroyed undefined | demo01.html:46 |
11.9.3 常用的生命周期方法¶
在这么多的生命周期钩子函数中,哪些才是项目开发中最为常用的呢?下面结合实际情况,从 6 个方面对各个生命周期钩子函数进行分析。
这里需要思考的是,在更新阶段是否要进行类似数据请求这样的操作,以及数据请求操作频率的问题,这将牵扯到性能相关的问题,因此这两个钩子函数的操作并不常用。如果一定要使用,还需要注意利用特
定的条件触发的频率限制。
(6)因为 destroyed () 已经完全卸载了实例对象,所以在这个钩子函数中操作的内容相对较少。如果需要进行定时器清除、取消监听、断开网络连接等操作,建议在 beforeDestroy 实例未完全卸载阶段进行,这样也能够确保 destroyed 阶段卸载的实例对象是比较干净纯粹的。
综上所述,mounted、beforeDestroy 等生命周期钩子函数是项目开发中最常用的钩子函数。
11.10 案例:水果库存静态页面功能优化¶

下面通过 Vue 技术重新实现第 10 章 JavaScript 的案例。同样包括水果库存静态页面的功能:鼠标悬浮和离开时的操作、单价的更新,以及删除指定行。
在 section10 文件夹下,引入第 10 章案例的 CSS、HTML 文件和相关图片,以及 vue.js 文件。目录结构如图 11-36 所示。
其中,demo01.html 用于实现鼠标悬浮和离开时的操作、demo02.html 用于实现单价的更新、demo03.html 用于实现删除指定行。且这三个功能是递进式的,后一个功能在前一个功能的基础上实现。
11.10.1 鼠标悬浮效果实现¶
首先借助 Vue 来实现鼠标悬浮移动时不同效果的实现。
修改 demo01.html 文件,引入 vue.js 文件,添加 Vue 代码。示例代码如下。
在 demo01.css 文件中添加如下 CSS 样式,示例代码如下。
运行代码,将鼠标悬浮在 “苹果” 所在行,如图 11-37 所示。

从图中可知,鼠标悬浮效果成功实现,鼠标停留在该行,该行整体颜色变深表示特指,与其他行区分开来;鼠标离开后,恢复原来的样式。
11.10.2 更新单价操作¶
本节内容对水果的单价进行优化,实现当鼠标单击单价列时,显示数字文本框,能够输入新的数额,更新单价信息。
修改 demo02.html 文件,添加 Vue 代码,示例代码如下。
//1. 鼠标滑动¶
运行代码,修改苹果的单价(元)为 10 如图 11-38 所示。

从图中可知,修改苹果单价(元)为 10 后,小计(元)为 200,总计(元)为 530,表明更新单价成功。并且保证输入时只有数字生效,如果输入字母则输入失败。
11.10.3 删除指定行¶
本节继续实现单击操作栏的删除图标,删除对应指定行的水果库存信息并更新总计。
修改 demo03.html 文件,添加 Vue 代码,示例代码如下。
运行代码,单击苹果所在行的删除图标,如图 11-39 所示。
图 11-39 删除苹果信息
| 名称 | 单价(元) | 数量(个) | 小计(元) | 操作 |
| 香蕉 | 7 | 10 | 70 | × |
| 梨儿 | 3 | 20 | 60 | × |
| 西瓜 | 10 | 20 | 200 | × |
| 总计(元) | 330 | |||
从图中可知,苹果信息删除成功,且总计随之改变。如果全部删除,如图 11-40 所示。从图中可知,所有水果信息删除成功,且总计为 0。

11.11 本章小结¶
本章主要介绍了 Vue 的相关知识,介绍了基于 HTML 的 Vue 模板语法,包括插值语法和指令语法两部分。详细介绍了声明式渲染、标签绑定、条件渲染、列表渲染等应用,以及 Vue 的事件处理和侦听属性 watch 的使用。另外还分析了 Vue 生命周期全过程,以及不同阶段钩子函数的调用情况。最后通过案例将之前 JavaScript 代码借助 Vue 实现,简化代码,进行优化,从而加深对 Vue 的理解和应用。通过本章的学习,希望大家可以灵活运用 Vue 进行前端页面开发。