vue 的响应式原理理解

今天就是出现了一个问题,就是在使用计数器时候出现了计数器只能点击一次,然后再点击就没反应了,而且也不会触发技术区的change方法,具体代码如下,就不说业务过程了,反正就是这个问题,让我想深入了解以下vue的响应。

  dialogForm: {
        questionIdList: [], //
  },
    selectedChange(e, i) {
      var newArr = [];
        for (let i = 0; i < e.length; i++) {
          e.sort = 1
          newArr.push(e)
      }
      this.dialogForm.questionIdList = this.dialogForm.questionIdList.concat(newArr)
    },

百度了一下找到了原因,其实就是因为我这样赋值,导致数组中的sort变成非相应式的,导致就算点计数器的加号也不能响应式的显示值,为什么会这样,就是其实vue中data中的内容都是放一些响应的值的内容的,准格尔时候你如果需要这个值就是响应的那就需要在data中定义,(就算你赋值为空也要定义)如果直接在方法中或者其他地方像我一样使用e.sort = 1这种赋值方式,那么这个sort是有值的,但是vue并不能响应式的去处理这个内容。

之前我对于这个响应式就是说一个值,如果在任何用的一处变了,别的地方也会跟着变,现在是时候深入研究一下了这个内容了,尤雨溪在文档中曾说过Vue最独特的功能之一就是非侵入式的响应系统。那不得仔细的看看这个内容。。

首先vue是怎么知道这个值的变化的

当你把普通的JavaScript对象传递给一个Vue实例作为data选项,Vue将遍历所有的属性,并使用Object.defineProperty将这些属性转为getter/setters。需要注意的是,在打印转换数据对象时,浏览器控制台打印的getters/setters格式并不相同。所以为了更友好的界面检查你可能需要安装vue-devtools。

let o={},age;
o.name='张三';
console.log(o.name);//张三
Object.defineProperty(o,"age",{
get(){
	return age,
set(val){
	age = val;
)

每一个组件实例有一个一致的watcher实例,它会把组件渲染的过程中把任何属性记录为依赖。而后当一个依赖的setter被触发的时候,它会通知watcher这个值被改变了,你要告诉所有依赖这个属性的组件,反过来导致组件被重新渲染。官方图如下
在这里插入图片描述

什么时候变化才会被检测到呢

我也搜索了一下之前使用Object.observe是可以的,但是后面被停用了,我也有些好奇,如果有这个 不就可以去检测了么,网上搜索说Angular 2团队曾经实验性的使用了 Object.observe,但是因为性能原因最终放弃了。原因在于 Object.observe的使用限制了很多 V8 中已有的优化,导致被 observed 的对象会比 non-observed 的对象慢得多。过多的上下文切换 (框架和浏览器之间) 会对异步的数据变化通知造成挑战,也很难对框架进行大幅性能优化 (macro-optimizations)。所以才会被删除。

所以现在Vue不能检测到属性的添加或删除。由于Vue在实例初始化期间执行了getter/setter转换过程,为了Vue转换换它和响应它,属性必须在data对象中存在。例如:

data: {
	wahah: 123
}
// 这里如果我们在使用 this.wahah 事后它是响应式的
//但是如果使用未定定义的内容时候例如
	this.wahah1 = 123
// 这时this.wahah1 不是响应式的

Vue不允许动态的添加一个新的属性到一个已经被创建好的实例中。那么又想加,又想让这个属性变成响应式的,需要使this.$set(object, key, value)方法向一个被嵌套的对象中添加响应式属性是可以的例如:

this.$set(this.someObject, 'b', 2)

vue需要声明响应式属性

由于Vue不允许动态添加根级响应属性,你必须通过声明所有根级响应式属性初始化Vue实例,即使值为空:
template: ‘

{{ message }}/div>’
data: {
// 用一个空值声明message
message: ‘’
},

// 稍后设置’message’
this.message = ‘wahaha!’

如果你没有声明message在data操作项,渲染函数试着访问一个不存在的属性,Vue将警告你。

这样的限制在背后是有技术上的原因的-它消除了依赖追踪中的边缘情况,也使得Vue实例在类型检查系统的帮助下发挥更好的作用。而且在代码可维护性方法也有重要的考虑:data对象就像组件状态的概要。当一会再看或者另一个开发者读代码的时候,预先声明所有响应式属性使得组件代码更容易理解。

异步更新队列

Vue执行DOM更新是异步的。无论何时当一个数据改变被观察到,它会开一个队列,缓存发生在同一个消息循环内的所有data改变。如果相同的watcher被触发多次,它仅仅被pushed到队列一次。这种缓存重复数据删除对于避免不必要的计算和DOM维护是重要的。然后,在下一次消息循环"tick"中,Vue刷新队列并执行实际的(已经去重的)工作。内部的Vue尝试对异步队列使用原生的Promise.then和MessageChannel。

例如,当你设置vm.someData = ‘new value’,组件将不会立即重新渲染。它将更新在下一个’tick’ , 当一个队列被刷新后。多数情况下你不需要关心这个,但是当你想在DOM状态更新之后做点什么。这就可能有些棘手。虽然Vue一般鼓励开发者沿着数据驱动的方式思考,避免直接接触DOM,有时可能确实需要这么做。为了等待数据更新之后Vue.js已经完成了DOM更新,在数据改变之后你能够立刻使用Vue.nextTick(callback)。当DOM已经被更新之后这个回调将被调用。

<template>
  <div>
    <p ref="message">{{ message }}</p>
    <button @click="changeMessage">Change Message</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello, Vue!"
    };
  },
  methods: {
    changeMessage() {
      this.message = "Updated Message";
      this.logMessage();
    },
    logMessage() {
      console.log("nextTick方法外:", this.$refs.message.textContent);//在这访问到的是未更新的DOM
      this.$nextTick(() => {
        // 在 DOM 更新后执行的操作
        const updatedMessage = this.$refs.message.textContent;//获取到的是更新后的DOM
        console.log("nextTick方法里:", updatedMessage);
      });
    }
  }
};
</script>

打印
nextTick方法外:Hello, Vue!
nextTick方法里:Updated Message

这个现象说明什么呢,就是说,我们点击这个按钮的时候,给赋值this.message = “Updated Message”;,然后调用了logMessage,要是按照java 的思维来说,在logMessage中去console.log(this.message)的时候应该打印修改后的内容,但是却恰恰相反,还是原来的内容,这个说明什么?说明这个时候dom并没有更新,然而使用 this.$nextTick这个方法等待dom的更新后然后使用这个回调打印时,这个值才进行了变化。。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值