为什么data属性是一个函数而不是一个对象?

简介: 为什么data属性是一个函数而不是一个对象?

#一、实例和组件定义data的区别

vue实例的时候定义data属性既可以是一个对象,也可以是一个函数

const app = new Vue({
    el:"#app",
    // 对象格式
    data:{
        foo:"foo"
    },
    // 函数格式
    data(){
        return {
             foo:"foo"
        }
    }
})

1

2

3

4

5

6

7

8

9

10

11

12

13

组件中定义data属性,只能是一个函数

如果为组件data直接定义为一个对象

Vue.component('component1',{
    template:`<div>组件</div>`,
    data:{
        foo:"foo"
    }
})

1

2

3

4

5

6

则会得到警告信息

警告说明:返回的data应该是一个函数在每一个组件实例中

#二、组件data定义函数与对象的区别

上面讲到组件data必须是一个函数,不知道大家有没有思考过这是为什么呢?

在我们定义好一个组件的时候,vue最终都会通过Vue.extend()构成组件实例

这里我们模仿组件构造函数,定义data属性,采用对象的形式

function Component(){
}
Component.prototype.data = {
  count : 0
}

1

2

3

4

5

6

创建两个组件实例

const componentA = new Component()
const componentB = new Component()

1

2

修改componentA组件data属性的值,componentB中的值也发生了改变

console.log(componentB.data.count)  // 0
componentA.data.count = 1
console.log(componentB.data.count)  // 1

1

2

3

产生这样的原因这是两者共用了同一个内存地址,componentA修改的内容,同样对componentB产生了影响

如果我们采用函数的形式,则不会出现这种情况(函数返回的对象内存地址并不相同)

function Component(){
  this.data = this.data()
}
Component.prototype.data = function (){
    return {
      count : 0
    }
}

1

2

3

4

5

6

7

8

修改componentA组件data属性的值,componentB中的值不受影响

console.log(componentB.data.count)  // 0
componentA.data.count = 1
console.log(componentB.data.count)  // 0

1

2

3

vue组件可能会有很多个实例,采用函数返回一个全新data形式,使每个实例对象的数据不会受到其他实例对象数据的污染

#三、原理分析

首先可以看看vue初始化data的代码,data的定义可以是函数也可以是对象

源码位置:/vue-dev/src/core/instance/state.js

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
    ...
}

1

2

3

4

5

6

7

data既能是object也能是function,那为什么还会出现上文警告呢?

别急,继续看下文

组件在创建的时候,会进行选项的合并

源码位置:/vue-dev/src/core/util/options.js

自定义组件会进入mergeOptions进行选项合并

Vue.prototype._init = function (options?: Object) {
    ...
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    ...
  }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

定义data会进行数据校验

源码位置:/vue-dev/src/core/instance/init.js

这时候vm实例为undefined,进入if判断,若data类型不是function,则出现警告提示

strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
  if (!vm) {
    if (childVal && typeof childVal !== "function") {
      process.env.NODE_ENV !== "production" &&
        warn(
          'The "data" option should be a function ' +
            "that returns a per-instance value in component " +
            "definitions.",
          vm
        );
      return parentVal;
    }
    return mergeDataOrFn(parentVal, childVal);
  }
  return mergeDataOrFn(parentVal, childVal, vm);
};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#四、结论

  • 根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
  • 组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象
相关文章
|
JavaScript 前端开发
双向数据绑定是什么
双向数据绑定是什么
241 2
|
11月前
|
Web App开发 移动开发 UED
介绍一下HTML5的新技能:多媒体支持
介绍一下HTML5的新技能:多媒体支持
394 2
|
8月前
|
机器人 API 定位技术
具身智能干货|ROS2理论与实践系列(二):ROS2通信机制核心
机器人是一种高度复杂的系统性实现,一个完整的机器人应用程序可能由若干功能模块组成,每个功能模块可能又包含若干功能点,在不同功能模块、不同功能点之间需要频繁的进行数据交互。比如以导航中的路径规划模块为例: 路径规划时就需要其他功能模块输入数据,并输出数据以被其他模块调用。 输入的数据有地图服务提供的地图数据、定位模块提供的机器人位姿数据、人机交互模块提供的目标点数据......。 输出的路径信息则被运动控制订阅或是回显在人机交互界面上。 那么这些相对独立的功能模块或功能点之间是如何实现数据交互的呢?在此,我们就需要介绍一下ROS2中的通信机制了。
772 62
|
11月前
CSS3制作的聚光灯下倒影文字选装动画特效源码
CSS3聚光灯下倒影文字特效是一段基于CSS3实现的聚光灯下带倒影的文字旋转动画效果代码,具有真实的视觉感,同时文字还会在旋转过程中显示出灯光的反射效果,很有意思,欢迎对此段代码感兴趣的朋友前来下载使用。
114 6
|
12月前
|
机器学习/深度学习 人工智能 自然语言处理
从人工智能到大模型的演变
本文概述了人工智能从早期的规则基础系统到现代大模型的演变过程,涵盖了符号主义、专家系统、统计学习、深度学习、自然语言处理以及大模型的出现与应用,分析了各阶段的关键技术和面临的挑战,展望了未来的发展方向。
540 3
|
容器
Qt6学习笔记七(ToolButton、RadioButton、GroupBox、CheckBox、ListWidget、TreeWidget、TableWidget)
Qt6学习笔记七(ToolButton、RadioButton、GroupBox、CheckBox、ListWidget、TreeWidget、TableWidget)
729 0
|
存储 JavaScript 容器
Vue组件之间的通信方式都有哪些?
Vue组件之间的通信方式都有哪些?
163 1
|
缓存 JavaScript
请描述下你对vue生命周期的理解?在created和mounted这两个生命周期中请求数据有什么区别呢?
请描述下你对vue生命周期的理解?在created和mounted这两个生命周期中请求数据有什么区别呢?
211 0
|
索引
SAP ABAP——内表(六)【追加内表数据—APPEND】
本文主要介绍一下SAP ABAP中内表的增删查改语句中的追加内表数据的APPEND相关语句,包括使用索引追加数据(APPEND语句不能使用关键字追加数据)以及不同类型内表使用APPEND追加数据的不同效果
2425 2
SAP ABAP——内表(六)【追加内表数据—APPEND】
|
关系型数据库 MySQL 开发工具
Git:分布式版本控制系统的崛起与演变
Git:分布式版本控制系统的崛起与演变
269 0