Vue数据变化后页面更新详细介绍

首先会通过module.hot.accept监听文件变化,并传入该文件的渲染函数:

module.hot.accept(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&", __WEBPACK_OUTDATED_DEPENDENCIES__ => { /* harmony import */ _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&");
(function () {
      api.rerender('472cff63', {
        render: _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render,
        staticRenderFns: _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns
      })
    })(__WEBPACK_OUTDATED_DEPENDENCIES__); })

随后执行rerender方法,只贴重点部分代码:

 record.Ctor.options.render = options.render
    record.Ctor.options.staticRenderFns = options.staticRenderFns
    record.instances.slice().forEach(function (instance) {
      instance.$options.render = options.render
      instance.$options.staticRenderFns = options.staticRenderFns
      // reset static trees
      // pre 2.5, all static trees are cached together on the instance
      if (instance._staticTrees) {
        instance._staticTrees = []
      }
      // 2.5.0
      if (Array.isArray(record.Ctor.options.cached)) {
        record.Ctor.options.cached = []
      }
      // 2.5.3
      if (Array.isArray(instance.$options.cached)) {
        instance.$options.cached = []
      }
      // post 2.5.4: v-once trees are cached on instance._staticTrees.
      // Pure static trees are cached on the staticRenderFns array
      // (both already reset above)
      // 2.6: temporarily mark rendered scoped slots as unstable so that
      // child components can be forced to update
      var restore = patchScopedSlots(instance)
      instance.$forceUpdate()
      instance.$nextTick(restore)
    })

首先会给当前的VueComponent的Options和实例里面的渲染函数替换为最新,然后执行实例上的forceUpdate方法:

Vue.prototype.$forceUpdate = function () {
          var vm = this;
          if (vm._watcher) {
              vm._watcher.update();
          }
      };

该方法是执行watcher实例的update方法:

Watcher.prototype.update = function () {
          /* istanbul ignore else */
          if (this.lazy) {
              this.dirty = true;
          }
          else if (this.sync) {
              this.run();
          }
          else {
              queueWatcher(this);
          }
      };
function queueWatcher(watcher) {
      var id = watcher.id;
      if (has[id] != null) {
          return;
      }
      if (watcher === Dep.target && watcher.noRecurse) {
          return;
      }
      has[id] = true;
      if (!flushing) {
          queue.push(watcher);
      }
      else {
          // if already flushing, splice the watcher based on its id
          // if already past its id, it will be run next immediately.
          var i = queue.length - 1;
          while (i > index$1 && queue[i].id > watcher.id) {
              i--;
          }
          queue.splice(i + 1, 0, watcher);
      }
      // queue the flush
      if (!waiting) {
          waiting = true;
          if (!config.async) {
              flushSchedulerQueue();
              return;
          }
          nextTick(flushSchedulerQueue);
      }
  }

重点看 queueWatcher(this),将最新的watcher放入quene队列并且将flushSchedulerQueue函数传给nextTick。

function nextTick(cb, ctx) {
      var _resolve;
      callbacks.push(function () {
          if (cb) {
              try {
                  cb.call(ctx);
              }
              catch (e) {
                  handleError(e, ctx, 'nextTick');
              }
          }
          else if (_resolve) {
              _resolve(ctx);
          }
      });
      if (!pending) {
          pending = true;
          timerFunc();
      }
      // $flow-disable-line
      if (!cb && typeof Promise !== 'undefined') {
          return new Promise(function (resolve) {
              _resolve = resolve;
          });
      }
  }

在callbacks里面放入一个执行回调的函数。并执行timerFunc():

timerFunc = function () {
          p_1.then(flushCallbacks);
          // In problematic UIWebViews, Promise.then doesn't completely break, but
          // it can get stuck in a weird state where callbacks are pushed into the
          // microtask queue but the queue isn't being flushed, until the browser
          // needs to do some other work, e.g. handle a timer. Therefore we can
          // "force" the microtask queue to be flushed by adding an empty timer.
          if (isIOS)
              setTimeout(noop);
      };

该方法异步执行flushCallbacks:

function flushCallbacks() {
      pending = false;
      var copies = callbacks.slice(0);
      callbacks.length = 0;
      for (var i = 0; i < copies.length; i++) {
          copies[i]();
      }
  }

开始执行回调函数我们第一次放进去的函数flushSchedulerQueue:

function flushSchedulerQueue() {
      currentFlushTimestamp = getNow();
      flushing = true;
      var watcher, id;
      // Sort queue before flush.
      // This ensures that:
      // 1. Components are updated from parent to child. (because parent is always
      //    created before the child)
      // 2. A component's user watchers are run before its render watcher (because
      //    user watchers are created before the render watcher)
      // 3. If a component is destroyed during a parent component's watcher run,
      //    its watchers can be skipped.
      queue.sort(sortCompareFn);
      // do not cache length because more watchers might be pushed
      // as we run existing watchers
      for (index$1 = 0; index$1 < queue.length; index$1++) {
          watcher = queue[index$1];
          if (watcher.before) {
              watcher.before();
          }
          id = watcher.id;
          has[id] = null;
          watcher.run();
          // in dev build, check and stop circular updates.
          if (has[id] != null) {
              circular[id] = (circular[id] || 0) + 1;
              if (circular[id] > MAX_UPDATE_COUNT) {
                  warn$2('You may have an infinite update loop ' +
                      (watcher.user
                          ? "in watcher with expression \"".concat(watcher.expression, "\"")
                          : "in a component render function."), watcher.vm);
                  break;
              }
          }
      }
      // keep copies of post queues before resetting state
      var activatedQueue = activatedChildren.slice();
      var updatedQueue = queue.slice();
      resetSchedulerState();
      // call component updated and activated hooks
      callActivatedHooks(activatedQueue);
      callUpdatedHooks(updatedQueue);
      // devtool hook
      /* istanbul ignore if */
      if (devtools && config.devtools) {
          devtools.emit('flush');
      }
  }

首先触发watcher.before(),该方法是beforeUpdate的hook。然后执行watcher.run()方法:

Watcher.prototype.run = function () {
          if (this.active) {
              var value = this.get();
              if (value !== this.value ||
                  // Deep watchers and watchers on Object/Arrays should fire even
                  // when the value is the same, because the value may
                  // have mutated.
                  isObject(value) ||
                  this.deep) {
                  // set new value
                  var oldValue = this.value;
                  this.value = value;
                  if (this.user) {
                      var info = "callback for watcher \"".concat(this.expression, "\"");
                      invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info);
                  }
                  else {
                      this.cb.call(this.vm, value, oldValue);
                  }
              }
          }
      };

该方法watcher.get方法,该方法会重新执行render方法生成vnode,然后调用update方法更新节点:

updateComponent = function () {
              vm._update(vm._render(), hydrating);
          };

总结:

1.获取监听文件最新的render和staticRenderFns并赋值给当前的VueComponent和当前vm实例。

2.使用$forceUpdate添加当前的vm的watcher并在queue中,最后异步执行flushSchedulerQueue,该函数遍历quene执行watcher的run方法,该方法会执行vm实例的_update方法完成更新。

