【Vue】全系列Vue教程-数据监视
  oODzP4HWeioA 2023年11月02日 37 0

hello,我是小索奇哈,精心制作的Vue系列持续发放,涵盖大量的经验和示例,由浅入深进行讲解。 本章给大家讲解的是数据监视,前面的章节已经更新完毕,后面的章节持续输出,有任何问题都可以留言或私信哈,一起加油~

数据监视

Vue实现数据监测的核心是通过defineProperty()劫持属性的getter& setter,当我们获取data数据时,底层都是通过调用getter & setter来实现的,在属性读取或修改时可以进行额外操作这实际上将数据对象“代理”了一层,形成所谓的“响应式数据”

具体一点来说哈,在初始化组件时,Vue会遍历data对象的所有属性,使用Object.defineProperty将它们转换成getter/setter这种代理允许Vue追踪依赖,在属性被访问和修改时通知变化(VM中的data就数据代理自_data)

例如我们定义了一个名为message的data属性,Vue会将其转化为

Object.defineProperty(data, 'message', {
  get () {
    // ...进行依赖收集 
  },
  set (newValue) {
    // ...触发更新
  }
})

这样在读取或修改message时,就可以触发getter和setter,从而进行依赖收集和更新触发,把这些直接叫做数据劫持即可

数据劫持就是Vue实现响应式的基石,它可以检测数据变化并触发回调来完成视图更新,使开发者只需要关注数据本身而不需要手动操作DOM

这里为什么提到数据劫持呢?

一句话形容:数据劫持是手段,数据监视是目的(没有数据劫持,就无法精确监视数据变化)

具体往下看,这里划重点,结合下面,不懂再爬上来一下哈

模拟一个小响应系统的工作方式:

<div id="app">
  <!-- 视图渲染 -->
</div>

<script>
// 数据对象  
const data = {
  name: 'John',
  age: 20
};

function reactive(obj) {
// 汇总所有obj形成一个数组并进行遍历
  // 核心响应式转换代码
  Object.keys(obj).forEach(key => {
    
    let value = obj[key];

    const dep = new Set();

    Object.defineProperty(obj, key, {

      get() {
        // 收集依赖
        dep.add(updateView); 
        return value;
      },

      set(newVal) {
        // 更新值
        value = newVal;
        
        // 触发依赖更新
        dep.forEach(fn => fn());
      }

    });

  });

  return obj;
}

// 数据响应式处理
const reactiveData = reactive(data);

// 视图更新函数 
function updateView() {
  // ...渲染视图
}

// 初始化
updateView(); 

// 数据改变时触发视图更新
reactiveData.name = "Bob";
</script>

这个响应式系统,可以自动更新视图

当获取属性值时,收集订阅者;当设置属性值时,通知订阅者更新视图

没有get和set就不会有响应式数据,也不会显示到页面上!(调试页面中的带括号的就证明有,直接定义的没有get、set也不会响应到页面上)

拓展

响应式转换可能不理解?

// 响应式转换
const reactiveData = reactive(data);

reactive函数是用来把普通对象转换成响应式对象的,它接受一个普通对象作为参数,对这个对象的所有属性进行响应式处理,然后返回这个响应式对象

所以这里

const data = {
  name: 'John',
  age: 20
};

是定义一个普通对象data

调用:

const reactiveData = reactive(data);

reactive函数会遍历data对象的所有属性,使用Object.defineProperty()把这些属性转换成getter/setter的形式

这样responsiveData就变成一个响应式对象了,它和data对象具有相同的属性,但多了响应式的功能之后我们使用responsiveData来代替data,就可以实现视图的自动更新

简单来说reactive(data)这一行的作用就是把一个普通对象data转换成响应式对象reactiveData,得到一个可以实现数据监听与视图更新的响应式数据对象

需要注意的是,由于是直接监测属性,所以对象上的层级结构过深时,内部属性的变化不会触发响应这需要完整替换对象或手动设置新值

data: {
  user: {
    name: 'John',
    friend: {
      name: 'Chris' 
    }
  }
}

如果直接这样改friend.name,Vue监测不到(直接修改深层属性无法被监测到,是因为Vue的响应式通过劫持属性的getter和setter来实现的但它只能劫持对象的第一层属性~)

this.user.friend.name = '小索奇'

重要:必须整个替换user对象,或者用Vue.set | this.$set 改friend.name,才会是响应式的数据,才会生效!

// 非响应式
this.user.name = '小索奇'
// 响应式
// Vue.set(target, key, value) | this.$set(target, key, value)
this.$set(this.user.friend, 'name', '小索奇')

数组也一样,记住不能够通过数组的索引值更改(数组本身不具备get、set方法)

this.list[0] = 'A'
// 如果数组下有对象属性,可以更改
this.list[0].name = '小索奇'

要直接调用Vue的全局API来改变数组,如用Vue.set改索引,或者用数组自身push、pop、shift、unshift、splice、sort、reverse、之类的API

  • 这些方法都被Vue做了包装,所以可以进行视图更新

上面的就可以用splice替换

this.list.splice(start, deleteCount, item1)

异步函数里改数据,页面也不会立刻更新,只有等异步函数完了,页面才显示新数据

setTimeout(() => {
  this.message = 'hello'
}, 1000)

所以用定时器、ajax之类的改数据,页面会晚一点才刷新

这就是Vue数据监测需要注意的几个点概括来说就是:

  • 嵌套深的数据要用Vue.set或者整个替换
  • 数组索引要用Vue.set或数组方法改
  • 异步函数里改数据,页面更新会 delay 一下

拓展

Vue.set(target,key,val)

// vm.$set()
Vue.set(vm._data.obj,'country','China')
// 为什么等同于下面的
Vue.set(vm.obj,'country','China')

这是因为数据代理-review

Vue.set 方法可以向响应式对象中添加一个属性并确保这个新属性同样是响应式的

Vue 在初始化时,会将 data 对象代理到 Vue 实例上,所以 vm.obj 等同于访问 vm._data.obj

也就是说,在组件实例 VM 中,访问 vm.obj 等同于访问 vm._data.obj,因为它会被代理到 _data 中的原始数据对象上

这是通过 Vue 的 proxy 方法实现的,大致如下:

function proxy(vm) {
  Object.keys(vm._data).forEach(key => {
    Object.defineProperty(vm, key, {
      get() { 
        return vm._data[key];
      }
    });
  });
}

所以 Vue.set 方法既可以接受原始的 _data 对象,也可以接受代理后的组件实例对象,效果是相同的

它们都指向同一个原始数据对象

注意

  • target不允许是VM实例,也不允许是直接根对象,比如data

【Vue】全系列Vue教程-数据监视_Vue


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
oODzP4HWeioA
最新推荐 更多