zoukankan      html  css  js  c++  java
  • Vue $set 分析

    提问

    Vue 2.x 里劫持对象仍用 Object.defineProperty() 方法,受此方法的限制,Vue 无法检测到直接数组更改(mobx 旧版本也有过这毛病,他对数组0到999项的内容都现实地 Object.defineProperty 监听了),和对象属性的添加或删除。

    举个例子,运行下面代码,能看到点击 changeArr 按钮,页面没有变化,而点击 setArr 按钮,页面上显示的数据就变了。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Form Demo</title>
    </head>
    <body>
        <div id="app">
            <div>{{arr[0]}}</div>
            <button @click="changeArr">changeArr</button>
            <button @click="setArr">setArr</button>
        </div>
    
        <!-- Vue.js v2.6.11 -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    arr: [1, 2, 3, 4]
                },
                methods: {
                    changeArr() {
                        this.arr[0] = 11;
                    },
                    setArr() {
                        Vue.set(this.arr, 0, 12);
                    }
                }
            })
            console.log(app);
        </script>
    </body>
    </html>
    

    那么 Vue 内部是如何解决对象新增属性不能响应的问题的呢?

    结论

    • 如果目标是数组,使用 vue 实现的变异方法 splice 实现响应式
    • 如果目标是对象,判断属性存在,即为响应式,直接赋值
    • 如果 target 本身就不是响应式,直接赋值
    • 如果属性不是响应式,则调用 defineReactive 方法进行响应式处理,并通知被观察者 ob.dep.notify()

    附录

    Vue 2.6.2 源码

    /**
     * Set a property on an object. Adds the new property and
     * triggers change notification if the property doesn't
     * already exist.
     */
    export function set (target: Array<any> | Object, key: any, val: any): any {
      if (Array.isArray(target) && typeof key === 'number') {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, val)
        return val
      }
      /*如果是一个对象,并且已经存在了这个key则直接返回*/
      if (hasOwn(target, key)) {
        target[key] = val
        return val
      }
      /*获得target的Oberver实例*/
      const ob = (target : any).__ob__
      /*
        _isVue 一个防止vm实例自身被观察的标志位 ,_isVue为true则代表vm实例,也就是this
        vmCount判断是否为根节点,存在则代表是data的根节点,Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property)
      */
      if (target._isVue || (ob && ob.vmCount)) {
        /*  
          Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property)。
          https://cn.vuejs.org/v2/guide/reactivity.html#变化检测问题
        */
        process.env.NODE_ENV !== 'production' && warn(
          'Avoid adding reactive properties to a Vue instance or its root $data ' +
          'at runtime - declare it upfront in the data option.'
        )
        return val
      }
      if (!ob) {
        target[key] = val
        return val
      }
      /*为对象defineProperty上在变化时通知的属性*/
      defineReactive(ob.value, key, val)
      ob.dep.notify()
      return val
    }
    
  • 相关阅读:
    批量管理增量日志(seek、tell)
    字符串和编码
    5.activiti--完成任务
    4.activiti--代理任务Claiming the task
    3.activiti--待办任务
    2.activiti-启动流程实例
    1.activiti-流程图
    html 各种高度
    redis-过期时间、访问限制与缓存
    spring mvc controller 接收参数
  • 原文地址:https://www.cnblogs.com/everlose/p/12564822.html
Copyright © 2011-2022 走看看