vue面试created中两次数据修改会触发几次页面更新详解

目录
  • 面试题:
  • 一、同步的
  • 二、异步的
  • 三、附加
  • 总结

面试题:

created生命周期中两次修改数据,会触发几次页面更新?

一、同步的

先举个简单的同步的例子:

new Vue({
  el: "#app",
  template: `<div>
    <div>{{count}}</div>
  </div>`,
  data() {
    return {
      count: 1,
    }
  },
  created() {
    this.count = 2;
    this.count = 3;
  },
});

created生命周期中,通过this.count = 2this.count = 3的方式将this.count重新赋值。

这里直接抛出答案:渲染一次。

为什么?

这个与数据的响应式处理有关,先看响应式处理的逻辑:

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 重点:创建一个发布者实例
  const dep = new Dep()
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        // 重点:进行当前正在计算的渲染Watcher的收集
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      // 重点:当数据发生变化时,发布者实例dep会通知收集到的watcher进行更新
      dep.notify()
    }
  })
}

在数据响应式处理阶段,会实例化一个发布者dep,并且通过Object.defineProperty的方式为当前数据定义getset函数。在生成虚拟vNode的阶段,会触发get函数中会进行当前正在计算的渲染Watcher的收集,此时,发布者depsubs中会多一个渲染Watcher实例。在数据发生变化的时候,会触发set函数,通知发布者depsubs中的watcher进行更新。

至于数据修改会触发几次更新,就与当前发布者depsubs中收集了几次渲染watcher有关了,再看watcher收集和created执行之间的顺序:

Vue.prototype._init = function (options) {
    // ...
    initState(vm);
    // ...
    callHook(vm, 'created');
    // ...
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
}

我们知道在initState(vm)阶段对数据进行响应式处理,但是此时发布者depsubs还是空数组。当执行callHook(vm, 'created')的时候,会执行this.count = 2this.count = 3的逻辑,也的确会触发set函数中的dep.notify通知收集到的watcher进行更新。但是,此时depsubs是空数组,相当于啥也没做。

只有在vm.$mount(vm.$options.el)执行过程中,生成虚拟vNode的时候才会进行渲染Watcher收集,此时,depsubs才不为空。最终,通过vm.$mount(vm.$options.el)进行了页面的一次渲染,并未因为this.count=2或者this.count=3而触发多余的页面更新。

简言之,就是created钩子函数内的逻辑的执行是在渲染watcher收集之前执行的,所以未引起因为数据变化而导致的页面更新。

二、异步的

同步的场景说完了,我们再举个异步的例子:

new Vue({
  el: "#app",
  template: `<div>
    <div>{{count}}</div>
  </div>`,
  data() {
    return {
      count: 1,
    }
  },
  created() {
    setTimeout(() => {
      this.count = 2;
    }, 0)
    setTimeout(() => {
      this.count = 3;
    }, 0)
  },
});

created生命周期中,通过异步的方式执行this.count = 2this.count = 3的方式将this.count重新赋值。

这里直接抛出答案:首次渲染一次,因为数据变化导致的页面更新两次。

为什么?

这个就与eventLoop事件循环机制有关了,我们知道javascript是一个单线程执行的语言,当我们通过new Vue实例化的过程中,会执行初始化方法this._init方法,开始了Vue底层的处理逻辑。当遇到setTimeout异步操作时,会将其推入到异步队列中去,等待当前同步任务执行完以后再去异步队列中取出队首元素进行执行。

当前例子中,在initState(vm)阶段对数据进行响应式处理。当执行callHook(vm, 'created')的时候,会将this.count = 2this.count = 3的逻辑推入到异步队列等待执行。继续执行vm.$mount(vm.$options.el)的过程中会去生成虚拟vNode,进而触发get函数的渲染Watcher收集,此时,depsubs中就有了一个渲染watcher

等首次页面渲染完成以后,会去执行this.count=2的逻辑,数据的修改会触发set函数中的dep.notify,此时发布者depsubs不为空,会引起页面的更新。同理,this.count=3会再次引起页面数据的更新。也就是说,首次渲染一次,因为this.count=2this.count=3还会导致页面更新两次。

