vue2/3

vue2/3

Proxy比defineProperty到底好在哪(vue2/3数据响应式实现方式的差异)

Proxy比defineProperty到底好在哪【渡一教育】

  • vue2中定义了一个Observer,利用Object.defineProperty来监听对象属性的读取的修改,为了监听对象的每个属性,Observer会对深度遍历每个属性。一旦属性被读取或者修改,Observer就可以在读取或修改时做一些其他操作。但这种监听方式有个弊端,那就是对象新增或者删除属性时,无法监听到。
  • Proxy解决了上述问题,Proxy直接监听对象整体,一旦对象发生变化,Proxy就行监听得到。
    在Vue2和Vue3中,响应式系统的实现方式有所不同。Vue2使用的是Object.defineProperty,而Vue3则采用了Proxy。这两种技术都用于实现数据的响应式,但它们的实现方式和能力有所不同。

1. Vue2中的Object.defineProperty

在Vue2中,响应式系统是通过Object.defineProperty来实现的。Object.defineProperty可以定义或修改对象的属性,并且可以设置属性的gettersetter,从而在属性被访问或修改时触发相应的操作。

代码示例:
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      console.log(`get ${key}: ${val}`);
      return val;
    },
    set: function reactiveSetter(newVal) {
      console.log(`set ${key}: ${newVal}`);
      if (newVal !== val) {
        val = newVal;
      }
    }
  });
}

const data = {};
defineReactive(data, 'message', 'Hello, Vue2!');

console.log(data.message); // 输出: get message: Hello, Vue2!
data.message = 'Hello, World!'; // 输出: set message: Hello, World!
缺点:
  • 无法监听数组的变化Object.defineProperty无法直接监听数组的变化(如pushpop等操作),Vue2中通过重写数组方法来实现对数组的监听。
  • 无法监听属性的添加或删除Object.defineProperty只能监听已经存在的属性,无法监听对象属性的添加或删除。

2. Vue3中的Proxy

Vue3中使用了Proxy来实现响应式系统。Proxy是ES6引入的新特性,它可以拦截对象的操作,包括属性的读取、设置、删除等。

代码示例:
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      console.log(`get ${key}: ${target[key]}`);
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      console.log(`set ${key}: ${value}`);
      return Reflect.set(target, key, value, receiver);
    },
    deleteProperty(target, key) {
      console.log(`delete ${key}`);
      return Reflect.deleteProperty(target, key);
    }
  });
}

const data = reactive({ message: 'Hello, Vue3!' });

console.log(data.message); // 输出: get message: Hello, Vue3!
data.message = 'Hello, World!'; // 输出: set message: Hello, World!
delete data.message; // 输出: delete message
优点:
  • 监听数组变化Proxy可以直接监听数组的变化,无需像Vue2那样重写数组方法。
  • 更全面的拦截能力Proxy可以拦截更多的操作,包括属性的读取、设置、删除、枚举等,而Object.defineProperty只能拦截属性的读取和设置。
  • 性能更好Proxy是原生支持的特性,性能上比Object.defineProperty更好。
  • 更简洁的代码:使用Proxy可以更简洁地实现响应式系统,无需像Object.defineProperty那样需要遍历对象的每个属性。

如何理解响应式?

响应式是指当数据发生变化时,系统能够自动更新依赖于这些数据的部分。在前端框架中,响应式系统通常用于实现数据与视图的自动同步。例如,当数据发生变化时,视图会自动更新,而不需要手动操作DOM。

在Vue中,响应式系统的核心是依赖追踪触发更新

  • 依赖追踪:当组件渲染时,Vue会记录哪些数据被使用(即依赖)。
  • 触发更新:当数据发生变化时,Vue会通知所有依赖该数据的组件进行更新。

Vue2中Observer是如何实现的?

在Vue2中,响应式系统的核心是Observer,它通过Object.defineProperty来实现对数据的监听。

实现步骤:
  1. 遍历对象属性Observer会遍历对象的每一个属性。
  2. 定义gettersetter:使用Object.defineProperty为每个属性定义gettersetter
  3. 依赖收集:在getter中收集依赖(即哪些组件或计算属性使用了该数据)。
  4. 触发更新:在setter中触发更新,通知依赖该数据的组件重新渲染。
