• 说一下v-model的理解双向绑定 vue响应式原理


    1.vue响应式原理 和proxy的对比

    参考: 1. vue2 里的响应式其实有点像是一个半完全体,对于对象上新增的属性无能为力,对于数组则需要拦截他的原型方法来实现响应式. 2. 这个时候Vue提供了一个api :this.$set来实现新增的属性也拥有响应式的效果.
    问题:但是很多时候需要小心翼翼的去判断到底什么情况下需要用 $set,什么时候可以直接触发响应式。
    在 Vue3 中,这些都将成为过去。proxy 到底会给 Vue3 带来怎么样的便利。
    例子1.

    let vm = new Vue({
     data() {
     return {
     a: 1
     }}})
    // ❌ oops,没反应!
    vm.b = 2 
    let vm = new Vue({
     data() {
     return {
     a: 1
     }
     },
     watch: {
     b() {
     console.log('change !!')
     }}})
     
    // ❌ oops,没反应!
    vm.b = 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.3 vue2 响应式原理

    例1.利用input实现数据双向绑定

     理解:
        发布者
        订阅者
        观察者
     <!-- 发布者 -->
      <input type="text">
      <!-- 订阅者 -->
      <div class="demo"></div>
    
      <!-- 观察者 Object.defineProperty(目标对象,目标对象中的属性,{6个属性:set,get,value,枚举,可选值,是否可写}) -->
      <script>
        /* 观察者 */
        // let target = {} //目标对象
        // Object.defineProperty(target, 'msg', {
        //   get () { },
        //   set (value) {
        //     console.log(value, 5);
        //     demo.innerHTML = value
        //     ipt.value = value
        //   }
        // })
    
        // let ipt = document.querySelector('input')
        // let demo = document.querySelector('.demo')
        // ipt.addEventListener('input', (e) => {
        //   console.log(e.target.value, 99);
        //   target.msg = e.target.value
        // })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    双向绑定

    1.什么是双向绑定

    单向绑定: javascript代码更新model时,view就会自动跟新双向绑定
    在单向绑定的基础上,用户更新了View,Model的数据也自动被更新了

    2.双向绑定的原理

    Vue 是数据双向绑定的框架,双向绑定由三个重要部分构成

    a.双向绑定由三个重要部分构成

    数据层(Model):应用的数据及业务逻辑
    视图层(View):应用的展示效果,各类UI组件
    业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来

    所以MVVM这里的控制层的核心功能便是 “数据双向绑定”

    b.理解:业务逻辑层(ViewModel)
    数据变化后更新视图
    视图更新后更新数据
    主要组成部分:
    监听器(Observer):对所有数据的属性进行监听
    解析器:(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数

    实现原理:
    对象类型:通过Objeact.defineProperty()对属性的 setter getter在数据变动时放送给订阅者,触发相应的监听回调读取、修改进行拦截(数据劫持)
    数组类型:通过重写更新数组的一系类方法拦截(对数组的变更方法进行包裹)
    存在问题:
    a.当对象中属性过多时Objeact.dfineProperty()需求对每一个属性进行遍历实现响应式,效率不高.
    b.新增属性,删除属性,界面不会刷新;
    c.只有configurable为true时候,该属性才能从对应的对象上被删除,单元数据不会响应式删除.
    d.直接通过下标修改数组,界面不会自动更新.

    **实现步骤:
    a.第一步需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
    b.第二步 compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
    c.第三步 Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    1、在自身实例化时往属性订阅器(dep)里面添加自己
    2、自身必须有一个update()方法
    3、待属性变动dep.notice()通知时,能调用自身的 update() 方法,并触发Compile中绑定的回调,则功成身退。
    d.第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
    **
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    例1:
    在源数据上 添加数值 属性

    <script>
            // 源数据
            let person = {
                name: '张三',
                age: 18
            }
            Proxy和Reflect是window上内置的函数
            let p = new Proxy(person, {
                get(target, propName) {
                    console.log(`有人读取了p身上的${propName}属性`)
                    return Reflect.get(target, propName)
                },
                set(target, propName, value) {
                    console.log(`有人新增/修改了p身上的${propName}属性,我要去更新界面了!`)
                    Reflect.set(target, propName, value)
                },
                deleteProperty(target, propName) {
                    console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
                    return Reflect.deleteProperty(target, prop)
                }
            })
        </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.2 vue3 proxy响应式原理

    实现原理:
    通过Proxy(代理):拦截对象中任意属性变化,包括:属性值的读写,属性的添加,属性的删除等.
    通过Reflect(反射):对源对象的属性进行操作.
    MDN文档中描述的Proxy与Reflect:

    Proxy   Reflect
    
    • 1

    例2:

     /* Proxy */
        let proxy = new Proxy({}, {
          // set (目标对象,key,value) {
          set (target, key, value) {
            console.log(target, key, value, 888);
            demo.innerHTML = value
            ipt.value = value
          }, // setter
          get () { } // getter  
        })
    
        let ipt = document.querySelector('input')
        let demo = document.querySelector('.demo')
        ipt.addEventListener('input', (e) => {
          console.log(e.target.value, 99);
          proxy.msg = e.target.value
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Proxy:

    //语法:let p = new Proxy(target, handler)
    target:要使用 Proxy 包装的目标对象,此处为person源数据;
    handler:一个对象。可以只传一个空对象,也能实现增删改查操作let p = new Proxy(person, {});可以向上述代码一样传入含有的get set deleteproperty函数来拦截对象中任意属性的变化;
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    set函数 get函数 deleteProperty函数 用法

    //get函数:
    
    get(target, propName) {
        // target:目标对象,也就是person源数据;
        // propName:被获取的属性名;
        console.log(`target, propName`, target, propName)
        return target[propName]
    },
     //set函数:
    
    // set函数对新增或修改一个属性都可以拦截到
    set(target, propName, value) {
       // value:新属性值
       console.log(`target, propName,value`, target, propName,value)
       target[propName] = value
    }
     
     //deleteProperty函数:
    
    deleteProperty(target, propName) {
      console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
      return delete target[propName]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Reflect:

    Reflect函数身上的一些方法与Object相同,比如:

    Reflect.get(target,propName)相当于get函数中直接target[propName];
    Reflect.set(target,propName,value)相当于set函数中target[propName] = value;
    Reflect.deleteProperty(target,propName)相当于deleteProperty函数中的delete target[propName]
    当然,Reflect函数身上还有Reflect.defineProperty方法,与Object.defineProperty有所不同:
    
    • 1
    • 2
    • 3
    • 4

    Object.defineProperty对同一个对象同一个属性重复操作时,系统会报错代码运行不下去,但Reflect.defineProperty不会报错只会运行第一条结果并继续执行后边代码;

    Object.defineProperty:

    let obj = { a: 1, b: 2 }
            Object.defineProperty(obj, 'c', {
                get() {
                    return 3
                }
            })
            Object.defineProperty(obj, 'c', {
                get() {
                    return 4
                }
            })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Reflect.defineProperty:

    let obj = { a: 1, b: 2 }
            const x1 = Reflect.defineProperty(obj, 'c', {
                get() {
                    return 3
                }
            })
            console.log('x1',x1)
     
            const x2 = Reflect.defineProperty(obj, 'c', {
                get() {
                    return 4
                }
            })
            console.log('obj')  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注:一般组件封装用Reflect函数比较多,宽容度好,组件更健壮

  • 相关阅读:
    Spring JDBC
    初识集合框架 -Java
    Python3数据科学包系列(一):数据分析实战
    SVM & FC+Softmax 分类
    AM@连续函数相关概念和运算性质
    重温redis和mysql的数据一致性问题
    上海理工大学第二届“联想杯”全国程序设计邀请赛
    Java8新特性stream和parallelStream有什么区别
    ORM数据库查询操作
    ArcGIS如何处理并加载Excel中坐标数据?
  • 原文地址:https://blog.csdn.net/qq_43944285/article/details/125141220