zoukankan      html  css  js  c++  java
  • Vue实例里面的data属性为什么用函数返回

    最近在学习Vue中在图灵社区买了一本电子书《Vue小书》,我感觉挺坑的,没有期待的那么好。

    其中有的一下子就给一大串代码,但这一大串代码只是为了说明某一点,但是这片代码很多处都可以讲的,大概是因为篇幅限制原因吧。

    其中我第一次看到的Vue实例中的data属性时,很好奇为什么是用function return 一个对象,而在之前某些视频中的写法又是直接写的对象,然后一查,又牵扯到JS原型链等一些问题。本质上是js语言的问题。

    首先官方解释:

    当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。
    所以官方推荐的写法是:
    data() {
    return {
    返回的数据
    }
    }

    然后又举了一个例子:

    假定存在组件ComponentA,里面定义为
    module.exports = {
    props: {
    title: String
    },
    data: {
    test: 1
    }
    }
    在某个Page里面我们定义了如下的模板

    <template>
        <div>
            <component-a title="1"></component-a>
            <component-a title="2"></component-a>
        </div>
    </template>
    
    

    那么上面两个ComponentA的实例中的data将同时为组件定义时data对应的对象,即相当于两个实例的data相互影响了。

    也就是第一个实例的data和第二个我们并不想改变的实例的data也会发生改变。
    再举个例子:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div id="app">
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
        </div>
        <!--祖册一个组件-->
        <template id='my_btn'>
            <button @click="counter += 1">点击的次数{{counter}}</button>
        </template>
    
        <script src="js/vue.min.js"></script>
        <script>
            Vue.component('my-btn', {
                template: '#my_btn',
                data() {
                    return {
                        counter: 0
                    }
                }
            })
    
            new Vue({
                el: '#app',
            })
        </script>
    </body>
    </html>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div id="app">
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
            <my-btn></my-btn>
        </div>
        <!--祖册一个组件-->
        <template id='my_btn'>
            <button @click="counter += 1">点击的次数{{counter}}</button>
        </template>
    
        <script src="js/vue.min.js"></script>
        <script>
    
            let data = {
                counter:0
            }
    
            Vue.component('my-btn', {
                template: '#my_btn',
                data() {
                    return data;
                }
            })
    
            new Vue({
                el: '#app',
            })
        </script>
    </body>
    </html>
    

    运行上面两段代码会发现不同结果。

    这个问题类比到引用数据类型。
    如果不用function return 每个组件的data都是内存的同一个地址,那一个数据改变其他也改变了,这当然就不是我们想要的。
    用function return 其实就相当于申明了新的变量,相互独立,自然就不会有这样的问题

    这让我之前看到的一个JS指针问题,js里面有引用类型和基本类型,

    //基本类型
    var a = 1;
    var b = a;
    b = 2;
    console.log(a) //1 值不随b的改变而改变

    //引用类型
    var a = {x: 1};
    var b = a;
    b.x = 2
    console.log(a.x) //2 值随着b的改变而改变

    具体原因,犀牛书里面好像讲过, 基本类型占用空间少,而引用类型占用大,js为了节约空间,就把引用类型的值用一个叫做指针的东西和变量连接起来, 也就是说改一个其他的都会跟着改变,公用的意思.

    我们用JS原型链在举一个例子:

    var MyComponent = function() {}
    MyComponent.prototype.data = {
      a: 1,
      b: 2,
    }
    // 上面是一个虚拟的组件构造器,真实的组件构造器方法很多
    
    var component1 = new MyComponent()
    var component2 = new MyComponent()
    // 上面实例化出来两个组件实例,也就是通过<my-component>调用,创建的两个实例
    
    component1.data.a === component2.data.a // true
    component1.data.b = 5
    component2.data.b // 5
    

    可以看到上面代码中最后三句测试代码,如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。这怎么可以,两个实例应该有自己各自的域才对。所以,需要通过下面方法来进行处理:

    var MyComponent = function() {
      this.data = this.data()
    }
    MyComponent.prototype.data = function() {
      return {
        a: 1,
        b: 2,
      }
    }
    

    这个方法就是给实例添加一个data属性,它的值是原型方法data返回的一个新对象,下次用实例访问data的时候就会直接访问到这个对象,因为有了实例属性data了,就不会去查找原型上的data。

    Vue.component('my-component', {
      template: '<div>测试</div>',
      data() {
        return {} // 返回一个唯一的对象,不要和其他组件共用一个对象进行返回
      },
    })
    

    上面这个操作是一个简易操作,实际上,它首先需要创建一个组件构造器,然后注册组件。注册组件的本质其实就是建立一个组件构造器的引用。使用组件才是真正创建一个组件实例。所以,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式。

  • 相关阅读:
    生成随机数的三种方法
    老外最常说的二十句钻石级英语
    线性探查法实现的散列表(哈希表)类
    STL容器之间可以直接相互赋值使用
    用位向量实现集合抽象数据类型
    一个利用map统计一段英文文章中每个单词出现次数的小程序
    各种排序算法的稳定性和时间复杂度小结
    24佳句对译收藏
    SQL 将一个字段内用逗号分隔的内容分成多条记录
    SQL递归查询(with cte as)
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/8321112.html
Copyright © 2011-2022 走看看