如何优化表示组织为列的数据的表的数据绑定



在显示组织为带有表的列的数据时,我无法了解如何正确使用计算属性。 我的代码的完整示例可在jsfiddle上找到,这是简短的版本和描述。 我想将此数据呈现为表:

var vueData = {
objects: [
{
name: "objectName",
objectData: [
{prop1: "val1", prop2: "val2"}
]
}
]
}

在此vueData数组的每个元素objectData应显示为一列(该列表示一个月或一天的数据)。objectData元素中的道具不应按原样显示,而应显示为计算值。并且显示的值应反映vueData的变化。

所以我来到了这个 vue 模板:

<table>
<tr>
<th>Object Name</th>
<th>Data Piece 1</th>
<th>Data Piece 2</th>
</tr>
<template v-for="obj in objects">
<tr> 
<th rowspan="2">{{ obj.name }}</th>
<td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop1) }}</td>
</tr>
<tr><td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop2) }}</td></tr>
</template>
</table>

一切都很好,除了我使用了方法,而不是 vue 的计算属性。方法的问题在于它们的结果不会缓存,并且在单个道具更改后,将重新计算所有单元格的值。

我可以为每个具有计算属性的单元格使用 vue 组件而不是tds,但这看起来有点矫枉过正,因为表格可能很大。

从 vue 的角度来看,还有其他解决方案吗?(我的意思是不改变数据结构或表格的外观等)。

我曾想用指令来做到这一点,但没有很好的方法来传递函数和参数,直到我想起我可以使用vnode.context来获取上下文,在那里我可以按名称查找函数,而不是将其作为实际函数传递。

因此,这是一个指令,它使用函数调用的结果更新其元素的textContent

