# Vue实例对象的数据选项

一般地,当模板内容较简单时,使用data选项配合表达式即可。涉及到复杂逻辑时,则需要用到methods、computed、watch等方法。本文将详细介绍Vue实例对象的数据选项

# data

data是Vue实例的数据对象。Vue将会递归将 data 的属性转换为getter/setter,从而让data属性能响应数据变化。

【注意】

不应该对 data 属性使用箭头函数

<script>
var data = { message: 'Hello Vue!' }
var vm = new Vue({
  el: '#app',
  data: data
})
console.log(vm);
</script>

Vue实例创建之后,可以通过vm.$data或者vm._data访问原始数据对象

data选项

Vue实例也代理了data对象上所有的属性

var data = { message: 'Hello Vue!' }
var vm = new Vue({
  el: '#app',
  data: data
})
console.log(vm.$data === data);  // true
console.log(vm.message); //'Hello Vue!'
console.log(vm.$data.message); //'Hello Vue!'

代理

被代理的属性是响应的,也就是说值的任何改变都是触发视图的重新渲染。设置属性也会影响到原始数据,反之亦然

vm._data.message = "我是修改后的message"  // 视图将显示更改为 我是修改后的message

WARNING

以 _ 或 $ 开头的属性不会被Vue实例代理,因为它们可能和Vue内置的属性或方法冲突。可以使用例如vm.$data._property的方式访问这些属性

const data = {
    message: "Hello Vue!",
    _name: "张三",
};
const vm = new Vue({
    el: "#app",
    data: data,
});

console.log(vm.message); // Hello Vue!
console.log(vm._name); // undefined
console.log(vm.$data._name); // 张三
console.log(vm._data._name); // 张三

# computed

  • 计算属性函数computed将被混入到Vue实例中。
  • 所有getter和setter的this上下文自动地绑定为Vue实例

【注意】

不应该使用箭头函数来定义计算属性函数

computed示例

<div id="app">
  <p>原始字符串: "{{ message }}"</p>
  <p>反向字符串: "{{ reversedMsg }}"</p>
</div>
<script>
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMsg() {
      return this.message.split('').reverse().join('')
    }
  }
})

</script>

真实渲染结果如下

<div id="app">
  <p>原始字符串: "Hello"</p>
  <p>反向字符串: "olleH"</p>
</div>

声明的计算属性 reversedMsg 。提供的函数将作为属性 vm.reversedMsg 的getter

TIP

  • vm.reversedMessage 的值始终取决于 vm.message 的值。
  • 可以像绑定普通属性一样在模板中绑定计算属性。当 vm.message 发生改变时,所有依赖于 vm.reversedMessage 的绑定也会更新
  • reversedMessage属性可以理解为由 message派生出的属性
console.log(vm.reversedMsg) // -> 'olleH'
vm.message = "Vue" // 改变data中的message
// 再打印reversedMsg
console.log(vm.reversedMsg) // -> 'euV' 

DANGER

// 如果修改 vm.reversedMsg // vm.reversedMessage依赖于vm.message的值,vm.reversedMessage本身并不能被赋值 // 计算属性默认只有 getter ,不过在需要时也可以提供一个 setter。代码示例如下

# 【setter】

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
  <!--完整写法-->
    // computed: {
    //    fullName: {
    //     get(){
    //       return this.firstName + ' ' + this.lastName
    //     }
    // }
  }
})
</script>

注意

需要注意的是,不是说我们更改了getter里使用的变量,就会触发computed的更新,前提是computed里的值必须要在模板里使用才行。

以下代码只会打印 updated

<div id="demo">
     <!-- <p> {{ fullName }} </p> -->
     <input type="text" v-model="firstName">
     <input type="text" v-model="lastName">
</div>

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'zhang',
    lastName: 'san'
  },
  computed: {
    fullName: function () {
      console.log('computed getter...')
      return this.firstName + ' ' + this.lastName
    }
  },
  updated () {
     console.log('updated')
  }
})

