Vue 计算属性简析

更新日期: 2019-12-22 阅读: 2.4k 标签: 属性

vue Computed

Vue 开发人员必然使用过计算属性(Computed Properties):你可以像绑定 data 属性一样在模板中绑定计算属性;计算属性一般依赖一个或多个 data 属性,并返回它们复杂逻辑下的状态;当这些依赖属性变更时,模版中绑定的计算属性也会随之更新。那问题来了,vue 是怎么实现这个机制的呢? 我看了一篇外文是讲解 computed 实现的,还挺有趣。源码太过复杂这里就不展开了,我想用一个简单的实例介绍一下它的工作原理。

let gift = new Vue({
    data: {
        price: 0,
    },
    computed: {
        status () { 
            return price > 1024 ? 'Smile' : 'Cry';
        }
    }
})


Object.defineProperty

首先回忆一下某 JS 原生 api——Object.defineProperty(obj, prop, descriptor)。

这个方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
  • obj:要在其上定义属性的对象
  • prop:要定义或修改的属性的名称
  • descriptor:将被定义或修改的属性描述符

示例如下:

let obj = {};

Object.defineProperty(obj, 'val', {
    get () {
        return 1024;
    }
});

console.log(obj.val); // 1024

该方法允许精确添加或修改对象的属性。get是其中的一个描述符,它会给该属性提供 getter 的方法。当访问该属性时,该方法会被执行。在如上代码里,尽管val看着像obj的一个属性,但在调用时内部事实上运行的是一个 get 方法。


data 属性

VUE 里有就一套构造函数,它可以将一个普通的对象构造为一个可观察的对象。VUE 利用的就是Object.defineProperty。以 data 属性为例,这些属性被调用或修改时,会触发的是一系列的 get 和 set 方法。这些对象被称为reactive Property。下面写了一个简单的函数defineReactive(obj, key, val)来模拟构造 data Reactive Property的过程。

function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get () {
            return val;
        },
        set (newVal) {
            val = newVal;
        }
    })
}
let gift = {};
defineReactive(gift, 'price', 250);

console.log(gift.price); // 250
gift.price = 1025;
console.log(gift.price); // 1025


Computed 方法

接着我们再写一个defineComputed(obj, key, computeFunc, updateCallback)来模拟 computed 方法里的构造实现。

function defineComputed (obj, key, computeFunc, updateCallback) {
    Object.defineProperty (obj, key, {
      get () {
        // call the compute function and return the value
        return computeFunc.call(obj);
      }
    })
}

defineComputed(
    gift, 
    'status', 
    function () {
        return this.price > 1024 ? 'Smile' : 'Cry';
    }
)

gift.price = 88;
console.log(gift.status) // Cry

gift.price = 1025;
console.log(gift.status) // Smile

嗯,很直白!当 price 变化后,status 的结果也随之变更。到此为止我们已经可以大体理解 data 数据和 computed 方法之间是如何关联的了。


Update 发布

那如何实现改变 data 属性,自动更新模版中绑定的计算属性呢? 前面defineComputed (obj, key, computeFunc, updateCallback)留了一个参数updateCallback还未使用,我想用它来模拟一下自动更新模版这个操作。

这里先定义一个全局的中介者用于暂存 update 方法:

let Mediator = { 
    target: null
};

我们补完一下defineComputed方法:

function defineComputed (obj, key, computeFunc, updateCallback) {

    function update () {
        let val = computeFunc.call(obj);
        updateCallback.call(obj, val);
    }
    
    Mediator.target = update;
    // Register update functions into data Property's listeners
    computeFunc.call(obj);  
    Mediator.target = null; // Reset the target so that no more property adds this as listenser

    Object.defineProperty (obj, key, {
      get: function () {
        return computeFunc.call(obj);
      }
    })
}

上述代码的第十行computeFunc.call(obj)在这里的作用其实是注册监听,具体可见如下代码第六和第七行。理解起来也不难,computeFunc.call(obj)最终会调用如下 data 里关联的的get方法。这时中介者就起作用了,我们将 Mediator.target 引用的 update 方法存入 listeners。这样每次对 data 的相关 property 赋值时,都会触发这一系列关联的 update 方法了。

