简介
说明
本文用示例介绍Vue中的data设计为函数的原因。
分别用Vue和原生JavaScript进行展示。
结论
对象是一个引用数据类型,如果data是一个对象会造成所有组件共用一个data。若data是一个函数,每次函数都会返回一个新的对象,这样每个组件都会维护一份独立的对象(data)。
根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况。
vue支持将一个对象作为vue构造参数中data属性的值,如果data是方法的话,也会先取得内部返回的对象结果。详见代码:
// vue/src/core/instance/state.js
function initData (vm: Component) {
  var data = vm.$options.data;
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {};
  if (!isPlainObject(data)) {
    data = {};
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    );
  }
  ...
}官网
组件基础 — Vue.js
问题引出
大家都知道,Vue的组件一般是下边这样写的。
可以看到,data是个函数,那么为什么不写成对象呢?就像这样:data: { msg: 'Hello'}
<template>
  <div class="hello">
    hello world
  </div>
</template>
<script>
export default {
  name: 'Demo',
  data () {
    return {
      msg: 'Hello'
    }
  }
}
</script>
<style scoped>
</style>实例:使用Vue创建计数器
例1:data为函数
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>this is title</title>
</head>
<body>
<div id="components-demo">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>Vue.config.productionTip = false</script>
<script>
    // 定义一个名为 button-counter 的新组件
    Vue.component('button-counter', {
        data: function () {
            return {
                count: 0
            }
        },
        template: '<button v-on:click="count++">你点击了 {{ count }} 次</button>'
    })
    new Vue({
        el: '#components-demo'
    })
</script>
</body>
</html>结果:每个计数器单独计数
例2:data为对象
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>this is title</title>
</head>
<body>
<div id="components-demo">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>Vue.config.productionTip = false</script>
<script>
    // 定义一个名为 button-counter 的新组件
    Vue.component('button-counter', {
        data: {
            count: 0
        },
        template: '<button v-on:click="count++">你点击了 {{ count }} 次</button>'
    })
    new Vue({
        el: '#components-demo'
    })
</script>
</body>
</html>结果:直接报错

是因为新版的Vue2直接进行了检测,如果不是函数,就会报错。
如果是旧版的Vue2,那么结果是:点击了一个按钮,所有按钮都会增加次数(它们共享data里的count这个属性)。
实例:原生JS
例1:data为函数
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>This is title</title>
</head>
<body>
<div class="container">
    这是个Demo
</div>
<script>
    function MyComponent() {
        this.data = this.data();
    }
    MyComponent.prototype.data = function() {
        return {
            age: 12
        }
    };
    let user1 = new MyComponent();
    let user2 = new MyComponent();
    console.log(user1.data === user2.data)  // false
    user1.data = {age: 13};
    console.log('user1的age:' + user1.data.age); //user1的age:13
    console.log('user2的age:' + user2.data.age); //user2的age:12
</script>
</body>
</html>结果:输出的结果不同

例2:data为对象
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>This is title</title>
</head>
<body>
<div class="container">
    这是个Demo
</div>
<script>
    function MyComponent() {
    }
    MyComponent.prototype.data = {
        age: 12
    }
    let user1 = new MyComponent();
    let user2 = new MyComponent();
    console.log(user1.data === user2.data)  // false
    user1.data = {age: 13};
    console.log('user1的age:' + user1.data.age); //user1的age:13
    console.log('user2的age:' + user2.data.age); //user2的age:12
</script>
</body>
</html>结果:输出的结果相同
 










