浅谈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 相关文章推荐
FireFox与IE 下js兼容触发click事件的代码
Nov 20 Javascript
使用Jquery实现点击文字后变成文本框且可修改
Sep 21 Javascript
常用javascript表单验证汇总
Jul 20 Javascript
JavaScript Split()方法
Dec 18 Javascript
springMVC结合AjaxForm上传文件
Jul 12 Javascript
原生JS实现-星级评分系统的简单实例
Aug 21 Javascript
vue之nextTick全面解析
May 17 Javascript
vue教程之toast弹框全局调用示例详解
Aug 24 Javascript
jquery实现左右轮播切换效果
Jan 01 jQuery
vue-cli2打包前和打包后的css前缀不一致的问题解决
Aug 24 Javascript
微信小程序把百度地图坐标转换成腾讯地图坐标过程详解
Jul 10 Javascript
小程序中设置缓存过期的实现方法
Jan 14 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
图象函数中的中文显示
2006/10/09 PHP
使用php get_headers 判断URL是否有效的解决办法
2013/04/27 PHP
php.ini修改php上传文件大小限制的方法详解
2013/06/17 PHP
asp.net网站开发中用jquery实现滚动浏览器滚动条加载数据(类似于腾讯微博)
2012/03/14 Javascript
js批量设置样式的三种方法不推荐使用with
2013/02/25 Javascript
原生js事件的添加和删除的封装
2014/07/01 Javascript
了不起的node.js读书笔记之node的学习总结
2014/12/22 Javascript
异步JS框架的作用以及实现方法
2015/10/29 Javascript
不同js异步函数同步的实现方法
2016/05/28 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
2017/04/07 Javascript
nodeJS进程管理器pm2的使用
2019/01/09 NodeJs
深入分析element ScrollBar滚动组件源码
2019/01/22 Javascript
微信小程序实现单选选项卡切换效果
2020/06/19 Javascript
使用js原生实现年份轮播选择效果实例
2021/01/12 Javascript
[01:10]DOTA2亚洲邀请赛 征战号角响彻全场
2015/01/06 DOTA
浅谈Python程序与C++程序的联合使用
2015/04/07 Python
在Django的视图(View)外使用Session的方法
2015/07/23 Python
图文详解WinPE下安装Python
2016/05/17 Python
Python金融数据可视化汇总
2017/11/17 Python
Django如何实现网站注册用户邮箱验证功能
2019/08/14 Python
Python自动化xpath实现自动抢票抢货
2020/09/19 Python
告诉你怎样写创业计划书
2014/01/27 职场文书
北体毕业生求职信
2014/02/28 职场文书
大学优秀班集体申报材料
2014/05/23 职场文书
人事任命书范文
2014/06/04 职场文书
2014幼儿园大班工作总结
2014/11/10 职场文书
承德避暑山庄导游词
2015/02/03 职场文书
小班上学期个人总结
2015/02/12 职场文书
热爱劳动主题班会
2015/08/14 职场文书
2016教师给学生的毕业寄语
2015/12/04 职场文书
公安忠诚教育心得体会
2016/01/23 职场文书
蓝天保卫战收官在即 :15行业将开展环保分级评价
2019/07/19 职场文书
详解如何在Canvas中添加事件的方法
2021/04/17 Javascript
PHP中国际化的字符串排序和比较对象详解
2021/08/23 PHP
使用python创建股票的时间序列可视化分析
2022/03/03 Python
Linux在两个服务器直接传文件的操作方法
2022/08/05 Servers