复杂data的处理方式

  • 在模板中,我们可以使用mustache语法显示一些 data中的数据
  • 但是在某些情况下,我们需要对数据做一些格式化后再显示,或者需要将多个数据结合起来显示
    • 比如:
      • 多个data数据进行运算

        • image.png
      • 三元运算符来决定结果

        • image.png
      • 数据进行某种转化后显示

        • image.png
    • 在模板中使用表达式,可以非常方便的实现,但是设计它们的初衷式用于简单的运算
    • 模板中放入太多的逻辑让 模板过重和难以维护
    • 并且如果每个地方都使用到,那么会大量重复的代码
  • 我们有没有用什么方法可以将逻辑抽离出去呢?
    • 可以,其中一种方式就是将逻辑抽取到一个method中,放到options
      • 缺点,所有的data使用过程就变成了一个方法调用
    • 使用计算属性(computed)推荐

认识计算属性Computed

  • 什么是计算属性?
    • 对于任何任何含有响应式的复杂逻辑,你都应该使用计算属性
    • 计算属性 将 被混入到组件的实例中。所有getter和setter的this上下文自动地绑定为组件实例
  • 计算属性的用法
    • 选项:computed
    • 类型:{[key: string]}: Function | {get: Function, set: Function}

案例学习

案例一:有两个变量 firstName 和 lastName,让它拼接后显示。
案例二:有个score,小于六十显示不及格,大于六十显示及格。
案例三:有一个变量message,记录一段文字:比如Hello World

  • 某些情况下直接显示这段文字
  • 某些情况下需要对这段文字进行反转
插值语法实现
<template id="my-app">
        <h2>{{firstName + " " + lastName}}</h2>
        <h2>{{score >= 60? '及格':'不及格'}}</h2>
        <h2>{{message.split(' ').reverse().join(' ')}}</h2>
</template>
  • 缺点
    • 模板中存在大量的复杂逻辑,不便于维护
    • 当有多次一样的逻辑时,存在重复的代码
    • 多次使用的时候,很多运算也需要多次执行,没有缓存
method 实现
    <template id="my-app">
        <h2>{{getAllName()}}</h2>
        <h2>{{getScoreLv()}}</h2>
        <h2>{{reveseMsg()}}</h2>
    </template>
methods: {
    getAllName() {
        return this.firstName + " " + this.lastName;
    },
    getScoreLv() {
        return this.score >= 60? '及格':'不及格';
    },
    reveseMsg() {
        return this.message.split(' ').reverse().join(' ');
    }
}
  • 缺点
    • 显示的值都变成了方法的调用
    • 多次使用方法的时候,没有缓存,也需要多次计算
computed 实现
  • 计算属性看起来时一个函数,但是在我们使用的时候不需要加(), 因为相当于给某个属性定义了getter获取器
  • 我们发现无论从直观上,还是效果上计算属性都是更好的选择
  • 并且计算属性是有缓存的。
    • 也就是说当,计算属性中引用的data中的值,不发生变化,它就不会重新执行逻辑。直接返回上次计算的结果,
    <template id="my-app">
        <h2>{{getAllName}}</h2>
        <h2>{{getAllName}}</h2>
        <h2>{{getScoreLv}}</h2>
        <h2>{{reveseMsg}}</h2>
    </template>
computed: {
    getAllName() {
        console.log(1);
        return this.firstName + " " + this.lastName;
    },
    getScoreLv() {
        return this.score >= 60? '及格':'不及格';
    },
    reveseMsg() {
        return this.message.split(' ').reverse().join(' ');
    }
}
计算属性 vs methods
  • 计算属性 有缓存 methods 没有
<template id="my-app">
    <h2>{{getAllName1}}</h2>
    <h2>{{getAllName1}}</h2>
    <h2>{{getAllName2()}}</h2>
    <h2>{{getAllName2()}}</h2>
</template>
methods: {
    getAllName2() {
        console.log("getAllName2");
        return this.firstName + " " + this.lastName;
    }
},
computed: {
    getAllName1() {
        console.log("getAllName1");
        return this.firstName + " " + this.lastName;
    }
}

image.png

  • 很显然,使用方法来抽离逻辑,每次都需要调用该方法,而计算属性基于它的缓存机制,会发现数据并没有发生改变,就会直接返回上一次运算的结果。

计算属性的缓存

这是什么原因呢?

  • 计算属性会基于它们的依赖关系进行缓存
  • 在数据不发生变化时,计算属性是不需要重新计算的
  • 但是,依赖数据发生变化,在使用时,计算属性依然会重新进行计算

计算属性的 getter 和 setter

  • 和Object.defineProperty() 相似

  • 在大多情况下,我们只需要getter方法,所以会直接写成一个函数

  • 但是,如果我们确实想设置计算属性的值时,可以通过定义setter方法

<template id="my-app">
    <h2>{{fullName}}</h2>
    <button @click="changeName">修改名字</button>
</template>
<script src="../js/vue.js"></script>
<script>
    const App = {
        template: '#my-app',
        data() {
            return {
                firstName: "Kobe",
                lastName: "Bryant",
                score: 80,
                message: "Hello World"
            };
        },
        methods: {
            changeName() {
                this.fullName = "dsfas adsfasd";
            }
        },
        computed: {
            fullName: {
                get: function() {
                    return this.firstName + " " + this.lastName;
                },
                set: function(newValue) {
                    console.log(newValue);
                    const names = newValue.split(' ');
                    [this.firstName, this.lastName] = names;
                }
            }
        }
    }
    Vue.createApp(App).mount("#app");
</script>

Vue 对 computed 的getter和setter的处理

  • Vue 源码内部对 computedOptions 对象中的元素进行逻辑判断。
  • 当前属性是一个函数时,直接给函数绑定上下文作用域, 如果不是就取对象中的get
  • image.png