MVVM及数据代理

MVVM及数据代理

2.2.1 MVVM分层思想

MVVM及数据代理_第1张图片

  1. MVVM是什么?
    • M:Model(模型/数据):代表应用程序的数据和业务逻辑,它可以是从服务器获取的数据、本地存储的数据,或者是应用程序内部产生的数据。例如,在一个电商应用中,商品的信息(如名称、价格、库存等)就属于Model的范畴。
    • V:View(视图):负责展示数据给用户,是用户直接交互的界面部分。比如电商应用中的商品列表页面、商品详情页面等。
    • VM:ViewModel(视图模型):VM是MVVM中的核心部分。(它起到一个核心的非常重要的作用。)它作为Model和View之间的桥梁,将Model中的数据转换为View可以展示的形式,并处理View的交互事件,将其映射到Model的更新上。
    • MVVM是目前前端开发领域当中非常流行的开发思想。(一种架构模式。)
    • 目前前端的大部分主流框架都实现了这个MVVM思想,例如Vue,React等。
  2. Vue框架遵循MVVM吗?
    虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。
    Vue框架基本上也是符合MVVM思想的。Vue通过数据绑定和指令系统,使得数据的变化能够自动反映到视图上,同时视图的交互也能影响数据的更新,这与MVVM的理念相契合。
  3. MVVM模型当中倡导了Model和View进行了分离,为什么要分离?
    假如Model和View不分离,使用最原始的原生的javascript代码写项目:
    如果数据发生任意的改动,接下来我们需要编写大篇幅的操作DOM元素的JS代码。例如,要更新页面上一个列表的显示,可能需要遍历列表元素,逐个修改其文本内容、样式等。
    将Model和View分离之后,出现了一个VM核心,这个VM把所有的脏活累活给做了,也就是说,当Model发生改变之后,VM自动去更新View。当View发生改动之后,VM自动去更新Model。我们再也不需要编写操作DOM的JS代码了。开发效率提高了很多。
<body>
  
  
  <div id="app">
    姓名:<input type="text" v-model="name">
  div>

  
  <script>
    // ViewModel  VM
    const vm = new Vue({
      el : '#app',
      // Model  M
      data : {
        name : 'zhangsan'
      }
    })
  script>
body>
2.2.2、vm可以访问哪些属性

通过Vue实例都可以访问哪些属性?(通过vm都可以vm. 什么。)

  1. Vue实例中的属性很多,有的以 $ 开始,有的以 _ 开始。
    • a. 所有以 $ 开始的属性,可以看做是公开的属性,这些属性是供程序员使用的。例如 vm.$el 可以获取到Vue实例所绑定的DOM元素,vm.$watch 可以用于监听数据的变化。
    • b. 所有以 _ 开始的属性,可以看做是私有的属性,这些属性是Vue框架底层使用的。一般我们程序员很少使用。比如 vm._uid 表示Vue实例的唯一标识符,vm._isVue 用于标识该对象是否是Vue实例。
  2. 通过vm也可以访问Vue实例对象的原型对象上的属性,例如:vm.$delete 等。vm.$delete 方法用于删除对象的属性,并且会触发视图的更新。
<body>
    <div id="app">
      <h1>{{msg}}h1>
    div>
    <script>
      let dataObj = {
        msg: "Hello Vue!",
      };

      const vm = new Vue({
        el: "#app",
        data: dataObj,
      });

      // 按说msg是dataObj对象的属性。
      console.log("dataObj的msg", dataObj.msg);

      // msg属性也可以通过vm来访问呢?
      // 这是因为Vue框架底层使用了数据代理机制。
      // 要想搞明白数据代理机制,必须有一个基础知识点要学会:Object.defineProperty()。
      // console.log("vm的msg", vm.msg);

      // 模拟Vue构造函数,无法直接用newVm.msg打印出
      function myVue(obj) {
        return obj
      }
      let newVm = new myVue({
        el: "#app",
        data: dataObj,
      });
      console.log(newVm.data.msg);
    script>
  body>
