模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,比如:
<div id="example">{{ message.split('').reverse().join('') }}</div> <script> var app = new Vue({ el:'#example', data:{message:'hello world'} }) </script>
这样模板不再是简单的声明式逻辑,必须看一段时间才能意识到,对于这些复杂逻辑,需要使用计算属性,例如:
<div id="example">{{ reversedMessage}}</div> <script> var app = new Vue({ el:'#example', data:{message:'hello world'}, computed:{ reversedMessage:function(){return this.message.split('').reverse().join('')} } }) </script>
在模板中可以把computed当作data属性来使用
computed是一个对象,每个键是计算属性的值,值有两种使用方法:值是一个函数,或者值是一个包含get和set的对象
源码分析
Vue实例后会先执行_init()进行初始化(4579行)时,会执行initState()进行初始化,如下:
function initState (vm) { //第3303行 vm._watchers = []; var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } if (opts.methods) { initMethods(vm, opts.methods); } if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } if (opts.computed) { initComputed(vm, opts.computed); } //如果定义了computed,则调用initComputed初始化computed if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch); } }
initComputed函数如下:
var computedWatcherOptions = {lazy: true}; //计算属性的配置信息 function initComputed (vm, computed) { //第3424行 // $flow-disable-line var watchers = vm._computedWatchers = Object.create(null); //定义一个空对象,没有原型的,用于存储所有计算属性对应的watcher // computed properties are just getters during SSR var isSSR = isServerRendering(); for (var key in computed) { //遍历每个计算属性 var userDef = computed[key]; //将计算属性的值保存到userDef里面 var getter = typeof userDef === 'function' ? userDef : userDef.get; //如果userDef是一个函数则赋值给getter,否则将userDef.get赋值给getter if ("development" !== 'production' && getter == null) { warn( ("Getter is missing for computed property \"" + key + "\"."), vm ); } if (!isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( //创建一个内部的watcher给计算属性用 vm, getter || noop, noop, computedWatcherOptions ); } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (!(key in vm)) { //如果key在vm中没有定义 注:组件的计算属性在模块加载的时候已经被定义在了原型上面了 defineComputed(vm, key, userDef); //则执行defineComputed()函数 } else { if (key in vm.$data) { warn(("The computed property \"" + key + "\" is already defined in data."), vm); } else if (vm.$options.props && key in vm.$options.props) { warn(("The computed property \"" + key + "\" is already defined as a prop."), vm); } } } }
注:对于计算属性的Watcher来说,它的lazy属性为true,因此new watcher()结尾时不会执行get()方法,而是直接返回undefined(在3127行)(求值会等到该计算属性被调用时才求值的)
回到initComputed()函数,defineComputed()定义如下:
function defineComputed ( //第3465行 target, key, userDef ) { var shouldCache = !isServerRendering(); if (typeof userDef === 'function') { //如果userDef为函数,则默认为get
sharedPropertyDefinition.get