美文网首页vue
vue解析2-响应式数据

vue解析2-响应式数据

作者: 百里哈哈 | 来源:发表于2020-04-06 21:09 被阅读0次

简单的示例

在实际的vue应用中watcher对象与vm实例是分不开的,然而为了简化observe、 dep、watcher的分析过程,特地采用了一个脱离vm的示例进行演示。

import Vue from 'vue'

function test1() {
    let observable = Vue.observable;
    let Watcher = Vue.Watcher;
    let person = {
        name: 'haha',
        age: 18
    }
    observable(person)
    person._watchers = [];
    let watcher = new Watcher(person, 'age', function (val) {
        console.log('the age is' , val)
    })
    console.log(watcher.value)
    person.age = 16;
    // person.age = 17;
    Vue.nextTick(function () {
        console.log(watcher.value)
    })
}

test1();

该例子中在Vue上额外挂载了watcher对象,以便对外暴露。
1.通过observable将data数据进行get、set的设置
2.通过new Watcher 实现对age数据的监听
3.watcher.update会推入到一个异步队列进行批次更新, 所以在 Vue.nextTick可取到最新的值

observe、dep、watcher关系图

observe.png

1.observer递归遍历data数据,为其属性设置get/set

  1. 在observer的get方法中实现依赖收集, 对使用到该数据的watcher对象进行订阅。
    3.在observer的set方法中,数据发生变更则通知其订阅的watcher对象进行update
    4.在new Watcher的时候会对其监听的数据进行一次获取即this.getter方法, 通过该方法会进入到observer的get方法中
    5.dep.notify通知其订阅的watcher进行更新, 通常情况下会将更新的对象放入一个queue队列采用异步更新。

watcher部分源码

var Watcher = function Watcher (
  vm,
  expOrFn,
  cb,
  options,
  isRenderWatcher
) {
  this.vm = vm;
  if (isRenderWatcher) {
    vm._watcher = this;
  }
  vm._watchers.push(this);
  // options
 ...
  this.cb = cb;
  this.id = ++uid$2; // uid for batching
  this.active = true;
  this.dirty = this.lazy; // for lazy watchers
  this.deps = [];
  this.newDeps = [];
  this.depIds = new _Set();
  this.newDepIds = new _Set();
  this.expression = process.env.NODE_ENV !== 'production'
    ? expOrFn.toString()
    : '';
  // parse expression for getter
  if (typeof expOrFn === 'function') {
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
    if (!this.getter) {
      this.getter = noop;
      process.env.NODE_ENV !== 'production' && warn(
        "Failed watching path: \"" + expOrFn + "\" " +
        'Watcher only accepts simple dot-delimited paths. ' +
        'For full control, use a function instead.',
        vm
      );
    }
  }
  this.value = this.lazy
    ? undefined
    : this.get();
};

$get & run

Watcher.prototype.get = function get () {
  pushTarget(this);
  var value;
  var vm = this.vm;
  try {
    value = this.getter.call(vm, vm);
  } catch (e) {
    ...
  } finally {
    // "touch" every property so they are all tracked as
    // dependencies for deep watching
    if (this.deep) {
      traverse(value);
    }
    popTarget();
    this.cleanupDeps();
  }
  return value
};

Watcher.prototype.run = function run () {
  if (this.active) {
    var value = this.get();
    ...
    }
  }
};

render Watcher

new Watcher.png

1.每个vm实例都会进行一个new watcher, 在该对象的getter上挂载updateComponent方法

  1. 在get方法中调用updateComponent完成render的vnode以及真实节点的渲染, 在render的过程中template中涉及的渲染数据都会对该watcher对象进行订阅

watcher.cleanupDeps作用

在写Vue模板的时候,模板中涉及的渲染数据都会对render watcher进行订阅。 如果我们更改的数据并不会影响到页面的渲染,安装正常的逻辑是不需要对render watcher进行订阅的。 例如v-if的一些条件判断,在false的情况 下其中的data不需要订阅render watcher, 所以需要对其进行解绑。测试用例如下

<template>
    <div>
        <p v-if="countFlag"> you counter is {{count}}</p>
        <p>another is {{msg}}</p>
        <span @click="countInCreate">to create num</span>
        <span @click="setCountFlag(false)">hide counter</span>
    </div>
</template>
<script>
export default {
    data () {
        return {
            count: 1,
            msg: 'test if',
            countFlag: true
        }
    },
    methods: {
        countInCreate() {
            this.count++;
        },
        setCountFlag(val) {
            this.countFlag = false;
        }
    }
}
</script>

可在Watcher.prototype.get中打断点进行测试, 点击countInCreate方法, 断点可以打入get中,接下来如果将this.countFlag设为false,则watcher.run并不会执行。

array方法重写

在Vue的开发过程中经常会遇到一些关于数组没有自动监听响应的问题, Vue只是对array的一些原生方法进行了重写。有下面代码可知使用push、pop、shift、unshift、splice、sort、reverse可实现自动监听, 爱用concat的小伙伴需谨慎。

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);

var methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  var original = arrayProto[method];
  def(arrayMethods, method, function mutator () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var result = original.apply(this, args);
    var ob = this.__ob__;
    var inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break
      case 'splice':
        inserted = args.slice(2);
        break
    }
    if (inserted) { ob.observeArray(inserted); }
    // notify change
    ob.dep.notify();
    return result
  });
});

相关文章

  • vue解析2-响应式数据

    简单的示例 在实际的vue应用中watcher对象与vm实例是分不开的,然而为了简化observe、 dep、wa...

  • 前端面试题【Day02】

    本篇绪论 1,Vue响应式原理 1,Vue响应式原理 在vue实例中声明的数据就是响应式的。响应式:数据发生改变,...

  • 实现双向数据绑定proxy比defineProperty优劣如何

    vue三要素 响应式:如何监听数据变化(双向数据绑定) 模版引擎:如何解析模板 渲染:vue如何将监听到的数据变化...

  • 2020-12-25

    Vue数据响应式 响应式:当一个物体对外界刺激做出反应,就是响应式。例如:我打你一拳你知道躲。 Vue 数据响应式...

  • Vue的响应式浅析

    1 Vue如何实现响应式? Vue的响应式是建立在监听data中的数据. 2 在Vue2中响应式的实现 Vue通过...

  • Vue源码03-响应式原理

    这节将专门讲解vue MVVM响应式处理 Vue采用的是 实现数据的响应式 数据劫持 Observer类 Obse...

  • 学习vue的响应式 mvvm -01 数据响应式

    理解VUE的设计思想:VUE的核心是MVVM MVVM框架的三要素:数据响应式 模板引擎以及渲染 数据响应式:监听...

  • Vue数据响应式

    1.什么是数据响应式 在 Vue 中,当 data 中的数据发生改变时,视图会进行更新,这就是Vue数据响应式的概...

  • 深入理解Vue数据响应式

    Vue数据响应式主要研究的是 Vue 构造选项中 data 属性的特性 深入响应式 官方文档 网址: https:...

  • VUE中数据响应式原理

    1.vue数据响应式的原理2.数据属性的四大特性3.访问器属性4.getter和setter vue数据响应式主要...

网友评论

    本文标题:vue解析2-响应式数据

    本文链接:https://www.haomeiwen.com/subject/khebphtx.html