vue原理分析(十)研究new Vue()中的initEvents

devtools/2024/9/23 11:15:43/

在Vue.prototype._init 中有一些init函数,今天我们来研究这些init函数

Vue.prototype._init = function (options) {......{initProxy(vm);}......initLifecycle(vm);initEvents(vm);initRender(vm);callHook$1(vm, 'beforeCreate', undefined, false /* setContext */);initInjections(vm); // resolve injections before data/propsinitState(vm);initProvide(vm); // resolve provide after data/propscallHook$1(vm, 'created');......
}

上一篇中已经研究了initLifecycle,今天我们往下研究

Vue.prototype._init = function (options) {......initEvents(vm);......
}

看下源码 initEvents(vm);

function initEvents(vm) {vm._events = Object.create(null);vm._hasHookEvent = false;// init parent attached eventsconst listeners = vm.$options._parentListeners;if (listeners) {updateComponentListeners(vm, listeners);}
}
vm._events = Object.create(null);

该行代码创建了一个原型为null的空对象,并将这个对象赋值给vm实例的_events属性

vm._hasHookEvent = false;

该行代码 给vm实例的_hasHookEvent属性赋值为false

const listeners = vm.$options._parentListeners;

从英文注释,我们可以知道这行代码是为了初始化父组件添加的事件

因为是取的组件$options属性的_parentListeners,这个比较复杂,涉及到双向绑定和虚拟dom,我们后续研究,先暂时理解为父组件绑定在当前组件上的事件

如果存在事件listeners

则执行下面这段代码

if (listeners) {updateComponentListeners(vm, listeners)
}
function updateComponentListeners(vm, listeners, oldListeners) {target$1 = vm;updateListeners(listeners, oldListeners || {}, add$1, remove$1, createOnceHandler$1, vm);target$1 = undefined;
}
target$1 = vm;

这行代码的主要作用是保留对vm实例的引用

updateListeners(listeners, oldListeners || {}, add, remove, vm)

这行代码执行updateListeners方法

function add$1(event, fn) {target$1.$on(event, fn);
}
function remove$1(event, fn) {target$1.$off(event, fn);
}

涉及到 target$1.$on 和 target$1.$off

Vue.prototype.$on = function (event, fn) {const vm = this;if (isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$on(event[i], fn);}}else {(vm._events[event] || (vm._events[event] = [])).push(fn);// optimize hook:event cost by using a boolean flag marked at registration// instead of a hash lookupif (hookRE.test(event)) {vm._hasHookEvent = true;}}return vm;
};

else之前的代码都很简单,先缓存this,如果传入的事件是事件数组的话,则分别对数组内的每一项调用$on绑定事件

else之前的代码 如果event不是个数组,给vm._events[event]传入fn函数

if (hookRE.test(event)) {vm._hasHookEvent = true;
}

它表示的是父组件有没有直接绑定钩子函数在当前组件上

return vm

最后 返回vm实例对象

现在我们知道了vm.$on方法主要就是把传入的方法给push到_events属性里,方便之后被$emit调用

Vue.prototype.$off = function (event, fn) {const vm = this;// allif (!arguments.length) {vm._events = Object.create(null);return vm;}// array of eventsif (isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$off(event[i], fn);}return vm;}// specific eventconst cbs = vm._events[event];if (!cbs) {return vm;}if (!fn) {vm._events[event] = null;return vm;}// specific handlerlet cb;let i = cbs.length;while (i--) {cb = cbs[i];if (cb === fn || cb.fn === fn) {cbs.splice(i, 1);break;}}return vm;
};
const vm = this;
// all
if (!arguments.length) {vm._events = Object.create(null);return vm;
}

从代码我们看出来当arguments.length为0的时候,说明没有任何参数,这时就需要移除所有的事件监听器,因此我们重置了 vm.events 属性。

// array of events
if (isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$off(event[i], fn);}return vm;
}

如果参数event是个数组,则循环调用vm.$off

分析特殊情况

const cbs = vm._events[event];
if (!cbs) {return vm;
}
if (!fn) {vm._events[event] = null;return vm;
}

表示,有event但是没有cbs,没有callbacks,没有回调函数

则vm._events[event] = null,返回vm实例对象

let cb;
let i = cbs.length;
while (i--) {cb = cbs[i];if (cb === fn || cb.fn === fn) {cbs.splice(i, 1);break;}
}
return vm;

事件名和回调都有,从从列表中找到与参数提供回调函数相同的那个函数,并将它从列表中移除

再回到updateListeners方法

