添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Vue.$data、this._data源码解析

$data是Vue实例中的实例属性,表示Vue实例观察的数据对象。官网给出的解释: vm.$data

类型 Object 详细 :Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象 property 的访问。

先看一个栗子:

<!DOCTYPE html>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	</head>
		<div id="app">
			{{ message }}
	</body>
	<script>
		var vm = new Vue({
			el: "#app",
			data: {
				message: "hello vue."
		console.log(vm.$data) //{__ob__: Observer}
		console.log(vm._data); //{__ob__: Observer}
		console.log(vm.$data == vm._data); //true
		//三种方式都可以访问到message
		console.log(vm.$data.message); //hello vue
		console.log(vm._data.message); //hello vue
		console.log(vm.message);  //hello vue
		console.log(Vue.prototype)
	</script>
</html>

在了解 vm.$data 之前我们先来复习一下原型及对象的属性,然后再了解一下Object.defineProperty()。正是Vue内部实现时用到了ES5的Object.defineProperty()方法,所以Vue不支持IE8及以下浏览器(IE8及以下浏览器是不支持ECMASCRIPT 5的Object.defineProperty())。

一、原型 __proto__ 、prototype属性

1、__proto__、prototype属性

在js中,对象可谓贯穿一生。对象可以分为两类。一是普通对象(Object);二是函数对象(Function)。prototype和__proto__都指向原型对象。

任意一个 函数(包括构造函数)都有一个prototype属性 ,指向该函数的原型对象 。函数本身是一种对象,所以函数既有prototype属性也有__proto__属性。

任意一个 构造函数实例化的对象,都有一个__proto__属性 (__proto__并非标准属性,ECMA-262第5版将该属性或指针称为[[Prototype]],可通过Object.getPrototypeOf()标准方法访问该属性) ,指向构造函数的原型对象。

举个栗子:

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
	   //给自己构造一个女朋友
	   function GirlFriend () {
	     this.name = "zhiling";
	   //设置GirlFriend()这个函数的prototype属性
	   var hand = {
	     whoName: "right hand",
	     someFunction: function(){
	       console.log("女朋友="+this.whoName);
	   //将函数的原型prototype指向hand
	   GirlFriend.prototype = hand; 
	   //打印hand
	   console.log(hand);
	   //创建一个myGirlFriend对象
	   var myGirlFriend = new GirlFriend();
	   //GirlFriend的原型prototype是hand对象
	   console.log(GirlFriend.prototype);
	   //myGirlFriend的原型__proto__是hand对象
	   console.log(myGirlFriend.__proto__);
	   //prototype 与__proto__ 的关系就是:对象的__proto__==对象的构造函数的prototype
	   console.log(myGirlFriend.__proto__ === GirlFriend.prototype) //true
	   console.log(Object.prototype==hand.__proto__);//true
	   console.log(Object.prototype); //最顶层的原型
	   console.log(hand.__proto__.__proto__)//null
	</script>
</html>
<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
	   //给自己构造一个女朋友
	   function GirlFriend () {
	     this.name = "zhiling";
	   //第一条:普通函数GirlFriend.__proto__->GirlFriend.__proto__.__proto__->GirlFriend.__proto__.__proto__.__proto__
	   console.log(GirlFriend.__proto__); //function Empty() {}
	   console.log(GirlFriend.__proto__.__proto__); //[object Object]
	   console.log(GirlFriend.__proto__.__proto__.__proto__); //null
	   //构造函数:GirlFriend.constructor.__proto__
	   console.log(GirlFriend.constructor.__proto__);//function Empty() {}
	   //构造函数:GirlFriend.constructor.prototype
	   console.log(GirlFriend.constructor.prototype);//function Empty() {}
	   //Object
	   console.log(GirlFriend.__proto__.__proto__.constructor.__proto__);//function Empty() {}
	   console.log(GirlFriend.__proto__.__proto__.constructor.prototype);//[object Object]
	   console.log(GirlFriend.__proto__.__proto__.prototype);//对象,undefined
	</script>
</html>

原型链的关系图:

二、Object属性简介

    在js中,我们有很多种方式给对象定义属性和赋值。其中最常见的就是:1)对象.属性 = 值;2)对象['属性'] = 值;

    除了上述的方式之外,还可以使用Object.defineProperty()方法来定义和修改对象的属性。Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

    注:应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。

1、Object属性的类型

    在了解Object.defineProperty()之前,先看看ECMAScript5中对对象属性的一个描述:

    Object 是一个属性的集合。每个属性既可以是一个命名的数据属性,也可以是一个命名的访问器属性,或是一个内部属性:

  • 命名的数据属性(named data property)由一个名字与一个 ECMAScript 语言值和一个 Boolean 属性集合组成(拥有一个确定的值的属性。这也是最常见的属性)
  • 命名的访问器属性(named accessor property)由一个名字与一个或两个访问器函数,和一个 Boolean 属性集合组成。访问器函数用于存取一个与该属性相关联的 ECMAScript 语言值(通过gettersetter进行读取和赋值的属性)
  • 内部属性(internal property)没有名字,且不能直接通过 ECMAScript 语言操作。内部属性的存在纯粹为了规范的目的。可以通过Object.getPrototypeOf()方法间接的读取到它的值。

2、Object属性特性

2.1、命名的数据属性/命名的访问器属性

    每个属性(property)都拥有4个特性(attribute).两种类型的属性一种有6种属性特性,也被叫做属性描述符:

    属性描述符通常使用在下面的这些函数中:Object.defineProperty, Object.getOwnPropertyDescriptor, Object.create.如果省略了属性描述符对象中的某个属性,则该属性会取一个默认值:

表7:默认的特性 名称默认值[[Value]]undefined[[Get]]undefined[[Set]]undefined[[Writable]]false[[Enumerable]]false[[Configurable]]false

2.2、内部属性

    这些内部属性不是 ECMAScript 语言的一部分。纯粹是以说明为目的定义它们。ECMAScript 实现需要保持和这里描述的内部属性产生和操作的结果一致。所有对象(包括宿主对象)必须实现 表8 中列出的所有内部属性(他们可以看成是一种规范)。

    1)所有对象都有一个叫做 [[Prototype]] 的内部属性。此对象的值是 null 或一个对象,并且它用于实现继承。它可以通过Object.getPrototypeOf(obj)访问

    2)内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 "Arguments""Array""Boolean""Date""Error""Function""JSON""Math""Number""Object""RegExp""String" 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。它可以通过Object.prototype.toString(obj)访问

    3)所有 ECMAScript对象 都有一个 Boolean 类型的 [[Extensible]] 内部属性,它决定了 是否可以给对象添加命名属性。如果 [[Extensible]] 内部属性的值是 false 就不得给对象添加命名属性。它可以通过Object.isExtensible(obj)访问

    4)[[Get]],对象默认的内置[[Get]]操作首先是在对象中查找是否有名称相同的属性,有则返回这个属性的值,如果没有找到,则会遍历可能存在的[[Prototype]]链,如果无论如何都没找到,name就返回一个undefined值。

    5)[[Put]],此操作并不仅仅只是给对象设置或者创建一个属性,[[Put]]会检查以下内容:a)属性是否是访问描述符,如果是并存在setter就调用setter;b)属性的数据描述符中的writable参数,如果writable不是false,则将该值设定为属性的值。

