浅谈vue.js中v-for循环渲染


Posted in Javascript onJuly 26, 2017

这两天学习了Vue.js 感觉v-for循环渲染这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记。

一、简介

vue.js 的循环渲染是依赖于 v-for 指令,它能够根据 vue 的实例里面的信息,循环遍历所需数据,然后渲染出相应的内容。它可以遍历数组类型以及对象类型的数据,js 里面的数组本身实质上也是对象,这里遍历数组和对象的时候,方式相似但又稍有不同。

(一)遍历对象

<div id="app">
  <ul>
    <li v-for="(val, key, index) in me">{{index}}. {{key}}: {{val}}</li>
  </ul>
</div>
...
var vm = new Vue({
  el: '#app',
  data: {
    me: {
      name: 'Dale',
      age: 22,
      sex: 'male',
      height: 170
    }
  }
});

这里,v-for 接收的参数相对较复杂,但是可以分为三个部分:

(1)括号及其内的遍历结果信息(val, key, index)

其中,val 是遍历得到的属性值,key 是遍历得到的属性名,index 是遍历次序,这里的 key/index 都是可选参数,如果不需要,这个指令其实可以写成 v-for="val in me";

(2)遍历关键词 in

in 可以使用 of 替代,官方的说法是“它是最接近 JavaScript 迭代器的语法”,但其实使用上并没有任何区别;

(3)被遍历对象 me

me 是绑定在实例 data属性上的一个属性,实际上,它是有一个执行环境的,也即是我们接触最多的 vue 实例,模板中,我们仍旧可以像在 methods 以及计算属性中一样,通过 this 访问到它,这里的 me 其实就相当于 this.me,模板中直接写 this.me 也是可行的。

渲染结果如下:

<div id="app">
  <ul>
    <li>0. name: Dale</li>
    <li>1. age: 22</li>
    <li>2. sex: male</li>
    <li>3. height: 170</li>
  </ul>
</div>

(二)遍历数组

<div id="app">
  <ul>
    <li v-for="(item, index) in items">{{index}}. {{item}}</li>
  </ul>
</div>
...
var vm = new Vue({
  el: '#app',
  data: {
    items: ['apple', 'tomato', 'banana', 'watermelon']
  }
});

和遍历对象相类似,最大的不同点在于对象的 “key” 和 “index” 是一致的,所以这里我们只需要取一个 index 即可,上面代码的渲染结果如下:

<div id="app">
  <ul>
    <li>0. apple</li>
    <li>1. tomato</li>
    <li>2. banana</li>
    <li>3. watermelon</li>
  </ul>
</div>

(三)遍历“整数”

理论上来说,整数并不是一个可遍历的单元,但是 vue 这里相当于给我们提供了一个方便方式来减少重复代码。

<div id="app">
  <ul>
    <li v-for="n in num">{{n}}</li>
  </ul>
</div>
...
var vm = new Vue({
  el: '#app',
  data: {
    num: 3
  }
});

渲染结果如下:

<div id="app">
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
</div>

二、实际应用

(一)对象、数组 & 组件

实际应用过程中,我们单独使用数组或者对象去描述我们的数据的情况很少,更常见的模式是结合了数组和对象两部分内容:

<div id="app">
   <ul class="persons">
    <li v-for="person in persons">name: {{person.name}}, age: {{person.age}};</li>
  </ul> 
</div>
...
var vm = new Vue({
  el: '#app',
  data: {
    persons: [
      {
        name: 'Dale',
        age: 22
      }, 
      {
        name: 'Tim',
        age: 30
      },
      {
        name: 'Rex',
        age: 23
      }
    ]
  }
});

本质上是遍历的一个数组,但是数组的每个元素却是一个对象,也就是上面的 person,我们可以通过 [] 以及 . 两种方式去访问这个对象的属性,比如这里的 name / age,渲染结果如下:

<div id="app">
  <ul class="persons">
    <li>name: Dale, age: 22;</li>
    <li>name: Tim, age: 30;</li>
    <li>name: Rex, age: 23;</li>
  </ul>
</div>

实际上,更加常用且强大的模式,是使用组件与数组/对象进行组合,操作方式与上面相类似。

(二)与 v-if 组合

添加了 v-for 指令的标签,实际上也可以同时添加 v-if 指令,但值得注意的是,v-for 的优先级更高,渲染模板时,相当于对每次遍历的结果进行了一次条件判断。

<div id="app">
   <ul class="persons">
    <li v-for="person in persons" v-if="person.age >= 23">name: {{person.name}}, age: {{person.age}};</li>
  </ul> 