代码示例:
function defineReactive(obj, key, val) {
  const dep = new Dep(); // 依赖管理器

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      dep.depend(); // 收集依赖
      return val;
    },
    set: function reactiveSetter(newVal) {
      if (newVal !== val) {
        val = newVal;
        dep.notify(); // 触发更新
      }
    }
  });
}

class Dep {
  constructor() {
    this.subscribers = new Set();
  }
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }
  notify() {
    this.subscribers.forEach(effect => effect());
  }
}

let activeEffect = null;
function watchEffect(effect) {
  activeEffect = effect;
  effect();
  activeEffect = null;
}

const data = {};
defineReactive(data, 'message', 'Hello, Vue2!');

watchEffect(() => {
  console.log('Effect:', data.message);
});

data.message = 'Hello, World!'; // 输出: Effect: Hello, World!
缺点:
  • 无法监听数组的变化(需要重写数组方法)。
  • 无法监听属性的添加或删除。

Reflect`是什么?

Reflect是ES6引入的一个内置对象,它提供了一组用于操作对象的方法。这些方法与Proxy的拦截器方法一一对应,例如Reflect.getReflect.setReflect.deleteProperty等。

Reflect的主要作用是:

  • 提供一种更规范的方式来操作对象。
  • Proxy配合使用,确保Proxy的拦截器方法能够正确地操作目标对象。

Proxy中为什么要用Reflect

Proxy的拦截器方法中使用Reflect是为了确保操作能够正确地应用到目标对象上。例如:

  • Reflect.get(target, key, receiver):确保get操作能够正确地获取目标对象的属性值。
  • Reflect.set(target, key, value, receiver):确保set操作能够正确地设置目标对象的属性值。

使用Reflect的好处是:

  • 避免手动操作目标对象,减少错误。
  • 保持代码的简洁性和一致性。
示例:
const target = { message: 'Hello' };
const proxy = new Proxy(target, {
  get(target, key, receiver) {
    console.log(`Getting ${key}`);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log(`Setting ${key} to ${value}`);
    return Reflect.set(target, key, value, receiver);
  }
});

proxy.message; // 输出: Getting message
proxy.message = 'World'; // 输出: Setting message to World

Proxygetset中为什么还有个receiver`?

receiverProxygetset方法的第三个参数,它指向当前的代理对象(或继承代理对象的对象)。

receiver的作用是:

  • 确保this的指向正确。例如,当代理对象被继承时,receiver会指向子类实例,而不是目标对象。
  • Reflect方法配合使用,确保操作能够正确地应用到代理对象上。
示例:
const target = { name: 'Alice' };
const proxy = new Proxy(target, {
  get(target, key, receiver) {
    console.log(`Getting ${key} from ${receiver === proxy}`);
    return Reflect.get(target, key, receiver);
  }
});

const child = Object.create(proxy);
child.name; // 输出: Getting name from false

为什么说“Proxy是原生支持的特性”?

Proxy是ES6标准的一部分,由JavaScript引擎原生支持。这意味着:

  • 不需要额外的库或工具来实现Proxy
  • 性能更好,因为它是引擎级别的实现。
  • 兼容性较好,现代浏览器和Node.js都支持Proxy

相比之下,Object.defineProperty虽然也是原生特性,但它的功能有限,无法实现Proxy那样全面的拦截能力。


【vue2源码】array.ts

路径

src/core/observer/array.ts

代码及注释

/*
 * 不对此文件进行类型检查,因为 Flow(类型检查工具)无法很好地处理
 * 动态访问数组原型上的方法
 */

// 导入 Vue 3 中的 TriggerOpTypes,用于标识触发更新的操作类型
import {TriggerOpTypes} from '../../v3'

// 导入 Vue 的工具函数 def,用于定义对象的属性
import {def} from '../util/index'

// 获取数组的原型对象
const arrayProto = Array.prototype

// 创建一个新的对象,继承自数组的原型对象
// 这样可以在不直接修改 Array.prototype 的情况下,扩展数组的方法
export const arrayMethods = Object.create(arrayProto)