2.2.3、object.defineProperty()
  1. 这个方法是ES5新增的。它为JavaScript对象的属性操作提供了更强大的控制能力,使得开发者可以在属性的读取、设置等操作上添加自定义的逻辑。
  2. 这个方法的作用是:给对象新增属性,或者设置对象原有的属性。
  3. 怎么用?
    Object.defineProperty(给哪个对象新增属性, '新增的这个属性名叫啥', {给新增的属性设置相关的配置项key:value对})
  4. 第三个参数是属性相关的配置项,配置项都有哪些?每个配置项的作用是啥?
    • value 配置项:给属性指定值。例如 Object.defineProperty(person, "age", { value: 30 }); 就给 person 对象添加了 age 属性,并赋值为 30
    • writable 配置项:设置该属性的值是否可以被修改。true 表示可以修改。false 表示不能修改。如 Object.defineProperty(person, "name", { value: "Tom", writable: false }); 之后,person.name 的值就不能被修改了。
    • enumerable 配置项:设置该属性是否可以被遍历。
      • true 表示该属性是可以遍历的。(可枚举的,可迭代的。)
      • false 表示该属性是不可遍历的。例如 Object.defineProperty(person, "secret", { value: "Some secret", enumerable: false }); 后,使用 for...in 循环遍历 person 对象时,secret 属性不会被遍历到。
    • configurable 配置项:设置该属性是否被删除。
      • true 表示该属性是可以被删除的。
      • false 表示该属性不可以被删除。如 Object.defineProperty(person, "id", { value: 1, configurable: false }); 后,delete person.id 将不会成功。
    • getter方法 配置项:不需要我们手动调用的。当读取属性值的时候,getter 方法被自动调用。getter 方法的返回值非常重要,这个返回值就代表读取的这个属性它的值。
    • setter方法 配置项:不需要我们手动调用的。当修改属性值的时候,setter 方法被自动调用。setter 方法上是有一个参数的,这个参数可以接收传过来的值。
      注意:当配置项当中有 settergetter 的时候,valuewritable 配置项都不能存在。
<body>
    <script>
      // 这是一个普通的对象
      let person = {};
      // 临时变量   
      let temp;
      // 给上面的phone对象新增一个color属性
      Object.defineProperty(person, "name", {
        //value : '章三',
        //writable : true,
        enumerable: false,
        // true表示该属性是可以遍历的。(可枚举的,可迭代的。)
        // false表示该属性是不可遍历的。

        configurable: false,
        // true表示该属性是可以被删除的。
        // false表示该属性是不可以被删除的。
        
        // getter方法配置项
        get: function () {
          console.log("getter方法执行了@@@");
          //return '动态'
          //return this.name  //递归,死循环
          return temp;
        },
        // setter方法配置项
        set: function (val) {
          console.log("setter方法执行了@@@", val);
          //this.name = val  //递归,死循环
          temp = val;
        },
      });
    script>
  body>
2.2.4、数据代理机制

通过访问 代理对象的属性 来间接访问 目标对象的属性。数据代理机制的实现需要依靠:Object.defineProperty() 方法。
注意:代理对象新增的这个属性的名字 和 目标对象的属性名要一致,这样我们访问代理对象属性,就像在访问目标对象的属性一样

<script>
      // 目标对象
      let target = {
        name: "zhangsan",
      };
      // 代理对象
      let proxy = {};
      // 如果要实现数据代理机制的话,就需要给proxy新增一个name属性。
      Object.defineProperty(proxy, "name", {
        get() {
          console.log("getter方法执行了@@@@");
          // 间接访问目标对象的属性
          return target.name;
        },
        set(val) {
          target.name = val;
        },
      });
    </script>
2.2.5、数据代理时对属性名的要求
  1. Vue实例不会给以 _ 和 $ 开始的属性名做数据代理。
  2. 为什么?
    如果允许给 _ 或 $ 开始的属性名做数据代理的话。vm 这个Vue实例上可能会出现 _xxx 或 x x x 属性,而这个属性名可能会和 V u e 框架自身的属性名冲突。例如, V u e 框架已经使用了 ‘ xxx 属性, 而这个属性名可能会和Vue框架自身的属性名冲突。 例如,Vue框架已经使用了 ` xxx属性,而这个属性名可能会和Vue框架自身的属性名冲突。例如,Vue框架已经使用了el来表示绑定的DOM元素,如果用户定义的属性也叫$el`,就会产生混淆和错误。
  3. 在Vue当中,给data对象的属性名命名的时候,不能以 _ 或 $ 开始。