到此这篇关于Vue数据变化后页面更新详细介绍的文章就介绍到这了,更多相关Vue页面更新内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue页面更新patch的实现示例

    patch的流程 组件页面渲染时,将render返回的新vnode(新节点)和组件实例保存的vnode(旧节点)作为参数,调用patch方法,更新DOM. 判断两个节点是否相同 处理过程中,需要判断节点是否相同.相同节点需要满足以下条件: key相同 标签类型相同 注释节点标识相同,都是注释节点,或者都不是注释节点 data的值状态相同,或者都有值,或者都没值 function sameVnode (a, b) {// 判断两个VNode节点是否是同一个节点 return ( a.key ===

  • Vue数据变化后页面更新详细介绍

    首先会通过module.hot.accept监听文件变化,并传入该文件的渲染函数: module.hot.accept(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&", __WEBPACK_OUTDATED_DEPENDENCIES__ =&g

  • vue数据变化但页面刷新问题

    目录 vue数据变化但页面刷新 watch监听到数据的变化但页面没有刷新 没有监听到数据的变化 改变了数据却没有自动刷新 说下结论 vue数据变化但页面刷新 watch监听到数据的变化但页面没有刷新 在数据改动的代码后加 this.$forceUpdate(); 添加this.$forceUpdate();进行强制渲染,效果实现.因为数据层次太多,render函数没有自动更新,需手动强制刷新. 没有监听到数据的变化 例如: 改变了数组中的某一项或者改变了对象中的某个元素时,监听则未生效. 数组若

  • vue视图响应式更新详细介绍

    目录 概述 思路 第一步统一封装更新函数 第二步监听并触发视图更新 引入Dep管家 实现下语法糖v-model 概述 前面两篇文章已经实现了对数据的变化的监听以及模板语法编译初始化,但是当数据变化时,视图还不能够跟随数据实时更新.本文就在之前的基础上介绍下视图响应式更新部分. 思路 统一封装更新函数 待数据发生改变时调用对应的更新函数 这里思考个问题: 在何时注册这个更新函数? 如何找到对应的更新函数? 第一步统一封装更新函数 基于上篇文章compile的部分,将数据初始化的部分统一封装起来.

  • vue中for循环更改数据的实例代码(数据变化但页面数据未变)

    废话不多说了,直接给大家贴代码了,具体代码如下所示: let that = this; for(let i = 0;i<that.tableData.length;i++){ this.tableData[i].zzzk = this.midForm.zzzk; console.log(this.tableData[i].zzzk) this.tableData[i].zhje = this.tableData[i].zzzk * this.tableData[i].dj * this.tabl

  • 关于vue跳转后页面置顶的问题

    目录 vue跳转后页面置顶 vue如何实现置顶 vue跳转后页面置顶 今天测试指出我的项目跳转页面时未置顶,嗯,这个太影响用户体验了. 但是办法总会有哒!No Problem! 只需在路径上匹配一段关于scroll方法即可, 我简单截取一段我的代码以作演示: 或者使用这段代码: afterEach (to, from, next) { window.scrollTo(0, 0) } 但是还有个问题依旧没有彻底克服! 比如,如果当前页面是首页,点击页面中的"首页"选项后,却依然不置顶,那

  • vue中eslintrc.js配置最详细介绍

    本文是对vue项目中自带文件eslintrc.js的内容解析, 介绍了各个eslint配置项的作用,以及为什么这样设置. 比较详细,看完能对eslint有较为全面的了解,基本解除对该文件的疑惑. /** * 参考文档 * [eslint英文文档]https://eslint.org/docs/user-guide/configuring * [eslint中文文档]http://eslint.cn/docs/rules/ */ /** * eslint有三种使用方式 * [1]js代码中通过注释

  • vue实现登录后页面跳转到之前页面

    在开发中我们经常遇到这样的需求,需要用户直接点击一个链接进入到一个页面,用户点击后链接后会触发401拦截返回登录界面,登录后又跳转到链接的页面而不是首页,这种问题该如何去做呢? 先说一下我们需要用到的几个API: 1.router.currentRoute:当前的路由信息对象,我们可以通过router.currentRoute.fullPath获得解析后的 URL,包含查询参数和 hash 的完整路径,如果要访问的页面的路由有命名(name)的话,可以通过router.currentRoute.

  • vue项目中的数据变化被watch监听并处理

    目录 vue数据变化被watch监听处理 监听当前vue文件数据 监听vuex中的数据 如何正确使用watch监听属性变化 基本用法 监听object 初始化变量触发监听回调 vue数据变化被watch监听处理 监听当前vue文件数据 例如,当前的vue文件的data中有如下属性: data() {     return {         dialogFormVisible: false,     } } 要监听dialogFormVisible变量的数据变化,则代码如下: watch: {

  • vue数据控制视图源码解析

    分析vue是如何实现数据改变更新视图的. 前记 三个月前看了vue源码来分析如何做到响应式数据的, 文章名字叫vue源码之响应式数据, 最后分析到, 数据变化后会调用Watcher的update()方法. 那么时隔三月让我们继续看看update()做了什么. (这三个月用react-native做了个项目, 也无心总结了, 因为好像太简单了). 本文叙事方式为树藤摸瓜, 顺着看源码的逻辑走一遍, 查看的vue的版本为2.5.2. 我fork了一份源码用来记录注释. 目的 明确调查方向才能直至目标

  • vue2从数据变化到视图变化发布订阅模式详解

    目录 引言 一.发布订阅者模式的特点 二.vue中的发布订阅者模式 1.dep 2.Object.defineProperty 3.watcher 4.dep.depend 5.dep.notify 6.订阅者取消订阅 小结 引言 发布订阅者模式是最常见的模式之一,它是一种一对多的对应关系,当一个对象发生变化时会通知依赖他的对象,接受到通知的对象会根据情况执行自己的行为. 假设有财经报纸送报员financialDep,有报纸阅读爱好者a,b,c,那么a,b,c想订报纸就告诉financialDe

随机推荐