表8 - 所有对象共有的内部属性 内部属性值的类型范围说明[[Prototype]]Object 或 Null此对象的原型[[Class]]String说明规范定义的对象分类的一个字符串值[[Extensible]]Boolean如果是 true,可以向对象添加自身属性。[[Get]]SpecOp(属性名) → 任意返回命名属性的值[[GetOwnProperty]]SpecOp(属性名) → Undefined 或 属性描述返回此对象的自身命名属性的属性描述,如果不存在返回 undefined[[GetProperty]]SpecOp(属性名) → Undefined 或 属性描述返回此对象的完全填入的自身命名属性的属性描述,如果不存在返回 undefined[[Put]]SpecOp(属性名, 任意Boolean)将指定命名属性设为第二个参数的值。flag 控制失败处理。[[CanPut]]SpecOp(属性名) → Boolean返回一个 Boolean 值,说明是否可以在 属性名 上执行 [[Put]] 操作。[[HasProperty]]SpecOp (属性名) → Boolean返回一个 Boolean 值,说明对象是否含有给定名称的属性。[[Delete]]SpecOp(属性名, Boolean) → Boolean从对象上删除指定的自身命名属性。flag 控制失败处理。[[DefaultValue]]SpecOp(暗示) → 原始类型暗示 是一个字符串 。返回对象的默认值[[DefineOwnProperty]]SpecOp(属性名, 属性描述, Boolean) → Boolean创建或修改自身命名属性为拥有属性描述里描述的状态。flag 控制失败处理。
<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		var user = {name:"老王",age:18,sex:"男"};
        //[[Prototype]]
		console.log(Object.prototype.toString(user));//[object Object]
		//[[Extensible]]
		console.log(Object.isExtensible(user)); //true
		//[[GetOwnProperty]]
		console.log(Object.getOwnPropertyNames(user));//Array(3):"name,age,sex"
		//[[GetProperty]]
		console.log(Object.getPrototypeOf(user));//[object Object]
		//[[HasProperty]]
		console.log(Object.hasOwnProperty("name"));//true
	</script>