// 需要拦截的数组变异方法列表
const methodsToPatch = [
  'push',    // 向数组末尾添加元素
  'pop',     // 删除数组末尾的元素
  'shift',   // 删除数组开头的元素
  'unshift', // 向数组开头添加元素
  'splice',  // 从数组中添加/删除元素
  'sort',    // 对数组进行排序
  'reverse'  // 反转数组
]

/**
 * 拦截数组的变异方法并触发更新事件
 */
methodsToPatch.forEach(function (method) {
  // 缓存数组原型上的原始方法
  const original = arrayProto[method]

  // 使用 def 函数在 arrayMethods 对象上定义新的方法
  // 这些方法会覆盖数组原型上的同名方法
  def(arrayMethods, method, function mutator(...args) {
    // 调用原始的数组方法,并获取返回值
    const result = original.apply(this, args)

    // 获取当前数组的观察者对象(Observer 实例)
    const ob = this.__ob__

    // 用于存储新插入的元素
    let inserted

    // 根据不同的方法,处理新插入的元素
    switch (method) {
      case 'push':
      case 'unshift':
        // push 和 unshift 方法会将所有参数作为新元素插入数组
        inserted = args
        break
      case 'splice':
        // splice 方法的第三个参数开始是插入的新元素
        inserted = args.slice(2)
        break
    }

    // 如果有新插入的元素,调用 observeArray 方法对它们进行观察
    if (inserted) ob.observeArray(inserted)

    // 通知依赖更新
    if (__DEV__) {
      // 在开发环境下,传递更详细的更新信息
      ob.dep.notify({
        type: TriggerOpTypes.ARRAY_MUTATION, // 更新类型为数组变异
        target: this,                        // 目标数组
        key: method                         // 触发更新的方法名
      })
    } else {
      // 在生产环境下,只通知更新
      ob.dep.notify()
    }

    // 返回原始方法的执行结果
    return result
  })
})


代码解释

  1. 类型检查注释

    • 代码开头的注释提到,不对此文件进行类型检查,因为 Flow(Vue 2 中使用的类型检查工具)无法很好地处理动态访问数组原型上的方法。
  2. 导入模块

    • TriggerOpTypes 是 Vue 3 中用于标识触发更新的操作类型的枚举。虽然这段代码是 Vue 2 的,但可能引用了 Vue 3 的某些类型定义。
    • def 是 Vue 的工具函数,用于定义对象的属性。
  3. 数组原型扩展

    • arrayProto 是数组的原型对象。
    • arrayMethods 是一个新创建的对象,继承自 arrayProto。这样可以在不直接修改 Array.prototype 的情况下,扩展数组的方法。
  4. 需要拦截的数组方法

    • methodsToPatch 是一个数组,列出了需要拦截的数组变异方法。这些方法会改变数组的内容,Vue 需要拦截这些方法以触发视图更新。
  5. 拦截数组方法

    • 对于 methodsToPatch 中的每个方法,代码都会在 arrayMethods 上定义一个同名的方法。
    • 这些新定义的方法首先会调用原始的数组方法(original.apply(this, args)),然后处理新插入的元素(如果有的话),最后通知依赖更新。
  6. 处理新插入的元素

    • 对于 pushunshift 方法,所有参数都是新插入的元素。
    • 对于 splice 方法,第三个参数开始是插入的新元素。
    • 如果有新插入的元素,调用 ob.observeArray(inserted) 对它们进行观察,以便 Vue 能够追踪这些新元素的变化。
  7. 通知依赖更新

    • 在开发环境下,ob.dep.notify 会传递更详细的更新信息,包括更新类型、目标数组和触发更新的方法名。
    • 在生产环境下,只简单地通知依赖更新。
  8. 返回原始方法的执行结果

    • 最后,返回原始方法的执行结果,确保新定义的方法与原始方法的行为一致。

总结

这段代码的核心目的是拦截数组的变异方法,并在这些方法被调用时触发 Vue 的响应式系统更新视图。通过这种方式,Vue 能够追踪数组的变化,并在数组内容发生变化时自动更新视图。

【vue2源码】nextTick.ts

在这里插入图片描述

nextTick应用场景及示例

