关于vue2响应式缺陷的问题

目录
  • vue2响应式缺陷
    • 1.对象新增的属性没有响应式
    • 2.数组的部分操作没有响应式
  • vue2与vue3的响应式原理
    • vue2响应式
    • vue3响应式雏形
    • vue3的响应式相较于vue2的优势

vue2响应式缺陷

响应式 : 数据改变 ==> 视图跟着改变

vue2响应式缺陷

1.对象新增的属性没有响应式

对象,新增属性b,修改b的值,值改变但视图并未更新

解决方案 : 使用vue提供的 api $set(对象,属性名,值) 效果如属性c

2.数组的部分操作没有响应式

数组中有7种操作有响应式

  • array.pop()
  • array.push()
  • array.shift()
  • array.unshift()
  • array.sort()
  • arry.reverse()
  • array.splice()

以上7中API会修改原数组(vue2的内部重写了这7个API)

其他的操作都不会有响应式

示例:

1.修改数组的第一个元素的值

// 直接通过下标来修改,没有响应式
fn1() {
    this.arr[0] = 100
}

通过下标直接赋值,没有响应式

2.修改数组的长度为0

//修改数组的length
fn2 () {
    this.arr.length = 0
}

修改数组的length,没有响应式

如何修改数组的值有响应式

fn1 () {
    // 方法1: 先删除,再添加
    this.arr.splice(0,1,100)
},
fn2 () {
    // 方法2: $set
    this.$set(this.arr, "0", 100)
}

vue2与vue3的响应式原理

给大家分享一下Vue2和Vue3的响应式原理;说到响应式无非就是监听属性的获取,修改及删除...,了解逻辑之后再去实现底层代码岂不是快了许多=_=

vue2响应式

原理:利用defineReactive方法,通过defineProperty对属性进行劫持,数组则是通过重写其方法来进行劫持,每个属性值都拥有自己的dep属性,用来存取所依赖的watch,当数据发生改变时,触发相应的watch去更新数据

代码实现:

const { arrayMethods } = require('./array')

class Observer {
    constructor(value) {
        Object.defineProperty(value, '__ob__', {
            value: this,
            enumerable: false,
            writable: true,
            configurable: true
        })
        if(Array.isArray(value)) {
            value.__proto__ = arrayMethods
            this.observeArray(value)
        } else {
            this.walk(value)
        }
    }

    walk(data) {
        let keys = Object.keys(data)
        for(let i = 0; i < keys.length; i++) {
            const key = keys[i]
            const value = data[key]
            defineReactive(data, key, value)
        }
    }

    observeArray(items) {
        for(let i = 0; i < items.length; i++) {
            observe(items[i])
        }
    }
}

function defineReactive(data, key, value) {
    const childOb = observe(value)

    const dep = new Dep()

    Object.defineProperty(data, key, {
        get() {
            console.log('获取值')
            if (Dep.target) {
                dep.depend()

                if (childOb) {
                    childOb.dep.depend()

                    if (Array.isArray(value)) {
                        dependArray(value)
                    }
                }
            }
            return value
        },
        set(newVal) {
            if (newVal === value) return
            observe(newVal)
            value = newVal
            dep.notify()
        }
    })
}

function observe(value) {
    if (Object.prototype.toString.call(value) === '[object Object]' || Array.isArray(value)) {
        return new Observer(value)
    }
}

function dependArray(value) {
    for(let e, i = 0, l = value.length; i < l; i++) {
        e = value[i]

        e && e.__ob__ && e.__ob__.dep.depend()

        if (Array.isArray(e)) {
            dependArray(e)
        }
    }
}

// array.js
const arrayProto = Array.prototype

const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'reverse',
    'sort'
]

methodsToPatch.forEach(method => {
    arrayMethods[method] = function (...args) {
        const result = arrayProto[method].apply(this, args)

        const ob = this.__ob__

        var inserted

        switch (method) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.slice(2)
            default:
                break;
        }

        if (inserted) ob.observeArray(inserted)

        ob.dep.notify()

        return result
    }
})
 

但是呢,Vue2的响应式还存在一些缺陷:1.对象新增属性,删除属性界面不会更新 2.通过数组下标修改数组内容界面不会更新

原因:1.Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的

2.通过数组下标修改数组不会触发响应,因为尤雨溪用了重写数组的方法来实现数据的响应绑定,当vue遇到push pop shift unshift splice sort reverse 的时候数组才会改变

解决方案:1.对象:对象新增属性无法更新视图,通过Vue.$set(obj,key,value),组件中通过this.$set(obj,key,value)

