当前位置: 首页 > 知识库问答 >
问题:

性能优化 - Vue 子组件渲染次数与父组件 props 和子组件数据变化的关系?

卞成荫
2024-01-09

一般来讲vue父组件的数据发生变化会触发父组件渲染,如果父组件传入子组件的props数据发生变化会触发子组件渲染,如果 vue 中 父组件的props和子组件的数据同时发生变化,子组件会渲染2次吗?

如题

共有2个答案

姚和顺
2024-01-09

在Vue的源码中,当响应式数据发现改变的时候并不会立刻更新页面,而是执行执行queueWatcher函数,而这个函数内部维护着一个队列,而需要执行更新的组件会被推入到这个队列中,如果你在一个事件循环多次向这个队列中push同一个组件,它只会push一次,因为它每次push的时候都会拿你正在push的组件的id去跟队列中的组件的id比较,如果存在就不再push。Vue会把队列通过nextTick来执行,而nextTick也不是立刻执行的,它内部是以Promise,mutationObserver,setImmediate,setTimeout其中一个来执行,优先级就是上面的排列顺序,所以它就变成了一个异步任务。因此子组件只会更新一次。

Vue视图更新源码分析如下:

/***将观察程序推入观察程序队列。*ID重复的作业将被跳过,除非*在刷新队列时推送。*/export function queueWatcher(watcher: Watcher) {    // 首先获取当前 watcher 的 id  const id = watcher.id  // 判断这个 watcher 是否已经存在,因为 Vue 并不是在数据发生变化之后立刻更新视图  // 而是收集起来的,因此在上一次没有更新视图之前,即使数据发生改变也无需再添加,因为  // watcher 更新视图的时候使用的数据依然是最新的数据  if (has[id] != null) {   // 已经存在就不再收集    return  }  if (watcher === Dep.target && watcher.noRecurse) {    return  }  has[id] = true  if (!flushing) {  // 否则把 watcher 放到一个队列中,在下一次事件循环的时候再一次性更新视图;    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.    let i = queue.length - 1    while (i > index && queue[i].id > watcher.id) {      i--    }    queue.splice(i + 1, 0, watcher)  }  // queue the flush  // 接着就开始执行渲染  if (!waiting) {    waiting = true    if (__DEV__ && !config.async) {      flushSchedulerQueue()      return    }    // 执行渲染,可以看到它调用的就是 nextTick 方法;    // flushSchedulerQueue 就是刷新页面的    nextTick(flushSchedulerQueue)  }}function flushSchedulerQueue() {  currentFlushTimestamp = getNow()  flushing = true  let watcher, id//刷新前对队列进行排序。//这样可以确保://1。组件从父级更新到子级。(因为父母总是//在子项之前创建)//2。组件的用户观察程序在其渲染观察程序之前运行(因为//用户观察程序是在渲染观察程序之前创建的)//3。如果组件在父组件的观察程序运行期间被破坏,//它的观察者可以被跳过。  queue.sort(sortCompareFn)//不要缓存长度,因为可能会推送更多的观察者//当我们运行现有的观察程序时  for (index = 0; index < queue.length; index++) {    watcher = queue[index]    if (watcher.before) {      watcher.before()    }    id = watcher.id    has[id] = null    watcher.run()    //在dev-build中,检查并停止循环更新。    if (__DEV__ && has[id] != null) {      circular[id] = (circular[id] || 0) + 1      if (circular[id] > MAX_UPDATE_COUNT) {        warn(          'You may have an infinite update loop ' +            (watcher.user              ? `in watcher with expression "${watcher.expression}"`              : `in a component render function.`),          watcher.vm        )        break      }    }  }
古弘
2024-01-09

在 Vue 中,当父组件的 props 数据发生变化时,子组件会被重新渲染。然而,子组件的数据变化并不会影响父组件的渲染次数。这是因为 Vue 的响应式原理是单向的:父组件通过 props 将数据传递给子组件,子组件内部的数据变化不会反馈给父组件。

如果父组件的 props 数据和子组件的数据同时发生变化,子组件的渲染次数取决于哪个变化先发生。如果父组件的 props 数据先发生变化,子组件会被重新渲染。如果子组件的数据先发生变化,由于 Vue 的懒加载机制,父组件可能不会感知到这个变化,因此不会触发重新渲染。

需要注意的是,如果子组件内部使用了 key 属性,并且 key 属性的值发生了变化,那么 Vue 会认为这是一个全新的子组件,会销毁旧的子组件并创建新的子组件,这样就会触发两次渲染。所以,如果需要让 Vue 感知子组件的数据变化并重新渲染,一种常用的方法是改变子组件的 key 属性值。

 类似资料:
  • 本文向大家介绍vue的props实现子组件随父组件一起变化,包括了vue的props实现子组件随父组件一起变化的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了vue的props实现父组件变化子组件一起变化,供大家参考,具体内容如下 类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 绑定动态 Props 到父组件的数据。每当父组件的数据变化时,也会传导给

  • 我对react还比较陌生,我一直在分解一个web应用程序,并用react组件替换部分。我现在正在开发一个组件,其中包含我创建的几个不同组件。 在新组件中,我在componentDidMount函数中进行API调用,并创建子组件。乍一看,一切看起来都很完美,但当我们在其中一个子组件中进行状态更改,然后在父组件中进行更改时,子组件将其状态重置为更改之前的状态。 我知道发生了什么,州政府没有被传递给家长

  • 本文向大家介绍详解vue父子组件间传值(props),包括了详解vue父子组件间传值(props)的使用技巧和注意事项,需要的朋友参考一下 先定义一个子组件,在组件中注册props 在父组件中,引入子组件,并传入子组件内需要的值 这种方式只能由父向子传递,子组件不能更新父组件内的data 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 诉求:react父组件某些值改变后,需要让子组件同步更新,但是这个过程不能让父组件重新渲染 请帮忙罗列下所有可能的方式

  • 我在用vue-cli支架做网页包 我的Vue组件结构/传家宝目前看起来如下: 应用程序 在应用程序级别,我需要一个vuejs组件方法,该方法可以将子组件的所有数据聚合到一个JSON对象中,然后发送到服务器。 有没有办法访问子组件的数据?具体来说,多层深? 如果没有,传递oberservable数据/参数的最佳实践是什么,这样当子组件修改它时,我就可以访问新的值?我试图避免组件之间的硬依赖关系,所以

  • 本文向大家介绍浅谈Vue父子组件和非父子组件传值问题,包括了浅谈Vue父子组件和非父子组件传值问题的使用技巧和注意事项,需要的朋友参考一下 本文介绍了浅谈Vue父子组件和非父子组件传值问题,分享给大家,具体如下: 1.如何创建组件 1.新建一个组件,如:在goods文件夹下新建goodsList.vue 2.在main.js中引入 import goodsList from 'goods/good