在 Vue2 中,nextTick 是一个非常重要的 API,用于在 DOM 更新完成后执行回调。由于 Vue 的响应式更新是异步批量处理的,直接修改数据后立即操作 DOM 可能无法获取更新后的结果。nextTick 可以确保代码在 DOM 更新完成后执行,从而避免操作未更新的 DOM。


🔍 核心应用场景

  1. 操作更新后的 DOM
    当数据变化后需要立即操作新生成的 DOM 元素(如获取元素尺寸、聚焦输入框等)。

  2. 依赖 DOM 的第三方库初始化
    例如使用图表库(ECharts)时,需要确保容器已渲染完成。

  3. 解决异步更新导致的逻辑问题
    当多个数据变化需要在同一事件循环中完成后统一处理。

  4. 在组件渲染完成后执行逻辑
    例如在 mounted 钩子中操作子组件的 DOM。


📝 具体示例

示例 1:数据更新后滚动到列表底部
<template>
  <div>
    <div ref="list" style="height: 100px; overflow-y: auto;">
      <div v-for="item in items" :key="item">{{ item }}</div>
    </div>
    <button @click="addItem">添加条目</button>
  </div>
</template>

<script>
export default {
  data() {
    return { items: [1, 2, 3] };
  },
  methods: {
    addItem() {
      this.items.push(this.items.length + 1);
      this.$nextTick(() => {
        // DOM 更新后滚动到最新条目
        const list = this.$refs.list;
        list.scrollTop = list.scrollHeight;
      });
    },
  },
};
</script>

说明

  • 点击按钮后,列表会新增一个条目。
  • 使用 nextTick 确保 DOM 更新完成后,滚动条滚动到列表底部。

示例 2:输入框自动聚焦
<template>
  <div>
    <button @click="showInputField">显示输入框</button>
    <input v-if="showInput" ref="input" />
  </div>
</template>

<script>
export default {
  data() {
    return { showInput: false };
  },
  methods: {
    showInputField() {
      this.showInput = true;
      this.$nextTick(() => {
        // DOM 渲染完成后聚焦
        this.$refs.input.focus();
      });
    },
  },
};
</script>

说明

  • 点击按钮后,输入框显示并自动聚焦。
  • 使用 nextTick 确保输入框渲染完成后才调用 focus 方法。

示例 3:结合第三方图表库(ECharts)
<template>
  <div ref="chart" style="width: 400px; height: 300px;"></div>
</template>

<script>
import echarts from 'echarts';

export default {
  data() {
    return { data: [5, 20, 36] };
  },
  mounted() {
    this.$nextTick(() => {
      // 确保容器 DOM 已渲染
      const chart = echarts.init(this.$refs.chart);
      chart.setOption({
        xAxis: { type: 'category', data: ['A', 'B', 'C'] },
        yAxis: { type: 'value' },
        series: [{ data: this.data, type: 'bar' }],
      });
    });
  },
};
</script>

说明

  • mounted 钩子中使用 nextTick 确保 DOM 渲染完成后初始化 ECharts 图表。

示例 4:在子组件渲染完成后操作 DOM
<template>
  <div>
    <ChildComponent ref="child" />
    <button @click="updateChild">更新子组件</button>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: { ChildComponent },
  methods: {
    updateChild() {
      // 更新子组件数据
      this.$refs.child.someData = 'new value';
      this.$nextTick(() => {
        // 子组件 DOM 更新后执行操作
        const childElement = this.$refs.child.$el;
        console.log('子组件已更新:', childElement);
      });
    },
  },
};
</script>

说明

  • 父组件更新子组件数据后,使用 nextTick 确保子组件 DOM 更新完成后再执行操作。

示例 5:解决多个数据更新后的统一处理
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="updateData">更新数据</button>
  </div>
</template>

<script>
export default {
  data() {
    return { message: 'Hello', count: 0 };
  },
  methods: {
    updateData() {
      this.message = 'Updated';
      this.count++;
      this.$nextTick(() => {
        // 确保所有数据更新完成
        console.log('DOM 已更新:', this.$el.textContent);
      });
    },
  },
};
</script>

说明

  • 点击按钮后,同时更新多个数据。
  • 使用 nextTick 确保所有数据更新完成后执行回调。