2.数组:通过数组下标修改数组不会触发视图更新,可以通过Vue.$set(obj,key,value),也可以通过push pop shift unshift splice sort reverse方法来实现响应

Vue3的响应式弥补了Vue2响应式的缺陷,并且还带来了许多优化,下面让我们来了解一下Vue3响应式的基本雏形

vue3响应式雏形

原理:利用了Proxy和Reflect来代替Vue2的Object.defineProperty()方法来重写响应式

这只是基本的代码:

        let person = {
            name: '张三',
            age: 18
        }
        const p = new Proxy(person, {
            get(target, propName) {
                console.log(`我的${propName}值被获取了`);
                return Reflect.get(target, propName)
            },
            set(target, propName, value) {
                console.log(`我的${propName}值被修改了`);
                Reflect.set(target, propName, value)
            },
            deleteProperty(target, propName) {
                console.log(`我的${propName}值被删除了`);
                Reflect.deleteProperty(target, propName)
            }
        })

target指的是整个person,propName指的是person中的某个属性,value指的是新值。

运行结果:

vue3的响应式相较于vue2的优势

用 Proxy 和 Reflect 来代替 vue2 中的 Object.definepeoperty()方法来重写响应式

vue3 中可以监听动态新增的属性

vue3 中可以监听删除的属性

vue3 中可以监听数组的索引和 length 属性

代码的执行效果更快

Proxy 可以直接监听对象而非属性

Proxy 可以直接监听数组的变化

Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的

Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改

