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

目录
  • vue2响应式双向绑定,在对象或数组新增属性页面无响应
    • 问题描述
    • 解决方法
  • vue2实现响应式数据
    • JS中的对象属性
    • 利用Object.defineProperty()进行数据劫持
    • 与标签联动
    • v-model的实现
  • 总结

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>          

虽然这种方法可以解决绑定对象属性丢失问题,但是如果给 每个属性设置 初始值 为 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()

总结

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

(0)

相关推荐

  • 使用Vue.$set()或者Object.assign()修改对象新增响应式属性的方法

    目录 Vue.$set() Vue.$set()源码 Object.assign() 首先建议先读读Vue官方文档深入响应式原理的介绍,对这一块你的理解会加深很多深入响应式原理 vue代码中,只要在data对象里定义的对象,赋值后,任意一个属性值发生变化,视图都会实时变化 比如下面在data定义了obj对象,mounted里赋值后,(也可以在其他地方赋值)只要obj.a或者obj.b的值改变了,视图会跟着变化 data() { return { obj: {} } }, mounted: { t

  • Vue响应式添加、修改数组和对象的值

    有些时候,不得不想添加.修改数组和对象的值,但是直接添加.修改后又失去了getter.setter. 由于 JavaScript 的限制, Vue 不能检测以下变动的数组: 1. 利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue 2. 修改数组的长度时,例如: vm.items.length = newLength 为了避免第一种情况,以下两种方式将达到像 vm.items[indexOfItem] = newValue 的效果, 同时也将触发状

  • vue中数据不响应的问题及解决

    目录 vue数据不响应问题 vue数据响应不及时问题 vue数据不响应问题 将本来要新增的属性提前在data中定义好 我可以提前在userInfo里面定义好,这样就不是新增属性了,就像下面这样 data() {     return {       userInfo: {         name: '子君',         sex: '男',         // 我先提前定义好         officialAccount: ''       }     }   } 直接替换掉userI

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

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

  • JS遍历数组和对象的区别及递归遍历对象、数组、属性的方法详解

    废话不多说了,直奔主题,你,具体代码如下所示: <script> //----------------for用来遍历数组对象-- var i,myArr = [1,2,3]; for (var i = 0; i < myArr.length; i++) { console.log(i+":"+myArr[i]); }; //---------for-in 用来遍历非数组对象 var man ={hands:2,legs:2,heads:1}; //为所有的对象添加cl

  • $.each遍历对象、数组的属性值并进行处理

    通过它,你可以遍历对象.数组的属性值并进行处理. 使用说明 each函数根据参数的类型实现的效果不完全一致: 1.遍历对象(有附加参数) $.each(Object, function(p1, p2) { this; //这里的this指向每次遍历中Object的当前属性值 p1; p2; //访问附加参数 }, ['参数1', '参数2']); 2.遍历数组(有附件参数) $.each(Array, function(p1, p2){ this; //这里的this指向每次遍历中Array的当

  • 关于React中使用window.print()出现页面无响应问题解决记录

    目录 一.问题背景: 二.问题原因: 三.问题解决: 总结: 一.问题背景: window.print()页面打印出现页面无响应 看了网上很多办法都是让用window.location.reload(),对于这个解决方案不置可否,刷新页面当然可以解决这个问题,但是不高级 二.问题原因: 可能操作了document但是并未进行销毁(可能是) 三.问题解决: 消除操作的document 封装一个printFun()方法 //该方法参数 content:需要打印的元素 printFun = (cont

  • Vue2实现组件props双向绑定

    Vue学习笔记-3 前言 Vue 2.x相比较Vue 1.x而言,升级变化除了实现了Virtual-Dom以外,给使用者最大不适就是移除的组件的props的双向绑定功能. 以往在Vue1.x中利用props的twoWay和.sync绑定修饰符就可以实现props的双向绑定功能,但是在Vue2中彻底废弃了此功能,如果需要双向绑定需要自己来实现. Vue2的组件props通信方式 在Vue2中组件的props的数据流动改为了只能单向流动,即只能由组件外(调用组件方)通过组件的DOM属性attribu

  • Vue响应式原理及双向数据绑定示例分析

    目录 前言 响应式原理 双向数据绑定 前言 之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是Vue,所以免不了问到其响应式原理和Vue的双向数据绑定.但是这边面试到的80%的同学会把两者搞混,通常我要是先问响应式原理再问双向数据绑定原理,来面试的同学大都会认为是一回事,那么这里我们就说一下二者的区别. 响应式原理 是Vue的核心特性之一,数据驱动视图,我们修改数据视图随之响应更新,就很优雅~ Vue2.x是借助Object.defineProperty()实现的,而Vue3.x是借助

  • Vue双向数据绑定与响应式原理深入探究

    目录 一.双向数据绑定和数据响应式是相同的吗 二.双向数据绑定的原理 三.数据响应式的原理与实现 一.双向数据绑定和数据响应式是相同的吗 不相同,原因如下: 响应式是指通过数据区驱动DOM视图的变化,是单向的过程. 双向数据绑定就是无论用户更新View还是Model,另一个都能跟着自动更新. 例如:当用户填写表单时,View的状态就被更新了,如果此时可以自动更新Model的状态,那就相当于我们把Model和View做了双向绑定. 双向数据绑定的数据和DOM是一个双向的关系. 响应式是双向绑定的一

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

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

  • 浅谈实现vue2.0响应式的基本思路

    最近看了vue2.0源码关于响应式的实现,以下博文将通过简单的代码还原vue2.0关于响应式的实现思路. 注意,这里只是实现思路的还原,对于里面各种细节的实现,比如说数组里面数据的操作的监听,以及对象嵌套这些细节本实例都不会涉及到,如果想了解更加细节的实现,可以通过阅读源码 observer文件夹以及instance文件夹里面的state文件具体了解. 首先,我们先定义好实现vue对象的结构 class Vue { constructor(options) { this.$options = o

  • 稍微学一下Vue的数据响应式(Vue2及Vue3区别)

    什么是数据响应式 从一开始使用 Vue 时,对于之前的 jq 开发而言,一个很大的区别就是基本不用手动操作 dom,data 中声明的数据状态改变后会自动重新渲染相关的 dom. 换句话说就是 Vue 自己知道哪个数据状态发生了变化及哪里有用到这个数据需要随之修改. 因此实现数据响应式有两个重点问题: 如何知道数据发生了变化? 如何知道数据变化后哪里需要修改? 对于第一个问题,如何知道数据发生了变化,Vue3 之前使用了 ES5 的一个 API Object.defineProperty Vue

随机推荐