一、事件系统
- 事件绑定原理
- 在 Vue3 中,事件绑定是通过模板语法(如
@click
)实现的。当模板被编译时,这些事件绑定表达式会被转化为 JavaScript 代码来处理事件。例如,对于模板<button @click="handleClick">Click me</button>
,编译后大致会生成如下代码:
- 在 Vue3 中,事件绑定是通过模板语法(如
function render() {
return h('button', {
onClick: this.handleClick
}, 'Click me');
}
- 这里
h
函数创建按钮虚拟节点时,将onClick
属性指向组件实例的handleClick
方法。当按钮在页面上被点击时,就会调用handleClick
方法。 - 在底层,Vue3 使用了 DOM 的原生事件机制。当组件挂载时,会根据虚拟节点上的事件绑定信息,通过
addEventListener
为真实 DOM 元素添加相应的事件监听器。例如:
function patch(n1, n2, container) {
if (!n1) {
// 新增节点
const el = (n2.el = document.createElement(n2.type));
if (n2.props) {
for (const key in n2.props) {
if (key.startsWith('on')) {
const eventName = key.slice(2).toLowerCase();
el.addEventListener(eventName, n2.props[key]);
}
}
}
container.appendChild(el);
} else {
// 更新节点逻辑...
}
}
- 上述代码展示了在
patch
过程中,当创建新节点时,如果节点属性中有以on
开头的属性(即事件绑定),会提取事件名并为 DOM 元素添加相应的事件监听器。
- 自定义事件与事件冒泡
- 自定义事件是 Vue 组件间通信的重要方式。在子组件中,可以通过
this.$emit('eventName', payload)
触发自定义事件,父组件通过@eventName="handleEvent"
监听该事件。例如:
- 自定义事件是 Vue 组件间通信的重要方式。在子组件中,可以通过
<!-- 子组件 -->
<template>
<button @click="sendCustomEvent">Send Event</button>
</template>
<script>
export default {
methods: {
sendCustomEvent() {
this.$emit('custom - event', 'Hello from child');
}
}
};
</script>
<!-- 父组件 -->
<template>
<child - component @custom - event="handleCustomEvent"></child - component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleCustomEvent(payload) {
console.log('Received payload:', payload);
}
}
};
</script>
- 从源码角度看,
$emit
函数在组件实例上被定义,它会在组件的_events
对象中查找对应的事件监听器数组,并依次调用这些监听器函数。对于组件树中的事件冒泡,它并非基于 DOM 的事件冒泡机制,而是 Vue 自定义的事件传播机制。当子组件触发自定义事件时,Vue 会沿着组件树向上查找父组件中是否有对应的事件监听器,并依次调用,从而实现类似冒泡的效果。
二、计算属性与侦听器
- 计算属性的实现机制
- 计算属性在 Vue3 中通过
computed
函数创建。例如:
- 计算属性在 Vue3 中通过
import { computed, reactive } from 'vue';
const state = reactive({
count: 0
});
const doubleCount = computed(() => state.count * 2);
- 在源码层面,
computed
函数返回一个ComputedRefImpl
实例。这个实例内部维护了一个effect
,用于追踪计算属性依赖的响应式数据(如上述例子中的state.count
)。ComputedRefImpl
有一个_dirty
标志,用于判断计算属性的值是否过期。当依赖的数据发生变化时,_dirty
会被设为true
,表示计算属性需要重新计算。当访问计算属性时,如果_dirty
为true
,会重新执行effect
中的计算逻辑,并更新_dirty
为false
,返回计算后的值。例如:
class ComputedRefImpl {
constructor(getter) {
this.getter = getter;
this._dirty = true;
this._value = undefined;
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true;
}
});
}
get value() {
if (this._dirty) {
this._value = this.effect.run();
this._dirty = false;
}
return this._value;
}
}
- 侦听器的工作原理
- 侦听器在 Vue3 中通过
watch
函数实现。例如:
- 侦听器在 Vue3 中通过
import { reactive, watch } from 'vue';
const state = reactive({
message: 'Initial'
});
watch(() => state.message, (newValue, oldValue) => {
console.log('Message changed from', oldValue, 'to', newValue);
});
state.message = 'Updated';
watch
函数接受一个源(可以是一个函数返回响应式数据,如上述例子中的() => state.message
)和一个回调函数。在源码中,watch
会创建一个WatchEffect
实例,它内部同样基于ReactiveEffect
来追踪源的变化。当源依赖的响应式数据发生变化时,会触发WatchEffect
实例的回调函数,并传入新值和旧值。此外,watch
还支持一些选项,如deep
选项用于深度监听对象或数组的变化,immediate
选项用于在初始化时立即执行回调函数。例如,当设置deep: true
时,WatchEffect
会递归地为对象或数组的所有属性添加依赖追踪,确保深层数据变化时也能触发回调。
三、错误处理
- 全局错误处理
- Vue3 提供了全局错误处理机制。可以通过
app.config.errorHandler
来设置全局的错误处理函数。例如:
- Vue3 提供了全局错误处理机制。可以通过
import { createApp } from 'vue';
const app = createApp({
// 组件选项
});
app.config.errorHandler = (err, vm, info) => {
console.error('Global error caught:', err);
console.log('Component instance:', vm);
console.log('Error info:', info);
};
app.mount('#app');
- 在源码中,当 Vue 在组件渲染、事件处理、生命周期钩子函数等过程中捕获到错误时,会调用
app.config.errorHandler
设置的函数。err
是错误对象,vm
是发生错误的组件实例(如果有的话),info
提供了关于错误发生位置的额外信息,如错误发生在哪个生命周期钩子函数或指令等。
- 组件内错误处理
- 组件内也可以通过
errorCaptured
生命周期钩子函数来捕获子组件传递上来的错误。例如:
- 组件内也可以通过
<template>
<child - component></child - component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
errorCaptured(err, childVm, errorInfo) {
console.error('Error captured in parent:', err);
console.log('Child component instance:', childVm);
console.log('Error info:', errorInfo);
return true; // 阻止错误继续向上传播
}
};
</script>
- 当子组件抛出错误时,会触发父组件的
errorCaptured
钩子函数。可以在这个钩子函数中进行错误处理,并且通过返回true
或false
来决定是否阻止错误继续向上传播到祖先组件的errorCaptured
钩子函数。这种机制使得 Vue 应用在错误处理方面具有较好的层次结构和灵活性。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容