<body>
    
    <div id="app">
      <h1>{{msg}}h1>
    div>
    
    <script>
      const vm = new Vue({
        el: "#app",
        data: {
          msg: "Hello Vue!",
          _name: "zhangsan", //不会做数据代码,vm上看不到
          $age: 20,//不会做数据代码,vm上看不到
        },
      });
    script>
  body>
2.2.6、模拟实现数据代理

简单实现 myvm.name==options.data.name

<script>
      const myvm = new MyVue({
        data: {
          msg: "Hello Vue!",
          name: "jackson",
          age: 30,
        },
      });
    </script>

数据代理js

// 实现数据代理,目的是读取  myvm.name == options.data.name
// 定义一个Vue类
class MyVue {
  // 定义构造函数
  // options 是一个对象{}
  // options对象中有一个data配置项
  constructor(options) {
    Object.keys(options.data).forEach((propertyName, index) => {
      Object.defineProperty(this, propertyName, {
        get() {
          // 读取对象的属性值 对象[变量]
          return options.data[propertyName];
        },
        set(val) {
          options.data[propertyName] = val;
        },
      });
    });
  }
}
2.2.7、Vue中的数据代理与数据劫持

vm 身上,有两个属性 $data_data,这两个属性都指向Vue底层的真实的 data 对象,通过 $data_data 获取各属性值,是不会走数据代理机制的。
其中:_data 是框架内部使用的,可以看做是私有的;$data,这是Vue框架对外公开的一个属性。
也就是说Vue框架会将 data 中的数据实时通过 Object.defineProperty 拷贝一份放在 $data 以及 _data 身上,供 vm 去使用。

  1. Vue的数据代理
    • 基本阐述:vue将 _data 中的所有数据属性通过 Object.defineProperty 添加到 vm 实例上,并且提供了 gettersetter 方法,于是通过 vm 直接获取数据的时候就调用 getter,获取 _data 中的值,当修改的时候调用 setter 修改 _data 中的值。这样,开发者在模板中直接使用数据时,实际上是通过 vm 实例访问到了 _data 中的数据。
    • 数据代理有什么用呢?:既然 vm 上挂的属性就是 _data 中的数据代理,那么 {{vm._data.name}}{{name}} 是等价的,{{vm._data.name='szk2'}}{{name='szk2'}} 也是等价的。所以就是为了写代码的方便,在 {{}} 直接写数据,或者直接修改就能操作到 _data 中。例如,在模板中直接写 {{msg}} 就可以展示 _datamsg 的值,当 msg 的值发生变化时,视图会自动更新。
  2. 数据劫持:数据劫持就是将vue代码里我们写的 data 加工了一下,变成 _data,让每个属性有了 gettersetter。vue通过监听者 observer 来监听 data 中的数据,通过 gettersetter 监听者里面的方法,监听数据的读取与修改,当修改属性的时候,setter 被调用,在 setter 方法中就会让订阅者执行重新解析模板的操作,从而改变了页面。例如,当用户在输入框中输入内容,修改了绑定的数据时,setter 方法会被触发,进而通知相关的订阅者(如视图更新函数),使得页面上的显示内容也随之更新。
    先看看我们写的 data 和加工之后的 data 有什么区别:原始的 data 只是一个普通的对象,其中的属性没有任何特殊的行为。而经过数据劫持处理后的 _data,其属性具有了 gettersetter 方法,能够实现数据的响应式变化。
  3. 总结
    • 数据劫持:创建Vue实例 vmvm 身上会有 _data 属性,_data 通过劫持 data 配置项,再通过 definePropertygettersetter,得到的响应式的数据。把vue中的 data 数据拦截改写成具有 gettersetter 形式的 _data,就是数据劫持。
    • 数据代理vm_data 中的数据又通过数据代理(也是通过 definePropertygettersetter 实现),放置到 vm 身上,vm 可以通过 getter 方法,setter 方法直接使用 _data 中的数据,方便书写数据。
    • 步骤:
      1. 把vue实例中的 data,通过 Object.definePropertysettergetter 进行数据劫持,使得 data 改写为到 _data(使得数据改写为响应式的数据,具有 gettersetter);
      2. 通过 vm 进行数据代理,代理 _data

你可能感兴趣的:(Vue2,Vue2)