零基础入门Vue之画龙点睛——再探监测数据

这篇具有很好参考价值的文章主要介绍了零基础入门Vue之画龙点睛——再探监测数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

追忆

上一节:零基础入门Vue之影分身之术——列表渲染&渲染原理浅析

虽然我深知,大佬告诉我”先学应用层在了解底层,以应用层去理解底层“,但Vue的数据如何检测的我不得不去学

否则,在写代码的时候,可能会出现我难以解释的bug

对此,本篇文章,将记录我对Vue检测数据的理解


对于Vue检测数据的实现,我打算由浅入深的去记录

  1. JavaScript实现数据监控
  2. 实现简单的数据监测(浅浅的响应式)
  3. Vue对哪些数据做了监测,哪些没有?

JavaScript的数据检测

Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。

熟悉JavaScript的人,应该在一个月黑风高的夜晚都了解过Object上的一个方法: Object.defineProperty()

而Vue大部分的数据监测都是依赖于这个方法来实现的

ps:本篇不会深度探讨这个静态方法的用法,仅仅对其get和set方法的用法讲解,为下一章节做铺垫

Object.defineProperty(obj, prop, descriptor)
  • obj:要添加新属性的对象
  • prop:要添加属性的名称(一般为字符串)
  • descriptor:这个属性的相关描述(配置)

假设现在有一个对象如下:

let person = {
  name:"张三"
};

现在,我试图这个person对象新增一个age属性,那么我可以这么干

Object.defineProperty(person, "age", {
  value:18 //设置默认的值
});

此时输出person时可以看到

{name: '张三', age: 18}

题外话:关于为什么是点开后为什么age属性与其他属性颜色不一样,可以将enumerable:true,使它可迭代

但这真的是一个普通属性了吗?并不会,当我试图

person.age = 20
console.log(person.age); //18

似乎修改不了数据,因为少了一个配置项

Object.defineProperty(person, "age", {
    value:18,
    writable:true //配置为可修改
});

此时上述代码差不多等同于

person.age = 18; //假设age没定义过,重新给person追加一个属性

get

用作属性 getter 的函数,如果没有 getter 则为 undefined。当访问该属性时,将不带参地调用此函数,并将 this 设置为通过该属性访问的对象(因为可能存在继承关系,这可能不是定义该属性的对象)。返回值将被用作该属性的值。默认值为 undefined。


从上面MDN上的话来说,当我们要用到对象上的某个属性时会调用 getter,如果对象没有设置,则默认是undefined,当访问这个属性时,访问得到的结果将是getter返回的结果

我现在有一个需求:当age属性被读取时,就加1岁,并且输出变化后的值

在上述代码里面是完不成这个需求的,此时就需要用到get方法了,当读取时会自动调用get方法,我可以在那个里面进行数据的递增

注意:官网描述中,get不能与 value 或 writable 同时使用

  1. 定义实际年龄数据:_age
  2. 当读取person.age,get选择器返回_age,并且把_age递增

具体实现代码如下:

let age = 18; //定义一个实际的年龄数据
let person = { //准备好目标对象
  name:"张三"
};

Object.defineProperty(person,"age",{
  get(){
    console.log("年龄即将递增一岁后:",age + 1);
    return age,age++;
  }
});
> person.age
年龄即将递增一岁后: 19
18
> person.age
年龄即将递增一岁后: 20
19

综上所述,当有人要读取某个属性的时候,可以对这个属性值做了处理在返回,或者是调用函数通知其他的事件触发等等


set

用作属性 setter 的函数,如果没有 setter 则为 undefined。当该属性被赋值时,将调用此函数,并带有一个参数(要赋给该属性的值),并将 this 设置为通过该属性分配的对象。默认值为 undefined。


set的用法正好和get方法对应,一个是读一个是写,set方法当要给属性赋值时会被调用,并且可以接收一个参数作为新的值

同样以这个对象为例,还是把get赋值写好,每次都返回当前age的值

let age = 18; //定义一个实际的年龄数据
let person = { //准备好目标对象
  name:"张三"
};

Object.defineProperty(person,"age",{
  get(){
    return age;
  }
});

现在呢,如果我去修改他得值,他还是出现改不掉的情况

这是因为set没配置,我先配置个最基本的set看看,能否修改age的值