</html>

3、常见的对象创建与赋值

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
		<script>
			//使用构造函数方式创建对象
			var obj = new Object();
			obj.name = "老王";
			obj.age = 18;
			obj.sex = "男";
			//使用对象字面量方式创建对象
			var user = {name:"老王",age:18,sex:"男"};
			//分别将其属性描述符打印出来
			console.log("obj属性描述:",Object.getOwnPropertyDescriptor(obj,"name"));
			console.log("user属性描述:",Object.getOwnPropertyDescriptor(user,"name"));
		    //  数据属性:[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]
            //  Configurable是否可以通过delete删除属性
            //  Enumerable可否for-in
            //  Writable能否修改属性值
            //  Value读取这个属性的数据值
		</script>
	</body>
</html>

    使用这种方式创建的对象,我们对一个Object对象设置属性时,一般是通过对象的.操作符或者[]操作符直接赋值的,通过这种方式添加的属性后续可以更改属性值,并且默认该属性是可枚举的他的数据属性描述符默认都是true)。如果我们想要修改他的属性默认的特性呢?那么Object.defineProperty()他来了。

三、Object.defineProperty()方法的使用

    Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象。详细文档可以看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

1、Object.defineProperty()语法

    语法:Object.defineProperty(obj, prop, descriptor)

    obj 要定义属性的对象。

    prop  要定义或修改的属性的名称或 Symbol 。

    descriptor 要定义或修改的属性描述符。

    返回值:被传递给函数的对象。

    注:在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一。

    该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for...in 或 Object.keys 方法),可以改变这些属性的值,也可以删除这些属性。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		var user = {};
		Object.defineProperty(user, 'age', {
		  value: 24,
		  writable: false
		console.log(user);
		console.log(user.age);//24
		user.age = 28; // throws an error in strict mode
		console.log(user.age);//24
	</script>
</html>

2、参数descriptor 属性描述符

    对象里目前存在的属性描述符有两种主要形式:数据描述符存取描述符数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。

2.1、数据描述符value 和 writable 

栗子:value -值、writable -是否可写

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		var user = {};
		Object.defineProperty(user, 'age', {
		  value: 24,
		  writable:true //默认为false,不可修改
		console.log(user); //[object Object]
		console.log(user.age);//24
		user.age = 28;
		console.log(user.age);//28
	</script>
</html>

2.2、存取描述符getter 和 setter

    和数据属性不同,存取器属性不具有可写性(writable attribute)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性。如果它只有setter方法,那么它是一个只写属性(数据属性中有一些例外),读取只写属性总是返回undefined。

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		function Archiver() {
			var temperature = null;
			var archive = [];
			Object.defineProperty(this, 'temperature', {
				get: function() {
					console.log('get!');
					return temperature;
				set: function(value) {
					temperature = value;
					archive.push({
						val: temperature
			this.getArchive = function() {
				return archive;
		var arc = new Archiver();
		arc.temperature; // 'get!'  访问时被调用
		arc.temperature = 11;  //设置时调用set--archive
		arc.temperature = 13;
		arc.getArchive(); // [{ val: 11 }, { val: 13 }]
		var obj = {};
		Object.defineProperty(obj, 'x', {
			set: function(val){
				this.x = "要设置我吗?";
		console.log(obj.x);//undefined
	</script>
</html>

2.3、共有属性configurable 和 enumerable

栗子:configurable - 是否可删除

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		var obj = {}
		Object.defineProperty(obj, 'name', {
			value: '老王',
			configurable: true,
		    writable: true
		delete obj.name
		console.log(obj.name);    // undefined
		Object.defineProperty(obj, 'name', {
			value: '老李',
			configurable: false, //不能被删除
		    writable: true
		delete obj.name
		console.log(obj.name); //老李
	</script>
</html>

栗子:enumerable - 是否可枚举

<!DOCTYPE html>
		<meta charset="UTF-8">
		<title></title>
	</head>
	</body>
	<script>
		var obj = {}
		Object.defineProperty(obj, 'name', {
			value: '老王',
			enumerable:true
		obj.sex = "男";
		Object.defineProperty(obj, 'age', {
			value: '28',
			enumerable:false
		console.log(Object.keys(obj)); //name,sex
		for(var keys in obj){
			console.log(keys+"="+obj[keys]);//name=老王 sex=男
		console.log(obj.propertyIsEnumerable('age')); //false
	</script>
</html>

    了解完了上诉内容,现在开始进入源码模式,探究一下Vue.$data、this._data和this.property 为何都能取到data里面的数据。

四、Vue.$data、this._data源码解析

1、找到Vue函数

    找到源码,Vue原来是一个函数。首先process.env.NODE_ENV是判断你启动时候的参数的,如果不符合的话,就发出警告,否则执行_init方法。值得一提的是一般属性名前面加_默认代表是私有属性,不对外展示。

function Vue(options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
    warn('Vue is a constructor and should be called with the `new` keyword')
  this._init(options)

2、初始化_init

    这个_init是哪来的呢?可以看到下面有很多初始化的函数,我们先看第一个initMixin,然后去查看他的定义。

initMixin(Vue)  //定义 _init(初始化就已经加载了,里面定义了Vue的原型方法_init)
stateMixin(Vue)  //定义 $set $get $delete $watch 等
eventsMixin(Vue) // 定义事件  $on  $once $off $emit
lifecycleMixin(Vue) // 定义 _update  $forceUpdate  $destroy
renderMixin(Vue) // 定义 _render 返回虚拟dom 

3、initMixin初始化

    initMixin中定义了原型方法_init,并初始化options参数,对生命周期变量初始化,初始化渲染Render,初始化 vm的状态,prop/data/computed/method/watch都在这里完成初始化,这里就有对data的初始化。initState(vm)

export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++
    // a flag to avoid this being observed-一个避免被观察到的标志
    vm._isVue = true
    // merge options - 合并opt信息
    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 || {},
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    // expose real self
    vm._self = vm
    initLifecycle(vm); // 定义 vm.$parent vm.$root vm.$children vm.$refs 等(生命周期变量初始化)
    initEvents(vm);   // 定义 vm._events  vm._hasHookEvent 等(事件监听初始化)
    initRender(vm); // 定义 $createElement $c (初始化渲染)
    callHook(vm, 'beforeCreate'); // 回调 beforeCreate 钩子函数
    initInjections(vm); // resolve injections before data/props (初始化注入)
    initState(vm);  // 初始化 props methods data computed watch 等方法 (状态初始化)
    initProvide(vm); // resolve provide after data/props
    callHook(vm, 'created'); // 回调 created 钩子函数
    // 如果有el选项,则自动开启模板编译阶段与挂载阶段
    // 如果没有传递el选项,则不进入下一个生命周期流程
    // 用户需要执行vm.$mount方法,手动开启模板编译阶段与挂载阶段
    if (vm.$options.el) {
      vm.$mount(vm.$options.el); // 实例挂载渲染dom

4、initState初始化

    将该对象赋值给 vm._data 属性,是函数也会转为变量。isReserved 函数通过判断一个字符串的第一个字符是不是 $ 或_来决定其是否是保留的,Vue 是不会代理那些键名以 $ 或 _ 开头的字段的,因为Vue自身的属性和方法都是以 $ 或 _ 开头的,所以这么做是为了避免与 Vue 自身的属性和方法相冲突。 如果 key 既不是以 $ 开头,又不是以 _ 开头,那么将执行 proxy 函数,实现实例对象的代理访问

function initData (vm: Component) {
  let data = vm.$options.data  //先通过$options获取到data
  data = vm._data = typeof data === 'function'  //将对象赋值给vm._data 
    ? getData(data, vm)  //判断data是不是通过返回函数对象的方式建立的,如果是,那么则执行getdata方法,getdata的方法主要操作就是 data.call(vm, 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',
  // proxy data on instance
  //检测data中的key是不是与props、methods中有重名
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') { //在非生产环境下如果发现在 methods 对象上定义了同样的key,打印一个警告
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
    } else if (!isReserved(key)) {//判断starts with $ or _
      proxy(vm, `_data`, key)
  // observe data
  observe(data, true /* asRootData */)

5、代理data->_data

    proxy(vm, `_data`, key);proxy 函数的原理是通过 Object.defineProperty 函数在实例对象 vm 上定义与 data 数据字段同名的访问器属性,并且这些属性代理的值是 vm._data 上对应属性的值。假如访问:vm.message,就会触发sharedPropertyDefinition的get,然后返回vm._data.message。

function proxy(target, sourceKey, key) {
	sharedPropertyDefinition.get = function proxyGetter() {
		return this[sourceKey][key]      //如:访问vm.message = 访问vm._data.message
	sharedPropertyDefinition.set = function proxySetter(val) {
		this[sourceKey][key] = val;
	Object.defineProperty(target, key, sharedPropertyDefinition);

6、在原型上绑定$data

    $data的数据劫持是在stateMixin函数中处理的,因为$data被定义为一个getter,实际上它仍然访问的是this._data。

function stateMixin (Vue) {
    // flow somehow has problems with directly declared definition object
    // when using Object.defineProperty, so we have to procedurally build up
    // the object here.
    var dataDef = {};
    dataDef.get = function () { return this._data };
    var propsDef = {};
    propsDef.get = function () { return this._props };
      dataDef.set = function () {
        warn(
          'Avoid replacing instance root $data. ' +
          'Use nested data properties instead.',
      propsDef.set = function () {
        warn("$props is readonly.", this);
    Object.defineProperty(Vue.prototype, '$data', dataDef); //将$data绑定到原型上
    Object.defineProperty(Vue.prototype, '$props', propsDef);
    Vue.prototype.$set = set;
    Vue.prototype.$delete = del;
    Vue.prototype.$watch = function (
      expOrFn,
      options
      var vm = this;
      if (isPlainObject(cb)) {
        return createWatcher(vm, expOrFn, cb, options)
      options = options || {};
      options.user = true;
      var watcher = new Watcher(vm, expOrFn, cb, options);
      if (options.immediate) {
        try {
          cb.call(vm, watcher.value);
        } catch (error) {
          handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
      return function unwatchFn () {
        watcher.teardown();

    注:数据劫持最著名的应用当属双向绑定,比较典型的是Object.defineProperty()和 ES2016 中新增的Proxy对象。Vue 2.x 使用的是Object.defineProperty()(Vue 在 3.x 版本之后改用 Proxy 进行实现)。

Vue.$data源码解析 $data是Vue实例中的实例属性,表示Vue实例观察的数据对象。官网给出的解释:vm.$data,类型:Object,详细:Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象 property 的访问。在了解vm.$data之前我们先来复习一下原型,然后再了解一下Object.defineProperty()。正是Vue内部实...
data中定义了一个数据msg, vue实例上访问这个数据有两种方式,this.$data.msg 和 this.msg,请问,为vue如何实现this.msg能直接访问到data中的msg变量???data又为什么是个函数? clone 下 vue 的项目源码,然后打开src/core/instance/index.js 调用 init 方法时先进行了检查,确保已经使用 n...
Vue关于data和this的解析总结:this.$data、this._data、this.xxx 为什么都能获取数据?data为什么是个函数? 如果 data 是个对象,那么整个vue实例将共享一份数据,也就是各个组件实例间可以随意修改其他组件的任意值,这样对于实际开发时很难受的。 但是 data 定义成一个函数,将会 return 出一个唯一的对象,不会和其他组件共享一个对象。 我们注册组件的时候实际上是建立了一个组件构造器的引用,只有使用组件的时候才会真正创建一个组件实例。 总而言之: this.d
$dataVue实例中的实例属性,表示Vue实例观察的数据对象。实际上在Vue官网对这部分有较为详细的描述,这里就不再赘述了(具体可看官网的描述Vue选项/数据)。本篇文章从源码层次来梳理$data背后的逻辑,实际上就是一个问题: 假设存在属性name,为什么修改vm.$data.name与vm.name可以达到相同效果? 实例属性$data 假设存在属性name,为什么修改vm.$data.name与vm.name可以达到相同效果? 以此问题为出发点,假设data中存在name,实际上就是
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象 this.$data获取当前状态下的data this.$options.data()获取该组件初始状态下的data。 object.assign(this.$data, this.$options.data()) 使用场景:有一个表单,表单提交成功后,希望组件恢复到初始状态,重置data数据。 然后只要使用Object.assign(this.$data, this.$options.data())就可以将当前
Vue实例中,我们可以通过`this`来访问实例的数据和方法。其中,`this.data`和`this.$data`都是用来访问Vue实例的数据的方式。 `this.$data`是Vue提供的一个属性,它指向Vue实例的数据对象,也就是我们在`new Vue()`时传入的`data`选项。例如: new Vue({ data: { message: 'Hello World' created: function () { console.log(this.$data.message) // 'Hello World' 上面的代码中,我们在Vue实例的`created`生命周期函数中使用了`this.$data.message`来访问Vue实例的数据对象。 而`this.data`并不是Vue提供的属性,它在Vue实例中并没有任何意义。如果我们在Vue实例中使用`this.data`来访问数据,将会得到`undefined`的结果。例如: new Vue({ data: { message: 'Hello World' created: function () { console.log(this.data.message) // undefined 因此,在Vue实例中,我们应该使用`this.$data`来访问实例的数据对象。