添加setter

<div id="demo">
     <p> {{ fullName }} </p>
     <input type="text" v-model="fullName">
     <input type="text" v-model="firstName">
     <input type="text" v-model="lastName">
</div>

computed: {
    fullName: {
    // getter 方法
        get(){
            console.log('computed getter...')
            return this.firstName + ' ' + this.lastName
        },
    // setter 方法
        set(newValue){
            console.log('computed setter...')
            let names = newValue.split(' ')
            this.firstName = names[0]
            this.lastName = names[names.length - 1]
            return this.firstName + ' ' + this.lastName
        }
      
    }
  },
updated () {
    console.log('updated')
}

<!-- 
    v-model="fullName",如果我们这里直接修改了fullName的值,
    那么就会触发setter,同时也会触发getter以及updated函数。
    其执行顺序是setter -> getter -> updated
-->

// console.log('computed setter...')
// console.log('computed getter...')
// console.log('updated')

注意

  • 并不是触发了setter也就会触发getter,他们两个是相互独立的。
  • 我们这里修改了fullName会触发getter是因为setter函数里有改变firstName 和 lastName 值的代码。
  • 如果注释掉上边的setter中修改firstName 和lastName的代码后就不会执行getter 如下
set(newValue){
    console.log('computed setter...')
    //  let names = newValue.split(' ')
    //  this.firstName = names[0]
    //  this.lastName = names[names.length - 1]
    return this.firstName + ' ' + this.lastName
}

// 执行如下 console.log('computed setter...') console.log('updated')

# methods

通过调用表达式中的 methods 也可以达到 computed 同样的效果

WARNING

[注意] 不应该使用箭头函数来定义methods函数

<div id="app">
  <p>原始字符串: "{{ message }}"</p>
  <p>反向字符串: "{{ reversedMsg() }}"</p>
</div>
data: {
    message: 'Hello'
},
methods: {
    reversedMsg() {
       return this.message.split('').reverse().join('')
    }
}    
  • 对于最终的渲染结果,两种方式确实是相同的
  • 不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。
  • 这就意味着只要 message 还没有发生改变,多次访问 reversedMsg 计算属性会立即返回之前的计算结果,而不必再次执行函数。

TIP

  • 假设有一个性能开销比较大的的计算属性A,它需要遍历一个极大的数组和做大量的计算生成。可能有其他的计算属性依赖于计算属性 A。就需要缓存这一特性,性能也会更+
  • 如果没有缓存,将不可避免的多次执行A的getter!如果不希望有缓存,则用 methods 替代

# watch

  • Vue提供了一种通用的方式来观察和响应Vue实例上的数据变动:watch选项。
  • watch属性是一个对象,键是需要观察的表达式,值是对应回调函数,回调函数得到的参数为新值和旧值。

WARNING

[注意] 不应该使用箭头函数来定义 watch 函数

示例

<div id="app">
  <button @click="num++">a加1</button>
</div>

<script>
var vm = new Vue({
  el: '#app',
  data: {
    num: 1
  },
  watch: {
    num(val, oldVal) {
      // 只要num发生变化 都将打印以下内容
      console.log(`num的新值:${val},num的旧值:${oldVal}`)
    }
  }
})
</script>

# 【$watch】

除了使用数据选项中的watch方法以外,还可以使用实例对象的$watch方法, 该方法的返回值是一个取消观察函数,用来停止触发回调

<script>
var vm = new Vue({
  el: '#app',
  data: {
    num: 1
  }
})
var unwatch = vm.$watch('num',function(val, oldVal){
  if(val === 10){
    // 当num的值更新到10时,触发unwatch(),来取消观察。
    // 点击按钮时,a的值仍然会变化,但是不再触发watch的回调函数
    unwatch(); 
  }
   console.log(`num的新值:${val},num的旧值:${oldVal}`)
})
</script>
Last Updated: 12/22/2022, 11:18:11 PM