浅谈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 相关文章推荐
基于jquery自定义图片热区效果
Jul 21 Javascript
表单元素的submit()方法和onsubmit事件应用概述
Feb 01 Javascript
Javascript编程之继承实例汇总
Nov 28 Javascript
深入探讨前端框架react
Dec 09 Javascript
JavaScript禁止用户多次提交的两种方法
Jul 24 Javascript
实例解析angularjs的filter过滤器
Dec 14 Javascript
bootstrap IE8 兼容性处理
Mar 22 Javascript
jQuery实现动态删除LI的方法
May 30 jQuery
jQuery实现可拖动进度条实例代码
Jun 21 jQuery
Bootstrap 3多级下拉菜单实例
Nov 23 Javascript
如何更好的编写js async函数
May 13 Javascript
JQuery判断radio单选框是否选中并获取值的方法
Jan 17 jQuery
前端主流框架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模板引擎smarty详细介绍
2015/05/26 PHP
PHP在弹框中获取foreach中遍历的id值并传递给地址栏
2017/06/13 PHP
PHP类与对象后期静态绑定操作实例详解
2018/12/20 PHP
laravel5.1 ajax post 传值_token示例
2019/10/24 PHP
php7 错误处理机制修改实例分析
2020/05/25 PHP
突发奇想的一个jquery插件
2010/11/19 Javascript
页面回到顶部的三种实现(锚标记,js)
2012/10/01 Javascript
JS模拟自动点击的简单实例
2013/08/08 Javascript
JS将表单导出成EXCEL的实例代码
2013/11/11 Javascript
IE浏览器IFrame对象内存不释放问题解决方法
2014/08/22 Javascript
使用AngularJS 应用访问 Android 手机的图片库
2015/03/24 Javascript
jquery实现定时自动轮播特效
2015/12/10 Javascript
JavaScript数组去重由慢到快由繁到简(优化篇)
2016/08/26 Javascript
jQuery基于ajax操作json数据简单示例
2017/01/05 Javascript
javascript实现右下角广告框效果
2017/02/01 Javascript
AngularJS中$http的交互问题
2017/03/29 Javascript
vue使用laydate时间插件的方法
2018/11/14 Javascript
vue组件间的参数传递实例详解
2019/04/26 Javascript
刷新页面后让控制台的js代码继续执行
2019/09/20 Javascript
vue 使用 v-model 双向绑定父子组件的值遇见的问题及解决方案
2021/03/01 Vue.js
python函数缺省值与引用学习笔记分享
2013/02/10 Python
Python利用递归和walk()遍历目录文件的方法示例
2017/07/14 Python
python实现视频读取和转化图片
2019/12/10 Python
HTML5适合的情人节礼物有纪念日期功能
2021/01/25 HTML / CSS
Trip.com香港网站:Ctrip携程旗下,全球最大的网上旅游社之一
2016/08/01 全球购物
Links of London官方网站:英国标志性的珠宝品牌
2017/04/09 全球购物
澳大利亚家具和家居用品在线:BROSA
2017/11/02 全球购物
解释一下抽象方法和抽象类
2016/08/27 面试题
String、StringBuffer、StringBuilder有区别
2015/09/18 面试题
医院护士的求职信范文
2013/12/26 职场文书
经典促销广告词大全
2014/03/19 职场文书
珍惜时间演讲稿
2014/05/14 职场文书
2014年国庆节演讲稿
2014/09/02 职场文书
2014幼儿园卫生保健工作总结
2014/12/05 职场文书
redis实现的四种常见限流策略
2021/06/18 Redis
详解Python中的进程和线程
2021/06/23 Python