在 Vue.js 框架中,与 HTML 页面元素的交互方式没有像原生 JavaScript 接口那么直接,它是通过先在 HTML 元素标签中嵌入一系列类似于普通标签属性的 Vue 指令属性来绑定数据,然后再通过在 JavaScript 代码中修改这些被绑定的数据来修改页面元素的显示方式与内容。在编程方法上,我们通常将这种用数据内容的变化来驱动整个程序业务运作的方式称之为"数据驱动开发"。这部分笔记将记录如何利用数据驱动的开发方式来完成数据绑定、事件响应,以实现控制页面元素与 CSS 样式等各项基本功能。
数据绑定
在之前的01_sayHello
程序中,我们现在<h1>
标签中使用模版语法绑定了一个名称为sayHello
的数据,该模版语法实际上是v-text
指令的语法糖。换句话说,该<h1>
标签更规范的语法应该是:
<h1 v-text="sayHello"></h1>
考虑到我们传统上编写 HTML 标签的习惯,使用{{ data_name }}
这样的模版标记会是更让人舒服的做法。当然了,v-text
指令设置的是当前元素标签下面的文本内容,如果要为标签本身的属性绑定数据,就得要使用v-bind
指令,具体语法是在要设置的标签属性名前面加上v-bind:
前缀。例如,如果想为<img>
标签的src
属性绑定数据,就可以这样做:
<img v-bind:src="vueLogo" style="200px">
另外,v-bind
指令还有一个简写形式,只需要在要绑定数据的标签属性名之前加一个:
前缀即可。在之前的01_sayHello
程序中,我采用的就是这种形式。在页面元素上绑定了数据之后,接下来就可以在相对应的 JavaScript 脚本中创建 Vue 实例了,这就是我之前在01_sayHello
程序的main.js
文件中编写的内容。这个 Vue 实例对象中至少要定义以下两个成员:
el
成员:该成员用于设置当前 Vue 实例所对应的元素容器,这通常是一个<div>
元素,某些情况也可以是 HTML5 提供的<header>
、<footer>
等容器类标签。该成员的值可以是任何一个符合 CSS 选择器语法的字符串,例如#ID
、.CLASSNAME
等。data
成员:该成员用于设置页面元素中绑定的数据,它的值本身也是一个 JSON 格式的对象,该对象的每个成员都对应一个页面元素中绑定的对象,例如在之前的01_sayHello
程序中,我绑定了sayHello
和vueLogo
这两个数据,就必须要在 Vue 对象的data
成员中为为它们设置相应的值。
事件处理
当然,01_sayHello
程序目前只是一个单向显示数据的业务。如果想要让其具备交互能力,还需要为页面元素绑定事件。在 Vue.js 框架中,绑定事件首先要通过v-on
指令来为目标元素标签注册事件处理函数,例如如果我们想在01_sayHello
程序用一个按钮来控制<img>
元素的显示与否,可以将该程序的index.htm
代码修改如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script defer="defer" src="../node_modules/vue/dist/vue.js"></script>
<script defer="defer" src="js/main.js"></script>
<title>你好,Vue.js</title>
</head>
<body>
<div id="app">
<h1> {{ sayHello }} </h1>
<!-- 上述模版语法背后真正的 Vue 语法:
<h1 v-text="sayHello"></h1>
-->
<img :src="vueLogo" v-show="isShow" style="200px">
<!-- 上述简写指令的完整 Vue 语法:
<img v-bind:src="vueLogo" style="200px">
-->
<input type="button" :value="isShow?'隐藏':'显示'" @click="toggleShow" />
<!-- 上述简写指令的完整 Vue 语法:
<input type="button" v-bind:value="isShow?'隐藏':'显示'" v-on:click="toggleShow" />
-->
</div>
</body>
</html>
在这里,我主要做了如下修改:
- 首先,在
<img>
标签中增加了一个v-show
指令,该指令可用于绑定一个布尔类型的数据(即这里的isShow
),以此来决定是否显示其所在的标签。 - 然后,在页面中新增了一个按钮标签,并用
v-bind
指令设置了该标签的value
属性,该属性的值是一个条件表达式,它将根据isShow
的值显示不同的文本。 - 最后,用
v-on
指令(@
是该指令v-on:
前缀的简写形式)为新增的按钮标签注册了一个名为toggleShow
的单击事件处理函数。
下面继续来对main.js
中的代码进行修改,具体如下:
const app = new Vue({
el: '#app',
data:{
sayHello: '你好,Vue.js!',
vueLogo: 'img/logo.png',
isShow: true
},
methods:{
toggleShow: function() {
this.isShow = !this.isShow;
}
}
});
在这里,我主要做了如下修改:
- 首先,在 Vue 对象的
data
成员中定义了之前绑定的布尔类型数据isShow
,并将其默认值设置为 true。 - 然后,为 Vue 对象新增了一个名为
methods
的成员。该成员用于设置页面元素中注册的事件处理函数,它的值也是一个 JSON 格式的对象。该对象的每个成员都对应一个已在页面元素中用v-on
指令注册的事件处理函数。在这里就是toggleShow
,该函数每次调用都会将isShow
的值取反。
这样一来,当我们在 Web 浏览器中打开该应用程序就会看到之前的 Vue 图标旁边多了个文本内容为隐藏
的按钮。当按钮被单击之后,图标就会消失,按钮上的文本也变成显示。之后,如果该按钮再次被单击,一切又会恢复原状。
用户输入
对于用户来说,除了触发事件之外,允许用户在运行时输入具体的数据也是一个应用程序必不可少的一项功能。下面将通过编写一个"待办事项"的程序来学习如何使用 Vue.js 框架实现能处理用户输入数据的应用程序界面。为此,我需要在code
目录下创建一个名为02_toDoList
的目录,并在该目录中创建一个名为index.htm
的文件,其代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script defer="defer" src="../node_modules/vue/dist/vue.js"></script>
<script defer="defer" src="js/main.js"></script>
<title>待办事项</title>
</head>
<body>
<div id="app">
<h1>待办事项</h1>
<div id="todo">
<ul>
<li v-for="( task,index ) in taskList">
<input type="checkbox" v-model="doneList" :value="task">
<label :for="task">{{ task }}</label>
<input type="button" value="删除" @click="remove(index)">
</li>
</ul>
</div>
<div id="done" v-if="doneList.length > 0">
<h2>已完成事项</h2>
<ul>
<li v-for="task in doneList">
<label :for="task">{{ task }}</label>
</li>
</ul>
</div>
<input type="text" v-model="newTask" @keyup.enter="addNew">
<input type="button" value="添加新任务" @click="addNew">
</div>
</body>
</html>
接下来,我会在同一目录下再创建一个名为js
的目录,并在该目录下创建main.js
脚本文件,其代码如下:
// 程序名称: toDoList
// 实现目标:
// 1. 学习 v-model、v-for 等指令
// 2. 掌握如何处理用户输入
const app = new Vue({
el: '#app',
data:{
newTask: '',
taskList: [],
doneList: []
},
methods:{
addNew: function() {
if(this.newTask !== '') {
this.taskList.push(this.newTask);
this.newTask = '';
}
},
remove: function(index) {
if(index >= 0) {
this.taskList.splice(index,1);
}
}
}
});
下面来具体分析一下这个程序。在通常情况下,Web 应用程序在前端接受用户输入信息的方式主要有两种:第一种方式是用文本框元素来获取用户从键盘输入的信息。在 Vue.js 框架中,我们可以用v-model
指令将<input type="text">
标签绑定到newTask
数据中,该指令与v-bind
指令不同的在于,它是一种双向绑定。也就是说,v-model
指令不止可以让 JavaScript 代码中对绑定数据的修改反映到页面元素中,也可以让页面元素获取到的用户输入数据同步到 JavaScript 代码中。在 JavaScript 代码获取到文本框中的用户输入内容之后,我们就可以通过事件处理函数addNew
将其加入到一个名为taskList
的数据列表中,然后将该事件处理函数注册给输入回车和鼠标单击事件。
第二种方式是用单选钮、复选框等选择性元素来获取用户用鼠标勾选的信息。例如在上面这个程序中,我在<div id="todo">
元素中用v-for
指令创建了一系列<input type="checkbox">
,它们都提供v-model
指令将自己双向绑定到了另一个名为doneList
数据列表上。在 Vue.js 框架中,如果一组复选框元素被v-model
指令绑定到了一个列表类型的数据上,那么当这些复选框被选上时,它们的value
属性值就会被自动添加到这个被绑定的数据列表中。
为了证明被选中的复选框被加入到了doneList
数据列表中,我还在页面中添加了一个<div id="done">
元素,并对doneList
数据列表进行了遍历显示。需要说明的是,用于遍历数据列表的v-for
指令主要有两种形式:
- 当我们只要遍历列表中的值时,可以这样写:
v-for="item in dataList"
,这时候数据列表(dataList
)中的每一项数据就会在遍历过程中逐一被迭代变量(item
)说读取。 - 当我们需要同时获取列表值及其在列表中的索引时,可以这样写:
v-for="( item,index ) in dataList"
,这时候数据列表(dataList
)在遍历过程中,每一项数据的值会被item
变量读取,而每一项数据的索引会被index
变量读取。
最后需要说明的是,对于<div id="done">
元素本身,我使用了v-if
指令,它的效果与之前的v-show
指令基本相同,唯一的区别是:v-if
指令会直接在 DOM 树上增加或删除其所在的元素节点,而v-show
指令则是单纯通过其所在元素的style
属性隐藏或显示该元素而已。在执行效率上,v-show
指令要更高效一些。在这里,我们设置了当doneList
列表中没有数据时,程序就直接将<div id="done">
元素从页面中移除,反之则在页面中添加该元素。下面来看一下02_toDoList
程序运行的效果: