Vue中的方法,属性计算和观察者

发布于 大漠

特别声明:此篇文章内容来源于@SARAH DRASNER的《Methods, Computed, and Watchers in Vue.js》一文。

我喜欢使用Vue的原因之一是,Vue中的methodscomputedwatchers的使用是多么的有用。如果没有彻底的理解它们之间的区别,就很难充分利用Vue的功能。尽管如此,我看到的大多数人对这个框架感到困惑,他们也会其中的差异感到困惑,所以我们很有必要的去了解一下这方面的知。

如果你想要一个快速答案,而且没有时间阅读整篇文章,那么这里有一些小总结:

  • methods:正如他的名字一样,它们是挂载在对象上的函数,通常是Vue实例本身或Vue组件
  • computed:属性最初看起来像一个方法,但事实却又不是方法。在Vue中,我们使用data来跟踪对特定属性的更改,得到一定的反应。计算属性允许我们定义一个与数据使用相同方式的属性,但也可以有一些基于其依赖关系的自定义逻辑。你可以考虑计算属性的另一个视图到你的数据。
  • watchers:这些可以让你了解反应系统(Reactivity System)。我们提供了一些钩子来观察Vue存储的任何属性。如果我们想在每次发生变化时添加一些功能,或者响应某个特定的变化,我们可以观察一个属性并应用一些逻辑。这意味着观察者的名字必须与我们所观察到的相符。

如果上面简单的总结让你感到困惑的话,不用担心!我们将继续深入下去,希望你阅读完这篇文章之后能帮助你解决心中的困惑。如果你熟悉Vanilla JavaScript,那么方法可能对你来说很简单。你可以直接进入计算属性和观察者的部分。

Methods

在使用Vue时,可能会用到很多方法。它们被恰当地命名,而本质上,我们把一个函数挂载在一个物体上。它们非常有用,可以将功能连接到事件的指令,甚至只是创建一个小的逻辑,就像其他函数一样被重用。例如,可以在另一个方法中调用方法。你也可以调用生命周期钩子中的方法。它们非常灵活。

这里有一个简单的示例:

<!-- HTML -->
<div id="app">
    <button @click="tryme">Try Me</button>
    <p>{{ message }}</p>
</div>

// JavaScript
let app = new Vue({
    el: '#app',
    data () {
        return {
            message: null
        }
    },
    methods: {
        tryme: function () {
            this.message = Date()
        }
    }
})

效果如下:

我们还可以执行指令本身的逻辑,比如<button @click="message = Date()">Try Me</button>,这在这个小例子中效果还蛮好的。然而,随着应用程序的复杂性不断增加,我们在上面看到就得将其分解,这样有利于提高可读性,这样的做法更常见。对于Vue,允许你在指令上使用逻辑表达式,但是有一定的限制。例如,表达式是允许的,但语句是不允许的。

你可能已经注意到了,我们可以在该组件或Vue实例中访问此方法,也可以在这里调用任何数据,比如本例中为this.message。你不需要调用一个方法,就像在你的指令中调用一个函数一样。例如,@click="methodName()"是不必要的。你可以使用@click="methodName"来引用它,除非你需要传递一个参数,比如@click="methodName(param)"

使用指令调用方法也是蛮好的,因为我们有一些现有的修饰符可用。其中一个非常典型的例子是.prevent,它将保存一个提交事件,重新加载页面,例如:

<form v-on:submit.prevent="onSubmit"></form>

有关于Vue指令修饰符更多的内容,可以点击这里进行了解

Computed

计算属性对于处理已经存在的数据是非常有意义的。任何时候,当你需要整理一大堆数据的时候,你不希望在每次击键时重新计算这些,可以考虑使用计算值。

下面一些场景,但不限于下面的场景,都可以使用Vue的计算属性:

  • 在用户输入时更新大量信息,比如列表的过滤
  • 从Vuex Store中收集相关信息
  • 表单验证
  • 数据可视化的变化取决于用户需要看到什么

计算属性是理解Vue的重要部分。它们是根据依赖关系进行缓存的计算,并且只在需要时进行更新。它们在使用的时候表现得非常好,而且非常有用。有许多大型库已经处理了这方面的逻辑,现在只需要几行代码就可以完成。

尽管Vue的方法和计算属性看起来相似,但计算属性不像方法一样使用。你在函数和返回中声明了一些逻辑,但是这个函数的名称变成了你在应用程序中使用的属性,比如data

如果我们需要根据用户输入的内容筛选这些英雄的名字,下面是我们的做法。我们尽量使用一些简单的示例,这样你就能把基本概念弄清楚。最初,我们的列表将在我们的模拟板中使用存储在datanames来输出:

<!-- HTML -->
<div id="app">
    <h1>Heroes</h1>
    <ul>
        <li v-for="name in names">{{ name }}</li>
    </ul>
</div>

// JavaScript
let app = new Vue({
    el: '#app',
    data () {
        return {
            names: ['Evan You', 'John Lindquist', 'Jen Looper', 'Miriam Suzanne']
        }
    }
})

现在我们做一个名称过滤器。首先创建一个v-modelinput,它一开始是一个空字符串,但最终我们将使用它来匹配和筛选我们的列表。我们将这个属性命名为findeName,你可以看到它在inputdata都被引用。

<!-- HTML -->
<div class="sorting">
    <label for="filtername">Find your hero:</label>
    <input v-model="findName" id="filtername" type="text" />
</div>

// JavaScript
let app = new Vue({
    el: '#app',
    data () {
        return {
            findName: '',
            names: [
                'Evan You',
                'John Lindquist',
                'Jen Looper',
                'Miriam Suzanne',
                'Chris Coyier',
                'Geoff Graham',
                'Divya Sasidharan',
                'Lea Verou',
                'Rachel Andrew',
                'Vitaly Friedman',
                'Ryan Florence',
                'Dan Abramov',
                'Jen Simmons',
                'Robin Rendle',
                'Nicole Sullivan'
            ]
        }
    }
})

现在,我们可以创建一个计算属性,它将根据用户输入的内容过滤所有的名称,累此在我们的findName属性中没有任何内容。你也注意到,在这里使用了正则匹配来确保不匹配大小写并不会有啥影响,因为用户通常不会在键入时大写。

let app = new Vue({
    el: '#app',
    data () {
        return {
            findName: '',
            names: [
                'Evan You',
                'John Lindquist',
                'Jen Looper',
                'Miriam Suzanne',
                'Chris Coyier',
                'Geoff Graham',
                'Divya Sasidharan',
                'Lea Verou',
                'Rachel Andrew',
                'Vitaly Friedman',
                'Ryan Florence',
                'Dan Abramov',
                'Jen Simmons',
                'Robin Rendle',
                'Nicole Sullivan'
            ]
        }
    },
    computed: {
        filteredNames: function () {
            let filter = new RegExp(this.findName, 'i')
            return this.names.filter(el => el.match(filter))
        }
    }
})

现在,我们需要在模板中做一些调整:

<ul>
    <li v-for="name in filteredNames">{{ name }}</li>
</ul>

它可以在每次击键时对名字进行过滤!只需要添加几行代码就完成了想要的工作,而且不需要添加任何其他的代码库。

Watchers

Vue中有很多好的抽象概念,任何一位程序员都会告诉你,抽象是一种痛苦,因为你最终会得到一个他们无法解决的用例。然而,这种情况是有原因的,因为Vue允许我们更深入地进入反应式系统,我们可以利用它来观察任何正在发生变化的事情。这可能非常有用,因为作为应用程序开发人员,我们所负责的大多数事情都是变化的。

观察者还允许我们编写更多的声明式代码。你不再自己追踪一切。Vue已经在后台帮你做了这些事情,因此你还可以访问对其跟踪、数据、计算或支持的任何属性的更改。