示例 6:动态加载组件后操作 DOM
<template>
  <div>
    <button @click="loadComponent">加载组件</button>
    <component :is="currentComponent" ref="dynamicComponent" />
  </div>
</template>

<script>
import DynamicComponent from './DynamicComponent.vue';

export default {
  data() {
    return { currentComponent: null };
  },
  methods: {
    loadComponent() {
      this.currentComponent = DynamicComponent;
      this.$nextTick(() => {
        // 动态组件加载完成后操作 DOM
        const componentElement = this.$refs.dynamicComponent.$el;
        console.log('动态组件已加载:', componentElement);
      });
    },
  },
};
</script>

说明

  • 动态加载组件后,使用 nextTick 确保组件 DOM 渲染完成后再执行操作。

⚙️ 实现原理

Vue2 的 nextTick 内部基于 微任务/宏任务队列 实现:

  1. 优先使用 Promise.then(微任务)。
  2. 降级到 MutationObserversetImmediate
  3. 最终使用 setTimeout(fn, 0)(宏任务)。

通过异步队列机制,确保回调在本次事件循环的 DOM 更新后执行。


💡 注意事项

  1. 避免滥用:频繁调用 nextTick 可能导致性能问题。
  2. 生命周期中的使用:在 mounted 钩子中操作 DOM 时,建议包裹 nextTick,因为子组件可能尚未渲染完成。
  3. watch 结合使用:在 watch 中监听数据变化时,如果需要操作 DOM,可以使用 nextTick

通过合理使用 nextTick,可以精准控制 Vue 的异步更新流程,解决 DOM 操作时机问题。


【Vue2】生命周期钩子函数

Vue 2 的生命周期是 Vue 组件从创建到销毁的整个过程,每个阶段都有对应的生命周期钩子函数,你可以在这些钩子中执行特定的逻辑。理解这些钩子函数有助于你在合适的时机执行代码,从而更好地控制组件的行为。

Vue 2 的生命周期钩子函数

Vue 2 的生命周期可以分为以下几个阶段,每个阶段都有对应的钩子函数:

  1. 创建阶段(Initialization)

    • beforeCreate
    • created
  2. 挂载阶段(Mounting)

    • beforeMount
    • mounted
  3. 更新阶段(Updating)

    • beforeUpdate
    • updated
  4. 销毁阶段(Destruction)

    • beforeDestroy
    • destroyed

1. 创建阶段

beforeCreate
  • 调用时机:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
  • 使用场景:此时组件的 datamethods 还未初始化,通常用于一些插件或功能的初始化。
beforeCreate() {
  console.log('beforeCreate: 组件实例刚被创建,data 和 methods 还未初始化');
}
created
  • 调用时机:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前尚不可用。
  • 使用场景:此时可以访问 datamethods,通常用于发起异步请求、初始化数据等。
created() {
  console.log('created: 组件实例创建完成,data 和 methods 已初始化');
}

2. 挂载阶段

beforeMount
  • 调用时机:在挂载开始之前被调用,相关的 render 函数首次被调用。
  • 使用场景:此时模板已经编译完成,但尚未将模板渲染到页面上。
beforeMount() {
  console.log('beforeMount: 模板编译完成,但尚未挂载到页面');
}
mounted
  • 调用时机:在实例挂载完成后调用,此时 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
  • 使用场景:此时 DOM 已经渲染完成,可以操作 DOM 或访问 DOM 元素。
mounted() {
  console.log('mounted: 组件已挂载到页面,可以访问 DOM');
}

3. 更新阶段

beforeUpdate
  • 调用时机:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
  • 使用场景:可以在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
beforeUpdate() {
  console.log('beforeUpdate: 数据更新,DOM 还未重新渲染');
}
updated
  • 调用时机:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。
  • 使用场景:此时 DOM 已经更新,可以执行依赖于 DOM 的操作。
updated() {
  console.log('updated: 数据更新,DOM 已重新渲染');
}

4. 销毁阶段

beforeDestroy
  • 调用时机:实例销毁之前调用。在这一步,实例仍然完全可用。
  • 使用场景:通常用于清理工作,比如清除定时器、取消事件监听等。