三、附加

如果我改变的值和data中定义的值一致呢?

new Vue({
  el: "#app",
  template: `<div>
    <div>{{count}}</div>
  </div>`,
  data() {
    return {
      count: 1,
    }
  },
  created() {
    setTimeout(() => {
      this.count = 1;
    }, 0)
  },
});

这个时候,在触发set的逻辑中,会当执行到if (newVal === value || (newVal !== newVal && value !== value)) { return }的逻辑,不会再执行到dep.notify,这种场景下数据的数据也不会引起页面的再次更新。

总结

从生命周期created和页面渲染的先后顺序,Object.defineProperty触发getset函数的机理,以及eventLoop事件循环机制入手,去分析created中两次数据修改会触发几次页面更新的问题就会清晰很多。

以上就是vue面试created中两次数据修改会触发几次页面更新详解的详细内容,更多关于vue created数据修改页面更新的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue中created与mounted的区别浅析

    大多数人在谈论生命周期钩子时会感到困惑的一件事是 created 和 mounted 之间的区别.有着相似的名称,觉得应该做同样的事情,但还是有一些细微的差别. 首先,created() 和 mounted() 都可以访问原型上的 data 和 props .例如,下面的代码中,这两个钩子将在控制台中打印出 My Data 和 My Props : <template> <div></div> </template> <script> expor

  • vue created钩子函数与mounted钩子函数的用法区别

    1:在使用vue框架的过程中,我们经常需要给一些数据做一些初始化处理,这时候我们常用的就是在created与mounted选项中作出处理. 首先来看下官方解释,官方解释说created是在实例创建完成后呗立即调用. 在这一步,实例已完成以下配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调.然而,挂载阶段还没开始,$el 属性目前不可见. 这话的意思我觉得重点在于说挂架阶段还没开始,什么叫还没开始挂载,也就是说,模板还没有被渲染成html,也就是这

  • vue路由第二次进入页面created和mounted不执行问题及解决

    目录 vue路由第二次进入页面created和mounted不执行 vue created.mounted等方法整理 总结 vue路由第二次进入页面created和mounted不执行 因为路由中created和mounted默认会进行缓存的,除非在router.js中配置:keepAlive: false: 这样是把这个页面的路由缓存给关闭了:true为开启,false为关闭: meta: {   keepAlive: false }, 还有一种办法就是使用activated钩子就可以了 1.

  • vue子组件created方法不执行问题及解决

    目录 vue子组件created方法不执行 解决方法 created和mounted方法没执行问题 vue子组件created方法不执行 近期做了一个项目 里面有一个树形菜单,将数据写在 js (死数据)中,所有的东西都能够正常执行(i 标签,子节点,父节点),但是当在请求接口文件或者请求后台数据的时候,发现引入的子组件的created方法不执行,但是点击父级菜单展开时还是能够触发,后来发现 是生命周期的问题,仔细查看一下,后来解决 解决方法 用watch 检测一下data的数据变化,creat

  • vue.js页面加载执行created,mounted的先后顺序说明

    created页面加载未渲染html之前执行. mounted渲染html后再执行. 由于created在html模板生产之前所以无法对Dom进行操作而mounted可以. 补充知识:关于Vue子组件data选项某个属性引用子组件props定义的属性的几点思考 学过Vue的都知道Vue等MVVM框架相对于传统的JS库比如Jquery最大的区别在于数据驱动视图,重点在于数据,拿到数据后将数据通过模板{{}}语法或者v-html展示在页面上. 我们也都知道在Vue父子组件可以通过Props实现父组件

  • vue面试created中两次数据修改会触发几次页面更新详解

    目录 面试题: 一.同步的 二.异步的 三.附加 总结 面试题: created生命周期中两次修改数据,会触发几次页面更新? 一.同步的 先举个简单的同步的例子: new Vue({ el: "#app", template: `<div> <div>{{count}}</div> </div>`, data() { return { count: 1, } }, created() { this.count = 2; this.coun

  • vue axios数据请求get、post方法及实例详解

    我们常用的有get方法以及post方法,下面简单的介绍一下这两种请求方法 vue中使用axios方法我们先安装axios这个方法 npm install --save axios 安装之后采用按需引入的方法,哪个页面需要请求数据就在哪个页面里引入一下. import axios from 'axios' 引入之后我们就可以进行数据请求了,在methods中创建一个方法 methods:{ getInfo(){ let url = "url" axios.get(url).then((r

  • vue项目中企业微信使用js-sdk时config和agentConfig配置方式详解

    1.如果只使用config配置的相关js接口 可采用如下方式引入 执行 npm weixin-sdk-js --save 局部引入 在vue页面中 import wx from 'weixin-sdk-js'; 全局引入 在vue 的main.js 页面中 引入后编写到vue原型链上,然后全局调用 import wx from "weixin-sdk-js"; Vue.prototype.$wx = wx; 2.如果要使用agentConfig配置的相关接口 一定不要执行npm命令引入

  • Vue中使用方法、计算属性或观察者的方法实例详解

    熟悉 Vue 的都知道 方法methods.计算属性computed.观察者watcher 在 Vue 中有着非常重要的作用,有些时候我们实现一个功能的时候可以使用它们中任何一个都是可以的,但是它们之间又存在一些不同之处,每一个都有一些适合自己的场景,我们要想知道合适的场景,肯定先对它们有一个清楚的了解,先看一个小例子. <div id="app"> <input v-model="firstName" type="text"&

  • Vue监听数据渲染DOM完以后执行某个函数详解

    实例如下: vm.$watch('某data数据',function(val){ vm.$nextTick(function() { 某事件(); }); }) 以上这篇Vue监听数据渲染DOM完以后执行某个函数详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多(步骤详解)

    一.思路分析和效果图 用vue来实现一个瀑布流效果,加载网络图片,同时有下拉刷新和上拉加载更多功能效果.然后针对这几个效果的实现,捋下思路: 根据加载数据的顺序,依次追加标签展示效果: 选择哪种方式实现瀑布流,这里选择绝对定位方式: 关键问题:由于每张图片的宽高不一样,而瀑布流中要求所有图片的宽度一致,高度随宽度等比缩放.而且由于图片的加载是异步延迟.在不知道图片高度的情况下,每个图片所在的item盒子不好绝对定位.因此在渲染页面前先获取所有图片的高度,是解决问题的关键点!这里选择用JS中的Im

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

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

  • vue cli实现项目登陆页面流程详解

    目录 1. 搭建项目 1.1 使用vue-cli创建项目 1.2 通过npm安装element-ui 1.3 导入组件 2 创建登录页面 2.1 创建登录组件 2.2 引入css(css.txt) 2.3 配置路由 2.4 在Login组件中将提交按键调整为100%宽度 2.5 运行效果 3. 后台交互 3.1 引入axios 3.2 axios/qs/vue-axios安装与使用 3.2.1 安装axios 3.2.2 发送get请求 3.2.3 发送post请求 3.2.4 简化axios使

  • 微信小程序开发数据缓存基础知识辨析及运用实例详解

    提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容. 提示:以下是本篇文章正文内容,下面案例可供参考 一.微信数据缓存是什么? 在实际开发中,在用到一个数据时,我们需要调用api接口去得到,然后渲染在页面中,但是对于一些数据,是经常需要使用的,如果每次使用时都需要调用api接口,会十分麻烦.数据缓存就解决了这个问题,我们可以在初次调用某api得到数据的同时将数据缓存,那么在之后的使用过程

  • Java中List<T>和List<?>的区别详解

    一.简介 <T>在List.Set.Map中经常见到,用来限制Class中的参数类型,确保Class中参数的一致性.例如:List<String> list = new ArrayList<>();创建了一个内部参数是String类型的类,list中的操作对象都是String.<?>代表任意java类型,只有在不关心数据的具体类型下才使用通配符表示,但在一些情况下,需要将<?>传入的数据进行强转,但这样不如直接传入<T>. 另外除了&

随机推荐