let age = 18; //定义一个实际的年龄数据
let person = { //准备好目标对象
  name:"张三"
};

Object.defineProperty(person,"age",{
  get(){
    return age; //返回实际年龄
  },
  set(newVal){
      age = newVal //修改实际年龄
  }
});
> person.age
18
> person.age = 19
19
> person.age
19

很显然是可以修改的

现在呢,我希望当我对年龄做出了修改,如果不是递增1的话就弹出警告

那么我可以这么干

let age = 18; //定义一个实际的年龄数据
let person = { //准备好目标对象
  name:"张三"
};

Object.defineProperty(person,"age",{
  get(){
    return age;
  },
  set(newVal){
      if(newVal !== age+1){
          console.warn("注意:你这个年龄增加的有点快啊!!!");
      }
      age = newVal
  }
});

此时这段代码,能完美的完成需求


画龙点睛

上面的例子,还是不怎么完善,万一有人给年龄随意赋值呢?那我是不是要弹出报错?

所以,当调用set的时候,可以进行一系列的数据类型判断,这里仅需判断是否为数值即可,区别不能为负值不然就抛出错误

代码如下:

let age = 18; //定义一个实际的年龄数据
let person = { //准备好目标对象
  name:"张三"
};

Object.defineProperty(person,"age",{
  get(){
    return age;
  },
  set(newVal){
      if(typeof newVal !== 'number'){
          throw "你在想什么呢?";
      }else if(newVal < 0){
          throw "你跟阎王沟通过?";
      }else if(newVal !== age+1){
          console.warn("注意:你这个年龄增加的有点快啊!!!");
      }
      age = newVal;
  }
});

实现简单的数据检测

在第一篇:零基础入门Vue之梦开始的地方——插值语法 中我提到如下的说明

"{{}}"在这个表达式里面可以写js的表达式,并且它里面的执行语句的this是vue实例,同时vue官方文档指出,在data中配置的东西最后都会通过数据代理的方式挂在到vue实例上。

data配置项里面的所有数据,都会以数据代理的方式挂在到vm实例上,并且Vue也会提供一个纯净版的Vue._data,此时这个Vue._data等同于我们配置的data
(即:vm._data === data is true)

而这个数据代理就是依赖于上一节说的 Object.defineProperty() 来实现


实现原理简单分析&实现

在Vue中,Vue对实际传入的data并没有直接挂在到vm对象及vm._data上,而是重新通过get和set去做一系列的数据代理和数据监测

这个过程中有许多细节要处理,本篇不可能以这一千不到的字数去说明白Vue的数据检测和数据代理

仅仅只是做一个基本的样例,供我自己学习


首先,我得准备一个方法用来刷新dom意思意思一下

function flashVirtualDom(){
  //此处省略新老虚拟dom之间的比较算法
  console.log("检测到数据更改,准备刷新虚拟dom");
}

然后呢,我要准备好一个数据

let data = {
    name:"张三",
    age:18,
    friends:["李四","王五","赵高"],
    school:{
        name:"北京大学",
        local:"北京",
        totalYears:4
    }
};

目标:接下来我希望不直接操作data的数据,而是用另外的方法去操作data的数据,并且当data数据发送改变时能被我写的代码检测到

既然不是直接操作,那么用户和真实数据应该有一个 中间层,所以我把它明明为Middle

这个中间层呢,使用Object.defineProperty() 把data的数据挂载到自己实例上,可以操作它的实例间接更改data

(换个说法:在上一节age就是真实数据,而person.age实际上是真实数据的代理,我并没有直接操作age,只不过这次数据交给data对象,更加的密集,集中保管和内部维护)

function Middle(obj){
    let keys = Object.keys(obj); //拿到所有的key
    for(let key of keys){
        //如果是对象
        if(typeof obj[key] === "object" && !(obj[key] instanceof Array)){
            this[key] = new Middle(obj[key]); //如果是对象嵌套那么递归调用
            continue;
        }else{
            //过滤undefined
            if(!obj[key]){
                continue;
            }

            //设置数据代理
            Object.defineProperty(this,key,{
                get(){
                    //读取就返回原模原样的值
                    return obj[key];
                },
                set(newVal){
                    //赋值就修改原始数据
                    flashVirtualDom();
                    obj[key] = newVal;
                }
            })
        }
    }
}

此时,在console执行如下代码