var vueData = {
objects: [{
name: 'Object 1',
objectData: [{
prop1: '1-1-1',
prop2: '1-1-2'
},
{
prop1: '1-2-1',
prop2: '1-2-2'
},
{
prop1: '1-3-1',
prop2: '1-3-2'
}
]
},
{
name: 'Object 2',
objectData: [{
prop1: '2-1-1',
prop2: '2-1-2'
},
{
prop1: '2-2-1',
prop2: '2-2-2'
},
{
prop1: '2-3-1',
prop2: '2-3-2'
}
]
},
{
name: 'Object 3',
objectData: [{
prop1: '3-1-1',
prop2: '3-1-2'
},
{
prop1: '3-2-1',
prop2: '3-2-2'
},
{
prop1: '3-3-1',
prop2: '3-3-2'
}
]
},
]
};
var vue = new Vue({
el: document.getElementById("vue"),
data: vueData,
methods: {
compute: function(prop) {
console.log('computing ' + prop);
return 'computed(' + prop + ')';
}
},
directives: {
cache(el, binding, vnode) {
if (binding.value !== binding.oldValue) {
el.textContent = vnode.context[binding.arg](binding.value);
}
}
},
computed: {
firstProp: function() {
return this.objects[0].objectData[0].prop1;
}
}
});
setTimeout(function() {
vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
th,
td {
border: 1px solid black;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
<table>
<tr>
<th>Object Name</th>
<th>Data Piece 1</th>
<th>Data Piece 2</th>
<th>Data Piece 3</th>
</tr>
<template v-for="obj in objects">
<tr>
<th rowspan="2">{{ obj.name }}</th>
<td v-for="dataPiece in obj.objectData"
v-cache:compute="dataPiece.prop1"
>
</td>
</tr>
<tr>
<td v-for="dataPiece in obj.objectData"
v-cache:compute="dataPiece.prop2">
</td>
</tr>
</template>
</table>
<span>Computed prop1 value = {{ firstProp }}</span>
</div>

我遇到了LinusBorg的这篇文章 动态生成计算属性 他展示了一个将属性映射到计算属性的函数。

我为您的objects变量调整了函数,因为原始变量更专注于平面表单数据(而且也有点令人生畏)。

function mapObjectToComputed({objects}) {
console.log(objects)
let res = {};
objects.forEach((obj,i) => {
obj.objectData.forEach((dat,j) => {
['prop1', 'prop2'].forEach(prop => {
const propModel = `objects_${i}_${j}_${prop}`;
const computedProp = {
get() {
console.log(`Getting ${propModel}`)
const val = this.objects[i].objectData[j][prop];
return val;
}
}
res[propModel] = computedProp;
})
})
})
return res;
}

这是模板的内部部分。我已将prop1更改为新语法,并保留了prop2

<template v-for="(obj, i) in objects">
<tr> 
<th rowspan="2">{{ obj.name }}</th>
<td v-for="(dataPiece, j) in obj.objectData">
{{ fetch(i,j,'prop1') }}
</td>
</tr>
<tr><td v-for="dataPiece in obj.objectData">
{{ compute(dataPiece.prop2) }}
</td></tr>
</template>

该组件是

var vue = new Vue({
el: document.getElementById("vue"),
data: vueData,
methods: {
fetch: function(i,j,prop) {
const propModel = `objects_${i}_${j}_${prop}`
return this[propModel];
},
compute: function(prop) {
console.log('computing ' + prop);
return 'computed(' + prop + ')';
}
},
computed: {
firstProp: function() {
return this.objects[0].objectData[0].prop1;
},
...mapObjectToComputed(vueData)
}
});

超时后的控制台为

Getting objects_0_0_prop1  
computing 1-1-2  
computing 1-2-2  
computing 1-3-2  
computing 2-1-2  
computing 2-2-2  
computing 2-3-2  
computing 3-1-2  
computing 3-2-2  
computing 3-3-2  

因此,只有prop2全面重新计算。

这是小提琴

正如你提到的,你可以使用一个组件。这是我找到的最干净的解决方案。该组件非常简单,已针对此示例进行了调整。如果计算特别昂贵,则可能值得使用。

var vueData = {
objects: [{
name: 'Object 1',
objectData: [{
prop1: '1-1-1',
prop2: '1-1-2'
},
{
prop1: '1-2-1',
prop2: '1-2-2'
},
{
prop1: '1-3-1',
prop2: '1-3-2'
}
]
},
{
name: 'Object 2',
objectData: [{
prop1: '2-1-1',
prop2: '2-1-2'
},
{
prop1: '2-2-1',
prop2: '2-2-2'
},
{
prop1: '2-3-1',
prop2: '2-3-2'
}
]
},
{
name: 'Object 3',
objectData: [{
prop1: '3-1-1',
prop2: '3-1-2'
},
{
prop1: '3-2-1',
prop2: '3-2-2'
},
{
prop1: '3-3-1',
prop2: '3-3-2'
}
]
},
]
};
var vue = new Vue({
el: document.getElementById("vue"),
data: vueData,
methods: {
compute: function(prop) {
console.log('computing ' + prop);
return 'computed(' + prop + ')';
}
},
components: {
cacheResult: {
props: {
fn: Function,
arg: String
},
template: '<td>{{fn(arg)}}</td>'
}
},
computed: {
firstProp: function() {
return this.objects[0].objectData[0].prop1;
}
}
});
setTimeout(function() {
vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
th,
td {
border: 1px solid black;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
<table>
<tr>
<th>Object Name</th>
<th>Data Piece 1</th>
<th>Data Piece 2</th>
<th>Data Piece 3</th>
</tr>
<template v-for="obj in objects">
<tr>
<th rowspan="2">{{ obj.name }}</th>
<td v-for="dataPiece in obj.objectData"
is="cacheResult"
:fn="compute"
:arg="dataPiece.prop1"
>
</td>
</tr>
<tr>
<td v-for="dataPiece in obj.objectData"
is="cacheResult"
:fn="compute"
:arg="dataPiece.prop2">
</td>
</tr>
</template>
</table>
<span>Computed prop1 value = {{ firstProp }}</span>
</div>

最新更新