function defineReactive(obj, key, val) {
    let listeners  = [];

    Object.defineProperty(obj, key, {
        get () {
            if( Mediator.target ) 
                listeners.push(Mediator.target);

            return val;
        },
        set (newVal) {
            val = newVal;
            listeners.forEach( (update) => update() );
        }
    })
}

回顾一下设计模式,这是很经典的订阅-发布模式。


复盘

最后我们再看一下方法调用。如下所示,通过修改 gift 的 price 就可以自动打印出 status 结果了。

let gift = {};

defineReactive(gift, 'price', 0);

defineComputed(
    gift, 
    'status', 
    function computeFunc () {
       return this.price > 1024 ? 'Smile' : 'Cry';
    },
    function updateCB (val) {
        console.log(val);
    }
)

gift.price = 1314; // Smile
gift.price = 250; // Cry

我们再来复盘一下整个过程

  1. 构造 data 属性为 Reactive Property (defineReactive)

  2. 构造 computed 方法,并在相关联的 data Property 里注册 update 方法 (defineComputed)

  3. 为 data 的 Property 赋值,并自动更新相关联的 update 方法

我把完整的代码贴到了最后,有兴趣的同学可以自己运行一下。


小结

VUE.js 已然成为业内最主流的框架之一,不得不说它的用户体验很友好、学习曲线也不高,几个月下来也可以把玩框架了。不过,使用框架最后很容易变成做填空题,技术增长也会遇到瓶颈;当遭遇框架之外的难题时便一筹莫展了。我自己也深陷瓶颈之中,希望能夯实一下基础知识,尽力突破这一层难关吧。共勉!


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/7331

相关推荐

html中的marquee属性

该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可能无法看到下面一些很有意思的效果,该标签是个容器标签

vue里的$refs属性

vuejs的极大程度的帮助减少了对dom的操作,他主要通过添加ref属性,但是当获取this.$refs属性时,稍有不注意就会输出undefined导致我们对dom节点的操作报错。this.$refs.xxx为undefined的几种情况记录:

css的overflow属性

事实上我挺长一段时间都没弄清楚overflow:scroll与overflow:auto的差别,今天测试了一下,总算是明白了。visible: 不剪切内容。hidden: 将超出对象尺寸的内容进行裁剪,将不出现滚动条。scroll: 将超出对象尺寸的内容进行裁剪,并以滚动条的方式显示超出的内容。

css使用到的border边框属性

border 在一个声明中设置所有的边框属性。 border-bottom在一个声明中设置所有的下边框属性。border-bottom-color设置下边框的颜色。border-bottom-style设置下边框的样式。

Cookie 的 SameSite 属性

Chrome 51 开始,浏览器的 Cookie 新增加了一个 SameSite 属性,用来防止 CSRF 攻击和用户追踪。Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击。

React 也能“用上” computed属性

初次见到计算属性一词,是在 Vue 官方文档 《计算属性和侦听器》 一节中,文章中是这样描述计算属性的:模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。

css常用属性

text-align 属性规定元素中的文本的水平对齐方式。属性值:none | center | left | right | justify;font-size表示设置字体大小,如果设置成inherit表示继承父元素的字体大小值。

JS、Jquery中判断checkbox是否选中

attr()与prop()如何选择:attr()方法返回被选元素的属性值。prop() 方法设置或返回被选元素的属性和值。当该方法用于返回属性值时,则返回第一个匹配元素的值。当该方法用于设置属性值时,则为匹配元素集合设置一个或多个属性/值对。

css z-index属性

z-index 仅适用于定位元素。即 postition 值为 relative, absolute 和 fixed 属性;堆叠顺序是当前元素位于 z 轴上的值。值越大表示元素越靠近屏幕,反之元素越远离屏幕在同一个堆叠上下文中, z-index 值越大,越靠近屏幕。

CSS中的cursor属性

css中的cursor这个属性是用来设置光标形状的。这个属性定义了鼠标指针放在一个元素边界范围内时所用的光标的形状。默认值:auto,继承性:yes,出现版本:css2

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!