> let m = new Middle(data);
undefined
> m.age = 30
检测到数据更改,准备刷新虚拟dom
30
> m.age
30

展开m,与Vue实例的_data进行对比,相差不大,当我修改其中一个数据时会调用flashVirtualDom();方法用于刷新dom,同样还可以写其他方法做其他操作


数据劫持

在尚硅谷的课程中,有提到”数据劫持“这一概念,对此,本篇也相应的记录下

个人理解的数据劫持,实际上是当数据发生变动时,率先拦截变动,做出处理后,在决定是否要变动数据

或者更改变动的结果,其在我理解更类似于hook的操作,当某个函数或者变量被赋值,然后我对他进行一次拦截

拦截之后做出我想要的操作,操作做完再让他回归正常运行


Vue中的数据监测

在做Vue的开发中,Vue并非对任何数据都做了监测,因此我认为我作为Vue的学习者,应当去了解具体有哪些情况不会被监测到,从而避免日后开发的各种奇奇怪怪的问题。

对于常见数据,有两种比较容易出错

  • 对象
  • 数组

对象相关的数据监测问题&解决方案

假设,在初始的data里面仅有如下数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="./vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="root">
        <div>姓名:{{person.name}}</div>
        <div>性别:{{person.sex}}</div>
        <div>年龄:{{person.age}}</div>
    </div>
</body>
<script>
    let vm = new Vue({
        el:"#root",
        data:{
            person:{
                name:"张三",
                sex:"男"
            }
        }
    })
</script>
</html>

这都很正常,但如果我想不修改这个代码,在项目上线后根据后端给的数据动态的增加

假设在某处代码上后端返回的数据有年龄,我想data的数据增加一条年龄并且展示到应该展示的位置

那么我试图这么做

vm.data.age = 19; //假设后端给出的数据是19

此时页面无任何变化

(实际操作,可以先让页面运行起来,然后再console上去追加一个年龄)

这个后期追加的数据在Vue中并没有被监测,导致他没有显示(简单说就是没有get和set方法)

那我该如何解决呢?

Vue非常人性化的提供另外的方法:Vue.set( target, propertyName/index, value )

注:用vm.$set也是一样的,详细区别可以去翻官方文档

在这个方法允许给某个对象添加属性并且监测

  • target:目标对象
  • propertyName/index:属性名称/索引值(可用于数组)
  • value:值

所以当我接收到后端数据时,我可以用这个方法追加数据并且被Vue所监测

Vue.set(vm.person,'age',19); //假设后端给出的数据是19

此时age即可直接显示到页面上了


数组相关的数据监测问题&解决办法

假设,这次问题代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="./vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="root">
        <ul>
            <li v-for="per in friends" :key="per.id">
                {{per}}
            </li>
        </ul>
    </div>
</body>
<script>
    let vm = new Vue({
        el:"#root",
        data:{
            friends:["张三","李四","王五","赵高"]
        }
    })
</script>
</html>

现在呢,我想要修改friends第一个元素,修改为”张四“

正常做法如下:

vm.friends[0] = "张四";

但页面数据无变化,实际数据已经更改了

这是为什么呢?展开friends数组,发现这并没有数组成员的get和set方法,Vue并未对数组成员做监测,因此改了之后,数据并未刷新

那么,我该如何做呢?官网其实早就给出了答案:变更方法。 官方对以下七个方法进行了包裹(我感觉hook更好理解)

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

依次,但我通过这些方法去做数组进行”增删改查“时,Vue会检测到,所以我想修改第一个元素为李四可以这么干

vm.friends.splice(0,1,"张四")

执行完后页面变了,说明变动被监测到了,除此之外还可以使用set方法

Vue.set(vm.friends,0,"张四");

The End

唔~(一口浊气)

这一篇可真长啊

本篇完~~~~


下一篇:零基础入门Vue之拘元遣将——其他常用指令&自定义指令文章来源地址https://www.toymoban.com/news/detail-825142.html

