之前我们一直都在说 “绑定”,从现在开始会发散和穿插着说些其他的内容。
在最最基本的知识这块,我们还有这么一些要说的:
1)绑定:用得比较少的知识点,如: v-html、v-once 和日期时间类的 <input>;
2)渲染:在上一篇你可能也注意到了,选项要是一个个手写的话,还是有些费事的 —— 这就是渲染的一个例子;
3)事件处理:好不容易能接收用户数据了,但是要怎么处理 “按钮点击”、“选中某个项目” 这些事件呢?
基于 “功利的实用主义”,掐指一算,咱们还是先从 2)渲染开始讲起。
1、列表渲染?
咱们上一篇中有一个是模拟的选择收货地址的,大概是这样的。
其中的各个选项,是这么写的。
<option value="beijing">北京市</option>
<option value="tianjin">天津市</option>
<option value="hebei">河北省</option>
<option value="shanxi">山西省</option>
还好只写了 4 个,要是把全国 34 个省级辖区都写出来,真要写个半死。
更进一步,要是选择了某个省级辖区之后,还要在另一个选择框中把下辖的各个地市都列出来,又要怎么做呢?
我们需要一种动态的、可以批量生成多个、具有相同格式的组件的技术,否则上面显示地市的需求就不是半死就能解决的了、全死都不一定能搞定。
这种技术,就是接下来要说的 “列表渲染” —— 当然,渲染列表之外的元素也是 OK 的 —— 如果你理解它的本质了的话。
Vue 官方的说明文档在: https://cn.vuejs.org/v2/guide/list.html 。
我们先不看 Vue 里面是怎么做的,而是根据掌握的其他知识脑补一下 —— 尤其是如果你用过其他的 “模板渲染引擎” —— 比如说 JSP、Velocity 和 Thymeleaf 的话。
以生成全国 34 个省级辖区的 <option> 组件为例,JSP 的代码大概是这样的:
<select>
<c:forEach var="province" items="${provinces}">
<option value="${province.code}">${province.name}</option>
</c:forEach>
</select>
而 Thymeleaf 的代码大概是这样的:
<select>
<option th:each="province : ${provinces}" th:value="${province.code}" th:text="${province.name}">
</option>
</select>
万一你从没用过任何的渲染引擎,那也可以想象一下如果要写成 Java 代码(其他的代码也行)是什么样的。
伪代码可能都长得跟下面差不多:
// 各个省级辖区的数组或者集合
Province[] provinces = [北京市, 天津市, 河北省, xxx, xxx];
// 遍历各个省级辖区,为每个辖区生成一个 <option> 组件
for (var province : provinces) {
// 生成一个 <option> 组件, 格式为 <option value="xxx">yyy</option>
OptionComponent option = createOptionComponent(province);
// 插入到 <select> 组件的 <option> 集合中
selectComponent.addOption(option);
}
Vue 中实现同样渲染功能的代码,大概是下面这样的。(注意 v-for 语句)
<select v-model="location">
<option v-for="province in provinces" :value="province.code">{{province.name}}</option>
</select>
带 <script> 的完整代码,大概是这样的:
<div id="app">
<div>位置:
<select v-model="location">
<option v-for="province in provinces" :value="province.code">{{province.name}}</option>
</select>
</div>
<div>您选择了:{{location}}</div>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
location: "tianjin",
provinces: [
{code: "beijing", name: "北京市"},
{code: "tianjin", name: "天津市"},
{code: "hebei", name: "河北省"},
{code: "shanxi", name: "山西省"}
]
}
});
</script>
如果你对 data 中 provinces 的语法看得不是很明白,那么你可能还得速成和习惯一下 JavaScript 的数据类型,尤其是数组和对象 —— 同时,最好忘了之前其他开发语言中的 “对象”。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_Types
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects
2、v-for 与 JavaScript 的循环语句
你有没有发现,其实原理非常非常地简单,模板引擎的风格也都大同小异。
通过其中 “小异” 的地方,也可以围观、吐槽和研究一下 JavaScript 的循环语句。
JavaScript 中除了常规的 for ...、do ... while 和 while 这类循环语句 (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration)之外,
还有 for ... in 和 for ... of 这类有些费解、但事实上在 Java、C# 等语言中也被广泛采用的循环语句。
1) for ... in 和 for ... of 表达式
for ... in 是个可以用来遍历各种类型的数据的、全能型循环语句。(参照:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in)
跟之前我们提到的全能型的 <input> 组件一样,for ... in 也充满了各种各样、违背直觉的诡异问题 —— 尤其是遍历数组的时候。
比如说下面这段代码:
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
for (var name in names) {
console.log(name);
};
看起来灰常简单,输出结果应该是 张三、李四、王五 。
可是呢,实际的输出结果是:0、1、2 —— 也就是各个元素的 index。
为了正确输出各个名字,咱们得这么写:(拜吐槽了,谁叫这个语法也是爷爷级的 ......)
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
for (var index in names) {
console.log(names[index]);
};
为了避免被后浪的口水淹死,JavaScript 推出了 for ... of 这个补丁,用来按照比较符合直觉的方式处理各种可以迭代的类型 —— 比如数组、集合、字符串(中的各个字符)等。
(具体参照:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of)
使用 for ... of 的遍历代码如下:
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
for (var name of names) {
console.log(name);
};
虽然有了这个补丁,用 for ... in 来遍历数组的话仍然不会报错。所以,我们只能把这么重要的事情默念 3 遍:用 for ... of 来遍历数组,用 for ... of 来遍历数组,用 for ... of 来遍历数组。
等等!那 v-for 表达式里,是该用 in 还是 of 呢?
答案是两种都行。Vue 官网上是这么说的:
2)Array.forEach() 函数
除了上面那两个容易混淆的 for ... in 和 for ... of 之外,我们还可以使用数组的 forEach() 函数来遍历每一个元素。
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
names.forEach(function(name) {
console.log(name);
});
或者,换成更时髦的 Lambda 表达式风格的写法,看起来会更加亲切一些。
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
names.forEach(name =>
console.log(name)
);
Array.forEach() 函数比较神奇的地方是,用于处理每个元素的回调函数中可以接收多个参数。
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
names.forEach(function(element, index, array) {
// element: 当前正在处理的元素
// index: 当前正在处理的元素的 index。可选。
// array: 当前正在遍历的数组。可选。
console.log("第 " + index + " 个元素:" + element);
});
使用 Lambda 表达式的版本是这样的:
// 名字的数组
var names = ["张三", "李四", "王五"];
// 输出 names 中的每个名字
names.forEach((element, index, array) => {
// element: 当前正在处理的元素
// index: 当前正在处理的元素的 index。可选。
// array: 当前正在遍历的数组。可选。
console.log("第 " + index + " 个元素:" + element);
});
其中的第 2 个参数 index,也就是当前正在处理的元素的 index,经常会被我们用来显示各个元素的编号。
v-for 中同样也支持这个 index 参数:
3、v-for 的其他用法
Vue 官网上,还有对于 v-for 处理数组之外的其他用法的描述。
https://cn.vuejs.org/v2/guide/list.html
https://cn.vuejs.org/v2/api/#v-for
1)遍历对象(Object)
要理解这个用法,你得先理解万能的 for ... in 是如何遍历对象的。
估计是把对象滥用成了字典 (Map),所以才有了这种特有的、非 Iterable 的遍历。
用到 v-for 中也非常少见,所以要是有时间的话,你自己吧啦吧啦相关的资料吧 😀
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in
2)遍历指定区间
v-for 还提供了一个遍历指定区间内所有整数的便利方式。(不知道是不是受 Python 的启发 ......)
4、思考题
有了 v-for,我们就能做一些比较有意思的东东了。比如说这个:
你可能要速成一下 <table> 组件的用法。
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/table