[关闭]
@hotjp 2017-12-25T08:27:56.000000Z 字数 4829 阅读 3294

比较过滤器、计算属性和方法(filter、computed、method)


现在有这样一个场景,我们通过某种方式得到一个这样的数据

data = {text: '一个text', aggr: '求和', xText: ''};

然后我们需要将这个对象的xTest属性渲染到页面上,并且当xTest值为空时我们要输出 data.text + '-' + data.aggr
而且以后还可能动态改变data.xText 的值
针对这种场景,在使用vue.js的情况下,理所应当最先想到的方法是用||
{{oneMessage.xText || oneMessage.text + '-' + oneMessage.aggr}}

这就和写函数留个默认值的思路一样

  1. function ax=10){
  2. console.log(x)
  3. };
  4. a();
  5. 10

受这种写法的启发,自然就想试试过滤器(filter)的写法
先来一个最简单的过滤器

  1. <template>
  2. <div>
  3. {{231|formatMoney}}
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. data() {
  9. return {
  10. };
  11. },
  12. method: {},
  13. filters: {
  14. formatMoney: function (val) {
  15. if (typeof (val)=== 'number') {
  16. return '¥'+val
  17. } else {
  18. return val
  19. }
  20. }
  21. }
  22. };
  23. </script>
  24. <style>
  25. </style>

当扔入过滤器的值是数值型就在之前加个¥的符号
vue过滤器的链接 https://cn.vuejs.org/v2/guide/filters.html#ad .
然后来看看针对这个问题我想到的过滤器写法

  1. <template>
  2. <div>
  3. {{oneMessage|xEmpty}}
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. data() {
  9. return {
  10. oneMessage: {text: '一个text', aggr: '求和', xText: ''}
  11. };
  12. },
  13. filters: {
  14. xEmpty: function (val) {
  15. let value = val.xText;
  16. if (value === null || value === '' || value === undefined) {
  17. return val.text + '-' + val.aggr;
  18. } else {
  19. return value;
  20. }
  21. }
  22. }
  23. };
  24. </script>

虽然实现了效果但并不很合理,
1.是我们需要渲染的属性值而并非对象。
2.是官方文档里对于指令v-textv-html的说明,期望值都是string,所以定界符{{ }}{{{ }}}也是同理,对象并不是被期望的类型。

在这之前我曾尝试直接在定界符里填入对象的属性

  1. {{oneMessage.xText|xEmpty}}

但会带来新的问题,
当我将oneMessage.xText扔进过滤器后,我并没有想到什么方法通过oneMessage.xText能得到oneMessage的其他属性值,也就是说我并不知道该如何通过一个对象的属性值拿到这个对象本身。所以这个思路暂时保留,以后讨论。

那么我们换个思路,近乎万能的计算属性(computed),当我们得到值的时候就可以对这个值进行运算,这样我们就能提前得到data.text + '-' + data.aggr的值
看一下文档上的例子

  1. <div id="example">
  2. <p>Original message: "{{ message }}"</p>
  3. <p>Computed reversed message: "{{ reversedMessage }}"</p>
  4. </div>
  5. <script>
  6. var vm = new Vue({
  7. el: '#example',
  8. data: {
  9. message: 'Hello'
  10. },
  11. computed: {
  12. // 计算属性的 getter
  13. reversedMessage: function () {
  14. // `this` 指向 vm 实例
  15. return this.message.split('').reverse().join('')
  16. }
  17. }
  18. })
  19. </script>

vue计算属性的链接 https://cn.vuejs.org/v2/guide/computed.html#ad .

就在这时,传来了一个好消息:我们调整一下需求。。。

  1. oneMessage: [
  2. {text: '一个text', aggr: 'sum', xText: ''},
  3. {text: '淘气的text', aggr: 'average', xText: ''},
  4. {text: '拖后腿的text', aggr: 'average', xText: ''},
  5. {text: '吃饭中的text', aggr: 'hahah', xText: ''},
  6. {text: '没定上饭的text', aggr: 'sum', xText: ''}
  7. ]

如上,我们拿到了这样的一个数组,aggr属性也需要格式化一下,规则大概是这样

  1. sum => '求和'
  2. average => '平均值'
  3. 其他输出它本身