到了这里,关于零基础入门Vue之画龙点睛——再探监测数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 05_Vue基础入门

    vue的作用 渐进式的前端框架(里面的框架按需获取) vue特点 1.解耦视图和数据 2.双向数据绑定 3.可复用的组件:常用的按钮和表格 4.前端路由数据:页面跳转 5.状态管理 6.虚拟DOM 引入方式 1.使用CDN连接引入,加载速度更快 2.下本地载源码引入 将文件保存到项目中,拖到引用的js文件

    2024年02月11日
    浏览(35)
  • vue基础入门

    1.1 什么是vue 官方概念:Vue(读音/vju:/,类似于view)是一套 用于构建用户界面 的前端 框架 1.2 vue 的特性 vue 框架的特性,主要体现在如下两方面: ① 数据驱动视图 ② 双向数据绑定 数据驱动视图 在使用了 vue 的页面中,vue 会 监听数据的变化 ,从而 自动 重新渲染页面的结

    2024年02月05日
    浏览(25)
  • vue 基础入门

    目录 vue 基础入门 vue 简介 1、vue 的特性 vue 的基本使用 1、基本使用步骤 2、vue 的调试工具 vue 的指令 1、内容渲染指令{{ }} 2、属性绑定指令 v-bind: 3、事件绑定  v-on:   @ 4、双向绑定指令 v-model 5、 条件渲染指令 v-if v-show   6 、列表渲染指令 v-for vue过滤器  1、定义过滤器

    2024年02月11日
    浏览(23)
  • Vue基础入门(中)

    组件的定义 组件具备复用性 注意: 全局组件 ,只要定义了,处处可以使用,性能不高,但是使用起来简单  与其对应的还有 局部组件 解释:单引号\\\'counter\\\'代表组件名字,也可随意命名,但是为了见名其意,建议使用变量名(es6语法中在key和value一样时可以只写counter即可)

    2024年02月06日
    浏览(23)
  • vue3 快速入门系列 —— 基础

    前面我们已经用 vue2 和 react 做过开发了。 从 vue2 升级到 vue3 成本较大,特别是较大的项目。所以许多公司对旧项目继续使用vue2,新项目则使用 vue3。 有些UI框架,比如ant design vue1.x 使用的 vue2。但现在 ant design vue4.x 都是基于 vue3,示例默认是 TypeScript。比如 table 组件管理。

    2024年04月08日
    浏览(44)
  • Vue2基础一、快速入门

    1、Vue 概念 (1)为什么学 前端必备技能 岗位多,绝大互联网公司都在使用Vue 提高开发效率 高薪必备技能(Vue2+Vue3) (2)Vue是什么 **概念:**Vue (读音 /vjuː/,类似于 view) 是一套 **构建用户界面 ** 的 渐进式 框架 官网: https://v2.cn.vuejs.org/ 构建用户界面:基于数据 渲染出用

    2024年02月15日
    浏览(40)
  • 前后端分离--Vue的入门基础版

    目录 一.前后端分离 二.Vue的简介 三.Vue的入门案例 四.Vue的生命周期          前后端分离是一种 软件架构模式 ,将应用程序的前端(用户界面)和后端(数据处理和业务逻辑)独立开发、独立部署。在前后端分离的架构下, 前端通过API(应用程序接口)与后端进行通信

    2024年02月07日
    浏览(32)
  • 第四章:前端框架Vue基础入门

    本章学习目标: 了解Vue框架架构思想 掌握Vue核心指令 掌握计算属性与监视 掌握组件化开发模式 官方文档:https://cn.vuejs.org/guide/introduction.html. 文档可选择使用optionsAPI(选项式API)阅读,或者CompositionApi(组合式API)阅读。选项式API更适合平滑从vue2过渡,以下示例均以Compositi

    2024年02月12日
    浏览(45)
  • 零基础入门Vue之皇帝的新衣——样式绑定

    大致掌握了上一节的 插值语法 我已经可以把想要的数据显示到页面上,并且仅需要修改变量,页面就会跟着实时改变 但如果对于已经熟悉前端的人来说,单单有数据还是不太行,还需要css对数据进行样式的修饰,让页面更加好看 所本篇将记录记录 Class 与 Style 绑定 的学习

    2024年02月19日
    浏览(25)
  • 零基础入门Vue之窥探大法——计算与侦听

    在 上一小节 我介绍了我学习vue入门 插值语法 的过程。 在本篇,我将记录我对vue的 计算属性和侦听器 的学习记录 注:本篇对于”侦听“和”监听“是一个意思 在官网上,可以看到这样一个例子: Vue官方预判到了某些人学了Vue,会在{{}}写复杂的语句,而这些语句会大大降

    2024年01月20日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包