</div>
...
var vm = new Vue({
  el: '#app',
  data: {
    persons: [
      {
        name: 'Dale',
        age: 22
      }, 
      {
        name: 'Tim',
        age: 30
      },
      {
        name: 'Rex',
        age: 23
      }
    ]
  }
});

这里先遍历了 persons 的所有元素,然后检查每次得到的 person 的是否大于或等于 23,是则输出,否则不输出,渲染结果如下:

<div id="app">
  <ul class="persons">
    <li>name: Tim, age: 30;</li>
    <li>name: Rex, age: 23;</li>
  </ul>
</div>

如果要让 v-if 指令的优先级更高,可以考虑在 v-for 指令所绑定的标签的父级上添加 v-if 指令。

三、注意事项

(一)key

与 v-for 一样,在不绑定 key 属性的情况下,vue 默认会重用元素以提高性能,如果不需要它的默认行为,显式绑定一个唯一的 key 即可。

(二)数据 -> 视图更新

vue 的视图更新是依赖于 getter/setter 的,如果直接修改、增加、删除数组元素,并不会触发视图的更新。这里 vue 重写了如下方法:

  • push
  • pop
  • shift
  • unshift
  • splice
  • sort
  • reverse

当通过它们修改数据的时候,将会触发视图的更新。

new Vue({
  data: {
    arr: [1, 2, 3]
  }
});

比如上面这种情况,如果我们想要在执行 arr 的 push 等方法,因为 push 是数组类型数据从 Array.prototype.push 继承过来的,所以我们一般情况下有两种实现方式。

(1)修改 Array.prototype

Array.prototype.push = function () {
  console.log(1);
}
([]).push();        // 1

这里我们修改了 Array.prototype 上的 push 方法,但是实际上,整个 prototype 属性都可以被重写,如 Array.prototype = xxx,这样做的好处很明显,在这一处进行修改,之后所有的数组类型都可以直接使用这个重写后的方法,实现和使用都非常简单;但是这样带来的副作用也特别明显,容易影响到其它的代码,其它使用这段代码的地方,除了功能实现上可能受到影响外,效率上也会有较大影响,原生 js 的代码都是经过特殊优化的,我们重写实现,效率肯定会受到影响,最重要的是,如果大家的代码在同一个环境下运行,然后都尝试重写同一个方法的话,最终结果不言而喻。

(2)增加自有方法

var arr = [];

arr.push = function () {
  console.log(1);
}

arr.push();         // 1
Array.prototype.push.toString();  // "function push() { [native code] }"

这里修改了 arr 的 push 方法, 但是并不涉及 Array.prototype.push,因为读写一个对象的属性/方法的时候,js 总是先尝试访问 “ownproperty”,也就是 “hasOwnProperty” 所检测的内容,这里我们姑且将其称为“自有属性(方法)”。读取数据的时候,如果没有读取到内容,那么 js 会尝试向上搜索 __proto__ 上的数据;写数据的时候,如果有这个自有属性,则会将其覆盖,如果没有,则将其作为自有属性添加到改对象上,而不会尝试将其添加到 __proto__ 上,这样的规则,也是为了防止“子类”以外修改“父类”的属性、方法等。这种实现方式虽然可以避免上面修改 Array.prototype 的一系列缺点,但是它的问题就更加明显,因为每次创建这样一个“数组”,就要重新实现/绑定这样一系列方法,它所带来的开发效率、性能问题不容小觑。

(3)vue 的实现方式

var arr = [];
new Vue({
  data: {
    arr: arr
  }
});

arr.push.toString();    // "function mutator() {var arguments$1 = arguments;... 这是 vue 自己的实现
Array.prototype.push.toString();   // "function push() { [native code] }"... 这是浏览器原生的实现
arr.hasOwnProperty('push');      // false 说明不是自有属性

以上说明 vue 既不是修改了 Array.prototype.push,又不是修改了自有属性。但我们通过 instanceof 操作符检查的时候,arr 又是 Array 的一个实例,那么它到底是怎么弄的实现的呢?或者说 vue 的 push 藏在哪儿呢?

var base = [];
var arr = [];

base.push = function () {
  console.log(1);
};
arr.__proto__ = base;
arr.push();     // 1
arr.__proto__.push(); // 1
arr.__proto__.push.toString(); // "function push() { [native code] }"

实际上,vue 是利用了类似上面的方式,先创建了一个 Array 的实例,也就是一个数组类型的基础对象 base,然后为它添加了一个自有方法 push,最后用 base 覆盖了需要扩展的 arr 对象的 __proto__ 属性。

浅谈vue.js中v-for循环渲染