function updateListeners(on, oldOn, add, remove, createOnceHandler, vm) {let name, cur, old, event;for (name in on) {cur = on[name];old = oldOn[name];event = normalizeEvent(name);if (isUndef(cur)) {warn$2(`Invalid handler for event "${event.name}": got ` + String(cur), vm);}else if (isUndef(old)) {if (isUndef(cur.fns)) {cur = on[name] = createFnInvoker(cur, vm);}if (isTrue(event.once)) {cur = on[name] = createOnceHandler(event.name, cur, event.capture);}add(event.name, cur, event.capture, event.passive, event.params);}else if (cur !== old) {old.fns = cur;on[name] = old;}}for (name in oldOn) {if (isUndef(on[name])) {event = normalizeEvent(name);remove(event.name, oldOn[name], event.capture);}}
}
for (name in on) {cur = on[name];old = oldOn[name];event = normalizeEvent(name);if (isUndef(cur)) {warn$2(`Invalid handler for event "${event.name}": got ` + String(cur), vm);}else if (isUndef(old)) {if (isUndef(cur.fns)) {cur = on[name] = createFnInvoker(cur, vm);}if (isTrue(event.once)) {cur = on[name] = createOnceHandler(event.name, cur, event.capture);}add(event.name, cur, event.capture, event.passive, event.params);}else if (cur !== old) {old.fns = cur;on[name] = old;}
}

遍历on中对象

如果on[name]未定义,则调用警告warn$2

如果oldOn[name]未定义,如果原事件 没有定义,则调用 createFnInvoker() 进行定义;但如果是一个 .once 修饰的事件,则会用 createOnceHandler() 重新定义该事件;最后将其添加到当前实例的 $on 属性中

如果on[name]与oldOn[name]不一致,将原事件定义的 函数定义部分 重新指向为当前新的事件定义,并赋值给新事件对象中

for (name in oldOn) {if (isUndef(on[name])) {event = normalizeEvent(name);remove(event.name, oldOn[name], event.capture);}
}

遍历oldOn中对象,将不存在于新事件对象中的事件去除


http://www.ppmy.cn/devtools/109949.html

相关文章

vscode 中使用 yarn 出错

问题 vscode 中使用 yarn 爆红&#xff0c;类似下图的错误&#xff1a; 原因 由于vscode中的集成终端使用的是powershell&#xff0c;所以需要设置下该权限才能正常使用yarn 解决 找到 powershell&#xff0c;以管理身份运行 输入&#xff1a;set-ExecutionPolicy Remot…

JSON格式

JSON&#xff08;JavaScript Object Notation, JS对象简谱&#xff09;是一种轻量级的数据交换格式。JSON 就是一种字符串格式,这种格式无论是在前端还是在后端,都可以很容易的转换成对象,所以常用于前后端数据传递 说明&#xff1a; JSON的语法 ​ var obj“{‘属性名’:‘…

2024数学建模国赛A题详细思路:基于空间几何运动学和优化模型matlab求解

2024数学建模国赛A题“板凳龙”闹元宵 2024高教社杯数学建模竞赛A题B题C题D题E题完整成品文章和全部问题的解题代码完整版本更新如下&#xff1a;https://www.yuque.com/u42168770/qv6z0d/rytbc1nelty1mu4o % 定义常量 L_head 3.41; % 龙头长度&#xff08;米&#xff09; L…

运维Tips | 如何安全的移除系统中旧的Linux内核?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 如何安全的删除系统中旧的 Linux 内核? 描述&#xff1a;如果更新了 Linux 操作系统&#xff0c;那么你会注意到&#xff0c;每次升级 Linux 内核后&#xff0c;GRUB 菜单都会添加一个新的引导条…

【原创教程】自动化工程案例01:8工位插针装配机03-程序解读

在前面两篇文章中&#xff0c;我们介绍了8工位设备每个工位的情况&#xff0c;然后我们介绍了触摸屏的情况&#xff0c;接着我们来看一下程序。关于一些实物照片不宜公开发表&#xff0c;需要的可以私信。 程序系统块设置 系统块中的模块实际上是我们所使用的的硬件设施 符号…

记录在飞牛NAS系统中通过Docker搭建并配置Linux版本XAMPP镜像环境[图文]

最近官产飞牛NAS系统开始公测&#xff0c;发现介绍的还不错&#xff0c;于是在虚拟机里面搭建了一个飞牛NAS系统试一试&#xff0c;发现这个nas系统里面的docker特别好用&#xff0c;特别是在现在很难拉取Docker官网的镜像。 飞牛NAS里面的docker内容丰富&#xff0c;拉取速度也…

前端代码规范- Commit 提交规范

什么是约定式提交 约定式提交&#xff08;Conventional Commits&#xff09;是一种用于代码版本控制的规范&#xff0c;旨在通过明确和标准化提交信息来提高代码协作质量和效率。其基本原则是通过规定提交信息的结构和语义来提高代码版本控制的可读性、可维护性和自动化程度。…

Linux运维_Bash脚本_源码编译Moby(Docker-CE)-20240803

Linux运维_Bash脚本_源码编译Moby(Docker-CE)-20240803 Bash (Bourne Again Shell) 是一个解释器&#xff0c;负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件&#xff0c;并于 1989 年发布的免费软件&#xff0c;作为 Sh (Bourne Shell) 的替代品。 您可…