Proxy 不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 深度解析 Vue3 的响应式机制

    目录 什么是响应式 响应式原理 定制响应式数据 Vueuse 工具包 什么是响应式 响应式一直都是 Vue 的特色功能之一:与之相比,JavaScript 里面的变量,是没有响应式这个概念的:你在学习 JavaScript 的时候首先被灌输的概念,就是代码是自上而下执行的: 我们看下面的代码,代码在执行后,打印输出的两次 double 的结果也都是 2:即使 我们修改了代码中 count 的值后,double 的值也不会有任何改变 let count = 1 let double = count

  • Vue 如何关掉响应式问题

    目录 Vue如何关掉响应式 例子 v-once Vue响应式的处理过程 Vue如何关掉响应式 大家都知道Vue有双向数据绑定 ,但是很少人知道怎样把它这个功能关掉 比如想要让某个值的改变不改变原有值 使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化. 例子 var obj = {   foo: 'bar' } Object.freeze(obj) new Vue({   el: '#app',   data: obj }) <div id

  • vue视图响应式更新详细介绍

    目录 概述 思路 第一步统一封装更新函数 第二步监听并触发视图更新 引入Dep管家 实现下语法糖v-model 概述 前面两篇文章已经实现了对数据的变化的监听以及模板语法编译初始化,但是当数据变化时,视图还不能够跟随数据实时更新.本文就在之前的基础上介绍下视图响应式更新部分. 思路 统一封装更新函数 待数据发生改变时调用对应的更新函数 这里思考个问题: 在何时注册这个更新函数? 如何找到对应的更新函数? 第一步统一封装更新函数 基于上篇文章compile的部分,将数据初始化的部分统一封装起来.

  • vue中响应式布局如何将字体大小改成自适应

    目录 响应式布局将字体大小改成自适应 vue文字大小自适应问题 响应式布局将字体大小改成自适应 1.在app.vue的生命周期函数中添加一段代码来设置页面的rem mounted: function() {     // 页面开始加载时修改font-size     var html = document.getElementsByTagName("html")[0];     var oWidth = document.body.clientWidth || document.doc

  • vue如何使用媒体查询实现响应式

    目录 使用媒体查询实现响应式的两种方式 前提 1.在每个组件中为其使用媒体查询 2.写n套页面,在使用这些页面的组件中进行一次媒体查询 vue中的媒体查询 语法 使用媒体查询实现响应式的两种方式 前提 依赖: sass,sass-loader 1.在每个组件中为其使用媒体查询 这种方法的有点是减少了重写不同终端同一组件的工作量,缺点是每个组件都要使用媒体查询,当一套页面组件不同时,需要进行组件的显示与隐藏(display:none!important),在不同终端区别不大的情况下建议使用这种方法

  • 实现一个VUE响应式属性装饰器详析

    目录 前言 不使用任何的响应Api 使用 reactive 实现 使用 ref 实现 使用装饰器实现 实现Reactive装饰器 实现Watch装饰器 总结 前言 使用面向对象的开发思想难免会用到类,既然有了类,那就应该有实例,然而我们使用类的时候可能需要实例中的某个属性是vue的响应属性,也可能里面的某个方法也可以被vue的watch监听.我就开始琢磨如何通过 Composition API 来实现这个类属性装饰器 不使用任何的响应Api // TestReactive.ts export c

  • 关于vue2响应式缺陷的问题

    目录 vue2响应式缺陷 1.对象新增的属性没有响应式 2.数组的部分操作没有响应式 vue2与vue3的响应式原理 vue2响应式 vue3响应式雏形 vue3的响应式相较于vue2的优势 vue2响应式缺陷 响应式 : 数据改变 ==> 视图跟着改变 vue2响应式缺陷 1.对象新增的属性没有响应式 对象,新增属性b,修改b的值,值改变但视图并未更新 解决方案 : 使用vue提供的 api $set(对象,属性名,值) 效果如属性c 2.数组的部分操作没有响应式 数组中有7种操作有响应式 a

  • Vue2响应式系统之set和delete

    目录 1.数组集 2.数组 del 3.对象 set 4.对象 del 5.总结 1.数组集 import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { list: [1, 2], }; observe(data); const updateComponent = () => { console.log(data.list); }; new Watc

  • Vue2 响应式系统之数组

    目录 1.场景 2.场景 2 3.方案 3.收集依赖代码实现 4.通知依赖代码实现 5.测试 6.总结 本文接Vue2响应式系统.Vue2 响应式系统之分支切换 ,响应式系统之嵌套.响应式系统之深度响应还没有看过的小伙伴需要看一下. 1.场景 import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { list: ["hello"],

  • Vue2 响应式系统之深度响应

    目录 1.场景 2.方案 3.场景2 4.总结 1.场景 import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: { innerText: { childText: "hello", }, }, }; observe(data); const updateComponent = () => { console.lo

  • Vue2响应式系统之嵌套

    目录 1.场景 2.执行过程 3.修复 4.测试 5.总结 1.场景 在 开发中肯定存在组件嵌套组件的情况,类似于下边的样子.Vue <!-- parent-component --> <div> <my-component :text="inner"></my-component> {{ text }} <div> <!-- my-component--> <div>{{ text }}</di

  • Vue2 响应式系统之分支切换

    目录 场景 observer(data) new Watcher(updateComponent) data.ok = false data.text = "hello, liang" 问题 去重 重置 测试 总结 场景 我们考虑一下下边的代码会输出什么. import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: &quo

  • Vue2响应式系统介绍

    目录 一.响应式系统要干什么 二.响应式数据 三.保存当前正在执行的函数 四.响应式数据 五.Observer 对象 六.测试 七.总结 前言: 目前工作中大概有 的需求是在用 的技术栈,所谓知其然更要知其所以然,为了更好的使用 .更快的排查问题,最近学习了源码相关的一些知识,虽然网上总结 的很多很多了,不少自己一个,但也不多自己一个,欢迎一起讨论学习,发现问题欢迎指出.40%Vue2VueVue 一.响应式系统要干什么 回到最简单的代码: data = { text: 'hello, worl

  • Vue2 响应式系统之异步队列

    目录 场景 解决方案 代码实现 执行结果 总结 试想一下如果这里的 console.log 是渲染页面,那改变一次值就刷新一下页面,会造成严重的性能问题,页面也会不停的改变. 场景 import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { a: 1, b: 2, c: 3, }; observe(data); const updateComponent

  • vue2响应式的缺点影响

    前言: 响应式:数据改变-->视图跟着变 对象新增的属性没有响应式 数组的部分操作没有响应式 push(),pop(),shift(),unshift(),splice(),sort(),reverse() 以上7中API会修改原数组(vue2的内部重写了这7个API) 其他的操作都不会有响应式 实际简单操作一波: <template> <div> <span v-for="(item,index) in arr " :key="index

  • vue2之响应式双向绑定,在对象或数组新增属性页面无响应的情况

    目录 vue2响应式双向绑定,在对象或数组新增属性页面无响应 问题描述 解决方法 vue2实现响应式数据 JS中的对象属性 利用Object.defineProperty()进行数据劫持 与标签联动 v-model的实现 总结 vue2响应式双向绑定,在对象或数组新增属性页面无响应 问题描述 vue2 中可以将数据与视图进行绑定,修改 data 对象的属性值将引起对应视图的改变. Vue2的数据视图绑定是通过JS特性这一语法实现,其使用中存在数据属性丢失的这 一 bug,主要针对 对象或数组 属

随机推荐