这里需要重写 push 等方法的数组,我们只需要将其 __proto__ 指向 base 数组,在读新创建的数组的 push 的时候,发现它并没有这样一个自有方法,那么它就尝试读 __proto__ 上的方法,发现 __proto__ 属性(也即 base 数组)上有这样一个自有方法,那么它就不必再向上搜索而直接使用 base.push。

通过这种方式,我们不必为每一个数组重写一遍 push 方法,也不必去修改 Array.prototype ,看起来倒像是一个两全其美的方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS函数验证总结(方便js客户端输入验证)
Oct 29 Javascript
JavaScript弹出窗口方法汇总
Aug 12 Javascript
微信内置浏览器私有接口WeixinJSBridge介绍
May 25 Javascript
Jquery ajax基础教程
Nov 20 Javascript
浏览器兼容的JS写法总结
Apr 27 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
Jul 13 Javascript
AngularJS实现的2048小游戏功能【附源码下载】
Jan 03 Javascript
vue上传图片到oss的方法示例(图片带有删除功能)
Sep 27 Javascript
详解离线安装npm包的几种方法
Nov 25 Javascript
可能被忽略的一些JavaScript数组方法细节
Feb 28 Javascript
详解js 创建对象的几种方法
Mar 08 Javascript
vue 中使用 watch 出现了如下的报错的原因分析
May 21 Javascript
前端主流框架vue学习笔记第一篇
Jul 26 #Javascript
关于vue.js组件数据流的问题
Jul 26 #Javascript
Vue.js弹出模态框组件开发的示例代码
Jul 26 #Javascript
VueJs单页应用实现微信网页授权及微信分享功能示例
Jul 26 #Javascript
node实现简单的反向代理服务器
Jul 26 #Javascript
Angular项目中$scope.$apply()方法的使用详解
Jul 26 #Javascript
Vue中计算属性computed的示例解读
Jul 26 #Javascript
You might like
用php获取远程图片并把它保存到本地的代码
2008/04/07 PHP
PHP自定义图片缩放函数实现等比例不失真缩放的方法
2016/08/19 PHP
Yii框架结合sphinx,Ajax实现搜索分页功能示例
2016/10/18 PHP
PHP 实现公历日期与农历日期的互转换
2017/09/13 PHP
PHP 实现文件压缩解压操作的方法
2019/06/14 PHP
php设计模式之建造器模式分析【星际争霸游戏案例】
2020/01/23 PHP
js下判断 iframe 是否加载完成的完美方法
2010/10/26 Javascript
dtree 网页树状菜单及传递对象集合到js内,动态生成节点
2012/04/14 Javascript
jQuery创建平滑的页面滚动(顶部或底部)
2013/02/26 Javascript
JavaScript中最简洁的编码html字符串的方法
2014/10/11 Javascript
js实现屏幕自适应局部代码分享
2015/01/30 Javascript
JS简单实现禁止访问某个页面的方法
2016/09/13 Javascript
weUI应用之JS常用信息提示弹层的封装
2016/11/21 Javascript
简单实现jquery焦点图
2016/12/12 Javascript
解析Vue2.0双向绑定实现原理
2017/02/23 Javascript
AngularJS中使用three.js的实例详解
2017/07/21 Javascript
深入浅析Node.js 事件循环、定时器和process.nextTick()
2018/10/22 Javascript
Vue 实现从文件中获取文本信息的方法详解
2019/10/16 Javascript
Python读写Excel文件方法介绍
2014/11/22 Python
深入浅析ImageMagick命令执行漏洞
2016/10/11 Python
解决Pycharm无法import自己安装的第三方module问题
2018/05/18 Python
利用Pytorch实现简单的线性回归算法
2020/01/15 Python
浅谈Python3实现两个矩形的交并比(IoU)
2020/01/18 Python
纯HTML5+CSS3制作图片旋转
2016/01/12 HTML / CSS
如何用css3实现switch组件开关的方法
2018/02/09 HTML / CSS
SHEIN香港:价格实惠的女性时尚服装
2018/08/14 全球购物
Vans澳大利亚官网:购买鞋子、服装及配件
2019/09/05 全球购物
中国好声音广告词
2014/03/18 职场文书
优秀乡村医生事迹材料
2014/05/28 职场文书
小学数学课题方案
2014/06/15 职场文书
舞蹈兴趣小组活动总结
2014/07/07 职场文书
机关驾驶员违规检讨书
2014/09/13 职场文书
南京市纪委监察局整改方案
2014/09/16 职场文书
2014年大班保育员工作总结
2014/12/02 职场文书
会计岗位职责
2015/02/03 职场文书
2015年机关党建工作总结
2015/05/22 职场文书