zoukankan      html  css  js  c++  java
  • Vue 之 Data

    Vue 之 Data

    描述

    Vue 实例的数据对象。Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的属性会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。

    一旦观察过,不需要再次在数据对象上添加响应式属性。因此推荐在创建实例之前,就声明所有的根级响应式属性。

    实例创建之后,可以通过 vm.$data 访问原始数据对象。Vue 实例也代理了 data 对象上所有的属性,因此访问 vm.a 等价于访问 vm.$data.a

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

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

    如果需要,可以通过将 vm.$data 传入 JSON.parse(JSON.stringify(...)) 得到深拷贝的原始数据对象。

    注意,如果你为 data 属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。

    data: vm => ({ a: vm.myProp })
    

    Vue组件中的data为什么是函数

    类比引用数据类型

    Object是引用数据类型,如果不用function 返回,每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;

    javascipt只有函数构成作用域(注意理解作用域,只有函数的{}构成作用域,对象的{}以及 if(){}都不构成作用域),data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。

    举个例子:

    const MyComponent = function( ) {};
    MyComponent.prototype.data = {
      a: 1,
      b: 2,
    }
    const component1 = new MyComponent();
    const component2 = new MyComponent();
    
    component1.data.a === component2.data.a; // true
    component1.data.b = 5;
    component2.data.b // 5
    

    如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改;

    两个实例应该有自己各自的域才对,需要通过下面的方法来进行处理:

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

    这样么一个实例的data属性都是独立的,不会相互影响了。

    所以,你现在知道为什么vue组件的data必须是函数了吧。这都是因为js本身的特性带来的,跟vue本身设计无关。其实vue不应该把这个方法名取为data(),应该叫setData或其他更容易理解的方法名。

    不要把所有东西都放进data里

    Vue组件实例中的data是我们再熟悉不过的东西了,用来存放需要绑定的数据但是对于一些特定场景,data虽然能够达到预期效果,但是会存在一些问题我们写下如下代码,建立一个名单,记录他们的名字,年龄和兴趣:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Data</title>
    </head>
    
    <body>
      <div id="app"> </div>
    
      <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
      <script> const template = ` <div> <h3>data列表</h3> <ol> <li v-for="item in dataList"> 姓名:{{item.name}},年龄:{{item.age}},兴趣:{{item.hobby.join('、')}} </li> </ol> </div> ` new Vue({ el: '#app', data () { return { dataList: [ { name: '张三', age: 33, hobby: ['唱','跳','rap','篮球'] }, { name: '李四', age: 24, hobby: ['唱','跳','rap','篮球'] }, { name: '王五', age: 11, hobby: ['唱','跳','rap','篮球'] }, { name: '赵六', age: 54, hobby: ['唱','跳','rap','篮球'] }, { name: '孙七', age: 23, hobby: ['唱','跳','rap','篮球'] }, { name: '吴八', age: 55, hobby: ['唱','跳','rap','篮球'] } ], } }, mounted () { console.table(this.dataList) // 打印列表形式的dataList console.log(this.dataList) // 打印字面量 }, template }) </script>
    </body>
    </html>
    

    Vue通过data生成我们能用的绑定数据,大概走了以下几个步骤:

    1. 从 initData 方法 中获取你传入的data,校验data是否合法
    2. 调用 observe 函数,新建一个 Observer 实例,将 data 变成一个响应式对象,而且为data添加 __ob__ 属性,指向当前 Observer实例
    3. Observer 保存了你的 value 值、数据依赖 dep 和 vue 组件实例数vmCount
    4. 对你的data调用 defineReactive$$1 递归地监所有的 key/value(你在data中声明的),使你的key/value都有自己的 dep, getter 和 setter

    我们忽略html的内容,重点放在这个 dataList 上(我用2种不同的形式打印了dataList),如上述步骤2、3、4所说,data中每个key/value值(包括嵌套的对象和数组)都添加了一个Observer。

    之前我们说滥用data会产生一些问题,问题如下:

    设想一下这样的场景,如果你的data属于纯展示的数据,你根本不需要对这个数据进行监听,特别是一些比这个例子还复杂的列表/对象,放进data中纯属浪费性能。

    那怎么办才好?

    放进computed中

    还是刚才的代码,我们创建一个数据一样的list,丢进computed里:

    computed: {
      computedList () {
        return [
          { name: '张三', age: 33, hobby: ['唱','跳','rap','篮球'] },
          { name: '李四', age: 24, hobby: ['唱','跳','rap','篮球'] },
          { name: '王五', age: 11, hobby: ['唱','跳','rap','篮球'] },
          { name: '赵六', age: 54, hobby: ['唱','跳','rap','篮球'] },
          { name: '孙七', age: 23, hobby: ['唱','跳','rap','篮球'] },
          { name: '吴八', age: 55, hobby: ['唱','跳','rap','篮球'] }
        ]
      }
    },
    

    打印computedList,你得到了一个没有被监听的列表

    为什么computed没有监听我的data

    因为我们的computedList中,没有任何访问当前实例属性的操作,根据Vue的依赖收集机制,只有在computed中引用了实例属性,触发了属性的getter,getter会把依赖收集起来,等到setter调用后,更新相关的依赖项。

    我们来看官方文档对computed的说明:

    var vm = new Vue({
      el: '#example',
      data: {
        message: 'Hello'
      },
      computed: {
        // 计算属性的 getter
        reversedMessage: function ( ) {
          // `this` 指向 vm 实例
          return this.message.split('').reverse().join('')
        }
      }
    })
    

    这里强调的是

    所以,对于任何复杂逻辑,你都应当使用计算属性。

    但是很少有人注意到api说明中的这一句:

    计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。

    也就是说,对于纯展示的数据,使用computed会更加节约你的内存

    另外 computed 其实是Watcher的实现,有空的话会更新这部分的内容

    为什么说“至少2.0是如此”

    因为3.0将使用Proxy来实现监听,性能将节约不少,参见https://www.jianshu.com/p/f99822cde47c

  • 相关阅读:
    leetcode 268. Missing Number
    leetcode 189. Rotate Array
    leetcode 118. Pascal's Triangle 、119. Pascal's Triangle II 、120. Triangle
    HDU高精度总结(java大数类)
    一起talk C栗子吧(第八十七回:C语言实例--使用管道进行进程间通信概述)
    iOS敏捷开发之道,经常使用的宏定义总结
    360 网络攻防 hackgame 解题报告(通关)
    打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
    数列求和
    指尖上的电商---(11)Windows平台部署SolrCloud
  • 原文地址:https://www.cnblogs.com/xuzhenlei/p/12410378.html
Copyright © 2011-2022 走看看