Vue.JS入门篇--列表渲染

JavaScript09

Vue.JS入门篇--列表渲染,第1张

查看一下效果,应该很容易得到结果

有时我们可能需要重复一个包含多个节点的块,这时可以用 <template>标签包裹这个块。这里的 <template>标签只起到语义上的包裹作用,其本身不会被渲染出来。例如:

简单值 (primitive value) 即字符串、数字、boolean 等并非对象的值。对于包含简单值的数组,你可用 $value 直接访问值:

有时我们可能想要更明确地访问当前作用域的变量而不是隐式地回退到父作用域。你可以通过提供一个参数给 v-repeat 指令并用它作为将被迭代项的别名:

Vue.js 内部对被观察数组的变异方法 (mutating methods,包括 push(), pop(), shift(), unshift(), splice(), sort() 和 reverse()) 进行了拦截,因此调用这些方法也将自动触发视图更新。

下面是一个演示的例子,点击按钮的时候数据项会被移除

Vue.js 给被观察数组添加了两个便捷方法:$set() 和 $remove() 。

你应该避免直接通过索引来设置数据绑定数组中的元素,比如 demo.items[0] = {},因为这些改动是无法被 Vue.js 侦测到的。你应该使用扩展的 $set() 方法:

$remove() 只是 splice()方法的语法糖。它将移除给定索引处的元素。当参数不是数值时,$remove() 将在数组中搜索该值并删除第一个发现的对应元素。

当你使用非变异方法,比如filter(), concat() 或 slice(),返回的数组将是一个不同的实例。在此情况下,你可以用新数组替换旧的数组:

你可能会认为这将导致整个列表的 DOM 被销毁并重新渲染。但别担心,Vue.js 能够识别一个数组元素是否已有关联的 Vue 实例, 并尽可能地进行有效复用。

在某些情况下,你可能需要以全新的对象(比如 API 调用所返回的对象)去替换数组。如果你的每一个数据对象都有一个唯一的 id 属性,那么你可以使用 track-by 特性参数给 Vue.js 一个提示,这样它就可以复用已有的具有相同 id 的 Vue 实例和 DOM 节点。

例如:你的数据是这个样子的

那么你可以像这样给出提示:

在替换 items 数组时,Vue.js 如果碰到一个有 _uid: '88f869d' 的新对象,它就会知道可以直接复用有同样 _uid 的已有实例。 在使用全新数据重新渲染大型 v-repeat 列表时,合理使用 track-by 能极大地提升性能。

你也可以使用 v-repeat 遍历一个对象的所有属性。每个重复的实例会有一个特殊的属性 $key。对于简单值,你也可以象访问数组中的简单值那样使用 $value 属性。

在 ECMAScript 5 中,对于给对象添加一个新属性,或是从对象中删除一个属性时,没有机制可以检测到这两种情况。针对这个问题,Vue.js 中的被观察对象会被添加三个扩展方法: $add(key, value), $set(key, value) 和 $delete(key)。这些方法可以被用于在添加 / 删除观察对象的属性时触发对应的视图更新。方法 $add 和 $set 的不同之处在于当指定的键已经在对象中存在时 $add 将提前返回,所以调用 obj.$add(key) 并不会以 undefined 覆盖已有的值。

v-repeat 也可以接受一个整数。在这种情况下,它将重复显示一个模板多次。 下面的例子将迭代3次。

有时候我们只需要显示一个数组的过滤或排序过的版本,而不需要实际改变或重置原始数据。Vue 提供了两个内置的过滤器来简化此类需求: filterBy 和 orderBy。

把看到的区域当画布,创建足够能展现界面的Dom就够了。

比如一个屏幕的高度一般是800像素左右,假设一条记录占用的高度是20像素,我们只用创建40个对象即可。

接下来就是对这40个对象进行数据填充,通过滚动条调整填充数据的起始下标。

10W条记录,IE下有些卡

<style type="text/css">

.panel{

overflow:scroll

width:200px

height:80%

}