显然需要先格式化一下aggr属性,所以我的思路如下

  1. <template>
  2. <div>
  3. <div v-for="(item, index) in fetchMessage" :key="index">
  4. display : {{item.xText || item.replaceText}}
  5. <br>
  6. modify xText
  7. <input type="text" v-model="item.xText">
  8. <br>
  9. modify aggr
  10. <input type="text" v-model="item.aggr">
  11. <br>
  12. modify text
  13. <input type="text" v-model="item.text">
  14. <br>
  15. </div>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. data: function () {
  21. return {
  22. oneMessage:
  23. [
  24. {text: '一个text', aggr: 'sum', xText: ''},
  25. {text: '淘气的text', aggr: 'average', xText: ''},
  26. {text: '拖后腿的text', aggr: 'average', xText: ''},
  27. {text: '吃饭中的text', aggr: 'hahah', xText: ''},
  28. {text: '没定上饭的text', aggr: 'sum', xText: ''}
  29. ]
  30. }
  31. },
  32. methods: {
  33. formatAggr: function (val) {
  34. //可能用switch更好
  35. if (val === 'sum') {
  36. return '求和'
  37. } else if (val === 'average') {
  38. return '平均值'
  39. } else {
  40. return val;
  41. }
  42. }
  43. },
  44. computed: {
  45. fetchMessage: {
  46. get: function () {
  47. let one = this.oneMessage;
  48. for (let i = 0; i < one.length; i++) {
  49. let value = one[i].xText;
  50. one[i].replaceText = one[i].text + '-' + this.formatAggr(one[i].aggr);
  51. }
  52. return one;
  53. },
  54. set: function () {
  55. }
  56. }
  57. }
  58. };
  59. </script>
  60. <style scoped>
  61. </style>

以上是我想到的一种思路,通过计算属性为数组中的每个对象中加一个replaceText属性用来存储默认显示的数据,在这之前用定义好的formatAggr方法格式化aggr属性,这个思路的缺点就是每个成员对象都要多一个可能用不到的属性,而且计算的对象是整个大数组,但也有他的优势就是比较直观
接下来的思路就不是很直观,但操作的对象是是单个成员对象,在这之前先看一下官方对method的解释vue方法的链接 https://cn.vuejs.org/v2/api/#methods .

  1. var vm = new Vue({
  2. data: { a: 1 },
  3. methods: {
  4. plus: function () {
  5. this.a++
  6. }
  7. }
  8. })
  9. vm.plus()
  10. vm.a // 2

其实就和平时写function没啥区别

  1. <template>
  2. <div class="computed_test">
  3. <div v-for="(item, index) in oneMessage" :key="index">
  4. display : {{format(index)}}
  5. <br>
  6. modify xText
  7. <input type="text" v-model="item.xText">
  8. <br>
  9. modify aggr
  10. <input type="text" v-model="item.aggr">
  11. <br>
  12. modify text
  13. <input type="text" v-model="item.text">
  14. <br>
  15. </div>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. data: function () {
  21. return {
  22. oneMessage:
  23. [
  24. {text: '一个text', aggr: 'sum', xText: 'xixi'},
  25. {text: '淘气的text', aggr: 'average', xText: ''},
  26. {text: '拖后腿的text', aggr: 'average', xText: ''},
  27. {text: '吃饭中的text', aggr: 'hahah', xText: ''},
  28. {text: '没定上饭的text', aggr: 'sum', xText: ''}
  29. ]
  30. }
  31. },
  32. methods: {
  33. format: function (i) {
  34. let selObj = this.oneMessage[i];
  35. if(selObj.xText){
  36. return selObj.xText;
  37. }else {
  38. return selObj.text + '-' + this.formatAggr(selObj.aggr);
  39. }
  40. },
  41. formatAggr: function (val) {
  42. //可能用switch更好
  43. if (val === 'sum') {
  44. return '求和'
  45. } else if (val === 'average') {
  46. return '平均值'
  47. } else {
  48. return val;
  49. }
  50. }
  51. },
  52. };
  53. </script>
  54. <style scoped>
  55. .computed_test{
  56. width: 600px;
  57. }
  58. </style>

这是一种用method实现的方法,vue里的method往往是一种较为通用的解决问题的思路,但往往不是最好的办法。
目前这个需求看起来写方法似乎也是一种比较好的解决手段,除了display : {{format(index)}}非常不语义化。
但操作单个成员对象就意味着这个方法执行了n次,反正不要讲道理了,我不喜欢这个思路。

当然在我也尝试了很多种其他方法,比如上面代码中formatAggr方法做成一个过滤器,但很遗憾我也没能成功,全是铺天盖地的报错

  1. {{oneMessage[0].xText || oneMessage[0].text + '-' + (oneMessage[0].aggr | formatAggr)}}
  2. {{oneMessage[0].xText || oneMessage[0].text + '-' + (oneMessage[0].aggr) | formatAggr}}

以上两种写法都不能正常执行,我也尝试了|||还有()的各种组合,没能成功,但过滤器如果可行的话应该是一个比较好的思路,这个思路先保留,可以一起讨论下。

比较这三种方法,显然第一感觉一定是过滤器(filter)是比较合适的,但vue的作者可能在设计时就没打算让过滤器有这么大的能力,在思考的过程中也没能找到什么有价值的资料。

计算属性(computed)是一个功能很强大的工具,往往都是一种比较合适的选择。

方法(method)是一种较通用的思路,并且vue封装的大多数指令都能用它展开,但我并不喜欢它。

所以,我更倾向使用计算属性。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注