beforeDestroy() {
  console.log('beforeDestroy: 组件即将销毁,此时实例仍然可用');
}
destroyed
  • 调用时机:实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
  • 使用场景:通常用于最后的清理工作。
destroyed() {
  console.log('destroyed: 组件已销毁,所有的事件监听器和子实例已被移除');
}

生命周期图示

为了更好地理解 Vue 2 的生命周期,可以参考以下图示:

beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed

总结

  • 创建阶段beforeCreatecreated 钩子用于组件的初始化和数据设置。
  • 挂载阶段beforeMountmounted 钩子用于 DOM 的挂载和操作。
  • 更新阶段beforeUpdateupdated 钩子用于数据更新时的 DOM 操作。
  • 销毁阶段beforeDestroydestroyed 钩子用于组件的清理和销毁。

【Vue2】父子组件的生命周期有何关联性?

在 Vue 2 中,父子组件的生命周期钩子函数执行顺序遵循明确的规律,理解这些关联性有助于优化组件间的数据传递、DOM 操作和资源管理。以下是父子组件生命周期的关联性及执行顺序的详细总结:


一、父子组件生命周期的执行顺序

1. 加载渲染阶段
  • 顺序
    父 beforeCreate → 父 created → 父 beforeMount → 子 beforeCreate → 子 created → 子 beforeMount → 子 mounted → 父 mounted
  • 说明
    • 父组件先完成初始化和模板编译(beforeCreatecreatedbeforeMount),但此时子组件尚未创建。
    • 在父组件的 beforeMount 阶段,子组件开始创建并完成自身的生命周期(beforeCreatecreatedbeforeMountmounted)。
    • 父组件的 mounted 在所有子组件挂载完成后触发,确保此时父组件可以安全操作子组件的 DOM。
2. 更新阶段
  • 顺序
    父 beforeUpdate → 子 beforeUpdate → 子 updated → 父 updated
  • 说明
    • 父组件数据变化时,先触发父的 beforeUpdate
    • 子组件接收父组件传递的 props 更新后,执行子组件的 beforeUpdateupdated
    • 父组件的 updated 最后执行,确保所有子组件已完成更新。
3. 销毁阶段
  • 顺序
    父 beforeDestroy → 子 beforeDestroy → 子 destroyed → 父 destroyed
  • 说明
    • 父组件销毁时,先触发自身的 beforeDestroy
    • 子组件依次销毁,触发自身的 beforeDestroydestroyed
    • 父组件的 destroyed 最后执行,确保所有子组件已清理完毕。

二、关键关联性分析

1. 父组件是子组件的“容器”
  • 创建与挂载:父组件需先完成模板解析(beforeMount),才能开始子组件的初始化和挂载。
  • 更新与销毁:父组件的状态变化或销毁会触发子组件的响应,体现了父子组件状态的依赖关系。
2. 异步组件的特殊场景
  • 如果子组件是异步加载的(如通过 () => import()),父组件的生命周期会先完成(执行到 mounted),子组件的生命周期后续才执行。

三、实际开发中的注意事项

1. 操作子组件的 DOM
  • 在父组件的 mounted 钩子中操作子组件的 DOM 是安全的,因为此时子组件已挂载完成。
2. 异步数据传递
  • 若父组件在 created 中异步获取数据并传递给子组件,子组件可能在 createdmounted 中无法立即获取数据。
    解决方法
    • 使用 v-if 控制子组件的渲染,确保数据就绪后再渲染子组件。
    • 子组件通过 watch 监听 props 的变化。
3. 资源清理
  • 在父组件的 beforeDestroy 中,子组件尚未销毁,可在此通知子组件执行清理逻辑(如移除事件监听或定时器)。

四、总结规律

  1. 创建与挂载:父组件先初始化,子组件先挂载完成。
  2. 更新与销毁:父组件先触发,子组件先完成。
  3. 执行顺序的核心原则:父组件作为容器,需确保子组件的生命周期在其作用域内完成。

通过掌握这些生命周期关联性,可以避免因时机不当引发的 Bug(如操作未挂载的 DOM),并优化父子组件的协作逻辑。具体示例和完整代码可参考相关开发文档或示例源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值