.panel .scroll{

}

.item{

width:500px

height:20px

}

.odd{

background-color:#ccc

}

.items{

overflow:hidden

position:absolute

}

.red{

color:red

}

.green{

color:green

}

</style>

<script>

window.console = window.console || { log: function() {} }

function absolutePoint(element) {

var result = [element.offsetLeft, element.offsetTop]

element = element.offsetParent

while (element) {

result[0] += element.offsetLeft

result[1] += element.offsetTop

element = element.offsetParent

}

return result

}

function ListView(options){

options = options || {}

var self = this,

$C = function(tagName) { return document.createElement(tagName)}, // 创建节点

p,

height,

item_height, // 项高

view_count, // 可见项条数

parent = options.parent || document.body, // 容器

height, // 面板历史高度

div_panel = $C("div"),

div_scroll = $C("div"),

div_items = $C("div"),

div_items_list = [$C("div")],

freed = [div_panel, div_scroll, div_items]// 可释放的对象

div_panel.className = "panel"

parent.appendChild(div_panel)

div_items.className = "items"

document.body.appendChild(div_items)

div_scroll.className = "scroll"

div_panel.appendChild(div_scroll)

div_panel.onscroll = function() {

doChange()

}

div_panel.onresize = function() {

doChange()

}

div_items_list[0].className = "item"

div_items.appendChild(div_items_list[0])

div_scroll.style.width = div_items_list[0].clientWidth + "px"

item_height = div_items_list[0].clientHeight

p = absolutePoint(div_panel)

with(div_items.style) {

left = p[0] + "px"

top = p[1] + "px"

width = div_panel.clientWidth

height = div_panel.clientHeight

}

/**

* 界面改变

*/

function doChange() {

if (!item_height) return

var i, div

if (height != div_panel.clientHeight) {

height = div_panel.clientHeight

view_count = parseInt(height / item_height)

for (i = div_items_list.lengthi <view_counti++) {

div = $C("div")

div.className = "item" + (i % 2 == 0 ? "" : " odd")

div_items.appendChild(div)

div_items_list.push(div)

}

for (i = 0i <div_items_list.lengthi++) {

div_items_list[i].style.display = i <view_count ? "" : "none"

}

div_scroll.style.height = div_panel.clientHeight + options.count - view_count + "px"

console.log(["view_count", view_count])

}

div_items.scrollLeft = div_panel.scrollLeft

if (!options.ondrawitem) return

i = Math.min(view_count, div_items_list.length)

while(i--) {

// 重新绘制

options.ondrawitem(i + div_panel.scrollTop, div_items_list[i])

}

}

doChange()

this.doChange = doChange

/**

* 释放Dom对象

*/

this.dispose = function() {

var i = freed.length

while(i--) {

freed[i].parentNode.removeChild(freed[i])

}

i = freed.length

while(i--) {

div_items_list[i].parentNode.removeChild(div_items_list[i])

}

}

}

function format(template, json) {

if (!json) return template

return template &&template.replace(/\$\{(.+?)\}/g, function() {

return json[arguments[1]]

})

}

window.onload = function() {

var i = 100000, data = new Array(i)

while(i--) {

data[i] = { index: i, random: Math.random(), key: (+new Date()).toString(36) }

}

var listview = new ListView({

count: data.length,

ondrawitem: function(i, div) {

div.innerHTML = format(" <em>${index} </em>  <span class=\"red\">${random} </span>  <span class=\"green\">${key} </span>", data[i])

}

})

}

</script>

1. 首先,浏览器会解析HTML文档,将其转换为DOM树;

2. 然后,浏览器会解析CSS文档,将其转换为CSSOM树;

3. 接着,浏览器会将DOM树和CSSOM树合并,形成渲染树;

4. 接下来,浏览器会根据渲染树来布局,计算每个节点的位置;

5. 最后,浏览器会将渲染树渲染到屏幕上,显示出一大一小两个包含的圆。