zoukankan      html  css  js  c++  java
  • 【每日一题】【vue2源码学习】vue如何检测数组的变化

    数组可以用defineProperty进行监听。但是考虑性能原因,不能数组一百万项每一项都循环监听(那样性能太差了)。所以没有使用Ojbect.defineProperty对数组每一项进行拦截,而是选择劫持数组原型上的个别方法并重写。

    具体重写的有:

    pushpopshiftunshiftsortreversesplice (这七个都是会改变原数组的)

    另外要注意的是:

    不是直接粗暴重写了Array.prototype上的push等方法,而是通过原型链继承与函数劫持进行的移花接木。并且只监听调用了defineReactive函数时传进来的数组。

    具体实现思路:

    以push为例,而是利用Object.create(Array.prototype)生成新的数组对象,该对象的__proto__指向Array.prototype。并在对象身上创建push等函数,利用函数劫持,在函数内部Array.prototype.push.call调用原有push方法,并执行自己劫持的代码(如视图更新)。最后将需要绑定的数组的__proto__由指向Array.prototype改向指成拥有重写方法的新数组对象。具体看下边源码仿写,真实Array.prototype里的祖宗级别push等方法没有动。

    思考:

    为啥不重写map等也是修改原数组的方法呢?

    特别注意:

    在Vue中修改数组的索引和长度,是无法被监控到并做响应式视图更新的。需要通过以上7种变异方法修改数组才会触发数组对应watcher进行更新。
    数组中如果是对象数据类型的也会进行递归劫持。
    如果情节需要,通过索引来修改数组里的内容。可以通过Vue.$set()方法来进行处理,或者使用splice方法实现。(其实$set内部的核心也是splice方法)

    原理mock:

    vue【数组】响应式数据原理mock

    let state = [1,2,3]; //待监听的数据
    
    // 1、响应式数据-函数劫持实现数组原型方法重写
    let OriginalArray = Array.prototype; // 并不是直接改写原型上的方法。而是给当前待监听的数组原型链上加了push等方法劫持了Array原型的push方法。
    let arrayMethods = Object.create(OriginalArray) // 创建一个新对象(对象or数组由第一个参数决定),带着指定的原型对象(Array.prototype)
    console.log(
      arrayMethods,
       // 原型修改
      arrayMethods.__proto__ === OriginalArray,
      arrayMethods.__proto__ === Array.prototype
    )
    function defineReactive(obj) {
      // 【函数劫持】改写这个新对象身上的push、splice等数组方法
      arrayMethods.push = function(...args){
        // 并还是调用原生的push方法
        OriginalArray.push.apply(this, args) // 或者用call(this, ...args)
        // 然后这里边做自己的事情,比如视图更新(具体源码怎么更新的视图?)
        render()
      }
      // 
      obj.__proto__ = arrayMethods // 修改传进来的、被监听的数组的原型链,链接数组与被重写的方法。原本__proto__指向Array.prototype,现在中间给他包了一层,指向我们重写的原型方法。并在重写的原型方法里再调用Array.prototype的同名原型方法。
    }
    defineReactive(state);
    // 操作dom
    function render() {
      app.innerHTML = state;
    }
    render()
    // 更改数据,观察dom修改
    btn.onclick = () => {
      state.push(state[state.length - 1] + 1)
    }
    

    源码位置:

    github:src/core/observer/array.js:8

    本文使用 mdnice 排版

  • 相关阅读:
    构建gitlab+Jenkins+harbor+kubernetes的DevOps持续集成持续部署环境
    iptables详解之filter
    运维自动化神器ansible之user模块
    运维自动化神器ansible之group模块
    利用sed将xml报文转换为分隔符形式报文
    你的火狐很慢吗?
    完全卸载Oracle11g
    MyEclipse注册代码
    css 空心字
    TEST 1 READING AND WRITING -- PART ONE
  • 原文地址:https://www.cnblogs.com/padding1015/p/13744239.html
Copyright © 2011-2022 走看看