温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》
  • 首页 > 
  • 教程 > 
  • 开发技术 > 
  • vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决

vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决

发布时间:2023-04-20 16:23:30 来源:亿速云 阅读:124 作者:iii 栏目:开发技术

本文小编为大家详细介绍“vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

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

    问题描述

    vue2 中可以将数据与视图进行绑定,修改 data 对象的属性值将引起对应视图的改变。

    Vue2的数据视图绑定是通过JS特性这一语法实现,其使用中存在数据属性丢失的这 一 bug,主要针对 对象或数组 属性丢失。

    使用 antv a-select (下拉框)组件,使用 v-mode 绑定对象 的值,但是 对象之前是没有赋值的,是一个 空对象 ,这就导致了 页面视图不刷新 ,但是 属性值有变化 ,这可以说是 vue2 的一个缺陷。

    // 空对象
    
    queryParam: {
    
          },

    解决方法

    方法一 (设置初始值)

    给 绑定的对象 赋初始值 null 或者 ' ' 

      queryParam: {
            approveStatus : null ,    
          },

     如果是普通 输入框 input ,这样的方法没什么问题,但是 我的页面使用 a-select 下拉框,默认有提示消息( placeholder),如果 赋初始值 为空 ,下拉框会填充空白内容 ,覆盖之前的提示消息 ,这样的页面 会比较不美观且不友好

      <a-select  v-model="queryParam.approveStatus" placeholder="审核状态" :allowClear="true">
            <a-select-option v-for="status of videoApproveStatus" :key="status.id">
                     {{  status.text }}
            </a-select-option>
     </a-select>
                        
      <a-select  v-model="queryParam.videotypeid" placeholder="视频类型" :allowClear="true">
             <a-select-option v-for="d in videotype" :key="d.myid">
                     {{ d.name }}
              </a-select-option>
       </a-select>

    vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决

    虽然这种方法可以解决绑定对象属性丢失问题,但是如果给 每个属性设置 初始值 为 null,那么所有的下拉框都是空白 , 可以看到 下拉框 赋初始值 为 null ,页面的效果非常不友好 ,没有提示信息 ,所有这种方法不推荐

    方法二 (创建一个新的对象,替换原对象)

    这种方法可以用于需要添加多个新属性,再把原对象与新属性合并到新对象中

    Object.assign(目标对象,原对象, 新属性)

    this.queryParam = Object.assign({}, this.queryParam, obj)

    我这里是 利用 a-select 下拉框 自带的 下拉框 改变方法 ,@change ,该方法有两个参数 value 和 option ,value 代表你改变的值 ,option (我也不太明白 ,反正里面东西很多),然后 我利用 这个 @change 方法 和  Object.assign(目标对象,原对象, 新属性), 解决了状态丢失问题 ,大家可以 参考下面的代码 ,根据自己的实际情况进行调整 (每个人的情况都不一样)

     <a-select @change="handleChange"  v-model="queryParam.approveStatus" placeholder="审核状态" :allowClear="true">
           <a-select-option v-for="status of videoApproveStatus" :key="status.id">
                   {{  status.text }}
           </a-select-option>
     </a-select>
     handleChange(value,option) { 
          if(option) {
            //  解决双向绑定状态丢失
              this.queryParam = Object.assign({}, this.queryParam, option.context.queryParam)
          } else {
            return 
          } 
        },

    vue2实现响应式数据

    JS中的对象属性

    JS的对象有两种属性进行描述 分别是数据属性和访问器属性。

    数据属性有四个值:

    • [[Configurable]] 表示是否可以删除定义,是否可以修改属性,默认为true

    • [[Enumberable]] 是否可以迭代

    • [[Writable]] 是否可以被修改

    • [[Value]] 对象具体的值

    而访问器属性也有四个值:

    • [[Configurable]] 表示是否可以删除定义,是否可以修改属性,默认为true

    • [[Enumberable]] 是否可以迭代

    • [[Get]] 获取函数,读取属性值时使用

    • [[Set]] 设置函数,写入属性时调用

    那么如何实现数据响应呢?

    利用Object.defineProperty()进行数据劫持

    实现响应式的前提是可以捕捉到到数据的更改,获取数据同理,这就需要利用JS对象的访问器属性,而更改这些属性 就要用到JS中的一个方法 Object.defineProperty() 上述的属性在更改时,哪怕更改一个属性,所有属性都会变为默认值。

    具体使用方法如下:

    let obj = {name:'Ton', age: 19}
    _value = obj.name
    Object.defineProperty(obj, name, {
        get(){
            return _value
        },
        set(newValue){
           _value = newValue
        }
    })

    方法的参数分别是 Object.defineProperty(对象名, 属性名, {执行器})

    而get 函数在读取该对象属性时调用,返回的值为读取的值; set 函数会在设置新值时调用 传入的newValue为新值。

    在Vue中 会用data一个对象包裹所有的值,因此可以用遍历的方法给每个属性加上该方法。

    将该逻辑封装到一个函数中:

    let data = {
        name: 'Ton',
        age: 19,
        salary: '10k'
    }
    
    Object.keys(data).forEach( key => { 
        observe(data, key, data[key])
    })
    //形成闭包 内部的变量不会消失
    function observe(obj, key, value){
        Object.defineProperty(obj, key, {
            get(){
                return value
            },
            set(newValue){
                value = newValue
            }
        })
    }

    这样data中的所有变量都会被绑定,但如果嵌套对象或数组,内部的对象不会被检测到。(可以用vue提供的 $set和 $delate 处理或 使用内部重写的数组方法)

    与标签联动

    与标签联动的方法有许多,最简单的是使用id 来绑定,而Vue中是使用指令的方式。

    过程主要分两步:

    • 获取dom

    • 将数据放上去

    ...
    <div id="app">
        <div v-test="name" class="box"></div>
        <div v-test="age" class="box"></div>
    </div>
    ...
    function compile(){
        let app = document.getElementById('app')
        //获取所有的子节点 值为3的是text节点 1为子标签节点
        app.childNodes.forEach( node => {
            if(node.nodeType === 1){
                //遍历该节点的属性 找出 v-text 
                //node.attributes是个类数组对象, 先转化为数组
                Array.from(node.attributes).forEach( key => {
                    //解构对象 nodeName 找到属性
                    let { nodeName, nodeValue } = key 
                     //如果存在 则修改值(关键步骤)
                    if(nodeName === 'v-test'){
                        node.innerText = data[nodeValue]
                       }
                })
            }
        })
    }
    compile()
    ...

    此时可以获取节点,并赋值数据,与数据劫持联动,最终结果如下:

    let data = { name: 'Ton', age: 19}
    //劫持数据
    Object.keys(data).forEach(key => {
        observe(data, key, data[key])
    })
    //形成闭包 内部的变量不会消失
    function observe(obj, key, value) {
        Object.defineProperty(obj, key, {
            get() {
                return value 
            },
            set(newValue) {
                value = newValue
                compile()
            }
        })
    }
    //获取元素 将数据放入
    function compile(){
        let app = document.getElementById('app')
        //获取所有的子节点 包括很多节点 3 为text 节点 1为子标签节点
        app.childNodes.forEach( node => {
            if(node.nodeType === 1){
                //遍历该节点的属性 找出 v-text 
                //node.attributes是个类数组对象, 先转化为数组
                let result = Array.from(node.attributes).filter( key => {
                    //结构属性的 nodeName 找到属性
                    let { nodeName } = key 
                    return nodeName === 'v-test'
                })
                if(result){
                    node.innerText = data[result[0].nodeValue]
                }
            }
        })
    
    }
    compile()

    每次修改也会引起DOM元素的修改,实现响应式。

    v-model的实现

    同v-test(v-on) 的不同,v-model要实现双向绑定,即 input框输入的值也需要传回data。实现的逻辑前面是相同的,都需要获取元素,但需要新增将input输入框的内容,传回。

    let data = { name: 'Ton', age: 19}
    //劫持数据
    Object.keys(data).forEach(key => {
        observe(data, key, data[key])
    })
    //形成闭包 内部的变量不会消失
    function observe(obj, key, value) {
        Object.defineProperty(obj, key, {
            get() {
                return value 
            },
            set(newValue) {
                value = newValue
                compile()
            }
        })
    }
    //获取元素 将数据放入
    function compile(){
        let app = document.getElementById('app')
        //获取所有的子节点 包括很多节点 3 为text 节点 1为子标签节点
        app.childNodes.forEach( node => {
            if(node.nodeType === 1){
                //遍历该节点的属性 找出 v-text 
                //node.attributes是个类数组对象, 先转化为数组
                let result = Array.from(node.attributes).filter( key => {
                    //结构属性的 nodeName 找到属性
                    let { nodeName } = key 
                    return nodeName === 'v-model'
                })
                if(result){
                    node.value = data[result[0].nodeValue]
                    addEventLisener('input', e => {
                            data[result[0].nodevalue] = e.target.value 
                        }
                    )
                }
            }
        })
    
    }
    compile()

    但目前的方法并不完美,需要添加一个防抖函数

    let data = { name: 'Ton', age: 19}
    //劫持数据
    Object.keys(data).forEach(key => {
        observe(data, key, data[key])
    })
    //形成闭包 内部的变量不会消失
    function observe(obj, key, value) {
        Object.defineProperty(obj, key, {
            get() {
                return value 
            },
            set(newValue) {
                value = newValue
                compile()
            }
        })
    }
    //获取元素 将数据放入
    function compile(){
        let app = document.getElementById('app')
        //获取所有的子节点 包括很多节点 3 为text 节点 1为子标签节点
        app.childNodes.forEach( node => {
            if(node.nodeType === 1){
                //遍历该节点的属性 找出 v-text 
                //node.attributes是个类数组对象, 先转化为数组
                let result = Array.from(node.attributes).filter( key => {
                    //结构属性的 nodeName 找到属性
                    let { nodeName } = key 
                    return nodeName === 'v-model'
                })
                if(result){
                    node.value = data[result[0].nodeValue]
                    addEventLisener('input', debounce(handel, result[0].nodeValue)
                }
            }
        })
        function debounce(fn, key, timer = 1000){
            let t = null
            return function(){
                if(t) { clearTimeout(t) }
                t= setTimeOut( _ => {
                    t = null
                    fn.call(this, key, arguments)
                },timer)
            }
        }
        function handel(key, event){
            data[key] = event.target.value
        }
    }
    compile()

    读到这里,这篇“vue2响应式双向绑定在对象或数组新增属性页面无响应怎么解决”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    vue
    AI