观察者非常适合执行逻辑,比如一个属性发生变化时做的事情。这并不是一个硬性规则。你可以绝对地使用观察者的逻辑来引用属性本身,但这是观察者如何与计算属性不同的一种方法,其中的变化将涉及到我们打算使用的属性。

来看一个简单的例子,看看发生了什么?

new Vue({
    el: '#app', 
    data() {
        return {
            counter: 0
        }
    },
    watch: {
        counter() {
            console.log('The counter has changed!')
        }
    }
})

正如你在上面的代码中看到的,我们在data中存储了counter,并且通过使用属性的名称作为函数名。当我们在watch中引用counter时,可以观察到该属性的任何变化。

使用观察者观察transiton的状态

如果状态足够相似,你甚至可以使用观察者来观察transition的状态。这里有一个小例子,用Vue做一个图表。随着数据的变化,观察者会更新它,并简单地在它们之间进行转换。

SVG也适合这样的场景,因为它是用数学构建的:

watch: {
    selected: function(newValue, oldValue) {

        var tweenedData = {}

        var update = function () {
            let obj = Object.values(tweenedData);
            obj.pop();
            this.targetVal = obj;
        }

        var tweenSourceData = { onUpdate: update, onUpdateScope: this }

        for (let i = 0; i < oldValue.length; i++) {
            let key = i.toString()
            tweenedData[key] = oldValue[i]
            tweenSourceData[key] = newValue[i]
        }

        TweenMax.to(tweenedData, 1, tweenSourceData)
    }
}

这里发生了什么?

  • 首先,创建一个虚拟对象,将被动画库更新
  • 然后有一个update函数,在每个tween步骤上调用。来这个推送数据
  • 然后创建一个对象来保存数据源,并将其作为更新事件的函数指针
  • 创建一个for循环,并将当前索引转换为字符串
  • 然后可以在目标虚拟对象之间进行补间,但是我们只会针对特定的键执行这个操作

我们也可以用动画来制作一些类似于这个时间差刻度盘的东西。我经常出差,所有的同事都在不同的地方,所以想找到一种方法来追踪我们所处的当地时间,以及白天、晚上变化。

在这里,我们正在观察被检查的属性,我们将使用不同的方法来包含时间轴动画,它会根据当前时间的相对关联来改变色调和饱和度以及其他一些元素。正如前面提到的,在下接列表中发生了更改,但是我们正在执行的是在基他地方应用的逻辑。

watch: {
    checked() {
        let period = this.timeVal.slice(-2),
        hr = this.timeVal.slice(0, this.timeVal.indexOf(':'));

        const dayhr = 12,
        rpos = 115,
        rneg = -118;

        if ((period === 'AM' && hr != 12) || (period === 'PM' && hr == 12)) {
            this.spin(`${rneg - (rneg / dayhr) * hr}`)
            this.animTime(1 - hr / dayhr, period)
        } else {
            this.spin(`${(rpos / dayhr) * hr}`)
            this.animTime(hr / dayhr, period)
        }

    }
},

还有一些关于观察者有趣的事情。例如,我们可以访问新的和旧的版本的属性作为参数,我们可以指定deep,特别是我们想看一个嵌套的对象。对于更详细的信息,在指南中有很多好的信息

你可以看到,观察者对于任何更新的东西都非常有用 —— 无论是表单输入、异步更新还是动画。如果你对Vue的反应感到好奇,指南中这些应该对你有所帮助。如果你想了解更多关于反应式相关的信息,我推荐你阅读@Andres Staltzt的文章,这是一种更好的编码方式

总结

希望这篇文章能够帮助你有效地使用Vue来加速你的应用程序的开发。这里有一个统计数据,程序员花了70%的时间阅读代码,却只使用30%的时间来写代码。就我个人而言,我喜欢这样。作为一个维护者,我可以看到一个我从未见过的代码库,并立即知道作者的意图是什么,由methodscomputedwatchers所做的区分。

扩展阅读

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/vue/methods-computed-and-watchers-in-vue-js.html5 Sneakers to Celebrate Cinco de Mayo