热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

大家都能懂的Vue源碼系列—09—initEvents

上篇文章中,我們重要講了initLiftcycle要領,它的作用是初始化vm實例中和生命周期相干的屬性。本日為人人引見另一個要領——initEvents。從這個要領的稱號來看,我們

上篇文章中,我們重要講了initLiftcycle要領,它的作用是初始化vm實例中和生命周期相干的屬性。本日為人人引見另一個要領——initEvents。
從這個要領的稱號來看,我們曉得它是和事宜相干的要領,詳細怎樣相干,我們先來看源碼。

export function initEvents (vm: Component) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}

先看第一行

vm._events = Object.create(null)

該行代碼創建了一個原型為null的空對象,並把它賦值給vm實例的_events屬性。關於Object.create,不明白的同硯能夠點這裏檢察。關於vm._events,許多博文中提到該屬性就是籠統的說是用來寄存事宜的對象,那末究竟寄存什麼事宜呢?vm的一切事宜都寄存在裏面?顯然是不對的,那詳細是什麼,我們來看下面的例子。
html部份:


@hook:created="hookFromParent"
@hover="hoverFromParent"
:msg-from-father="msg"
:hit-from-father="hit"
>

js部份

const childCompOnent= Vue.component('child', {
template: '

{{msgFromFather}} and {{hitFromFather}}

',
data: function () {
return {
childMsg: 'Hello, I am Child'
}
},
props: ['msgFromFather', 'hitFromFather'],
methods: {
showMsgFromSon () {
console.log('Hello Vue from son')
}
},
mounted () {
console.log('child mounted')
}
})
const app = new Vue({
el: '#app',
data: function () {
return {
msg: 'Hello Chris, I am your father',
hit: 'I will hit you if you do not study',
}
},
components: {
childComponent
},
methods: {
hoverFromParent () {
console.log('attch event')
},
hookFromParent () {
console.log('attch hook')
}
}
})

下圖示意的是上述demo中vm._events屬性的值

《大家都能懂的Vue源碼系列—09—initEvents》

上面例子中,child組件上除了父組件綁定的要領以外,其組件內部另有showMsgFromSon和mounted鈎子要領,然則這兩個要領都沒有出如今_events屬性中。綜上可知,vm._events示意的是父組件綁定在當前組件上的事宜。
接下來看代碼

vm._hasHookEvent = false

這行代碼把我們vm實例上的_hasHookEvent屬性設置為false。該屬性示意父組件是不是經由歷程”@hook:”把鈎子函數綁定在當前組件上。該用法能夠在上個demo中找到,經由歷程以下體式格局完成綁定。

@hook:鈎子稱號="綁定的函數"

繼承回到源碼中

// init parent attached events
const listeners = vm.$options._parentListeners

從英文詮釋中,我們曉得這行代碼的作用是初始化父組件增加的事宜。那詳細是什麼意義呢?經由歷程追蹤vm.$options._parentListeners的賦值歷程(這個歷程有點龐雜,在以後講雙向綁定和假造dom的時刻會說到),我們曉得vm.$options._parentListeners實在和上面的_events一樣,都是用來示意父組件綁定在當前組件上的事宜。(固然照樣略有點差別,這個以後會解說)假如存在這些綁定的事宜,那末就實行下面代碼

if (listeners) {
updateComponentListeners(vm, listeners)
}

假如事宜存在,則挪用updateComponentListeners更新這些要領。

export function updateComponentListeners (
vm: Component,
listeners: Object,
oldListeners: ?Object
) {
target = vm
updateListeners(listeners, oldListeners || {}, add, remove, vm)
target = undefined
}

來看updateComponentListeners要領的源碼

target = vm

這行代碼的重要作用是保存對vm實例的援用,在實行updateListeners要領時能訪問到實例對象,並實行add和remove要領。

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

在研討updateListeners源碼之前,我們先來相識一下傳入的這幾個參數。listeners我們前面說過,是父組件綁定在當前組件上的事宜對象,oldListeners示意當前組件上舊的事宜對象,vm是vue實例對象。這三個沒什麼好說的,我們詳細來講講別的兩個參數add和remove。

add要領

add要領源碼以下:

function add (event, fn, once) {
if (once) {
target.$once(event, fn)
} else {
target.$on(event, fn)
}
}

假如第三個參數once為true,則實行vue.$once要領,不然實行vue.$on要領。我們先來看vue.$on

vue.$on要領

為何要先講$on要領,因為$once要領中也須要用到$on,在看$on源碼之前,我們先來看看官方文檔里對它的定義。

監聽當前實例上的自定義事宜。事宜能夠由vm.$emit觸發。回調函數會吸收一切傳入事宜觸發函數的分外參數。

曉得了vue.$on的定義以後,我們再來看源碼。

Vue.prototype.$on = function (event: string | Array, fn: Function): Component {
const vm: CompOnent= this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i this.$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 lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}

else之前的代碼都很簡樸,先緩存this,假如傳入的事宜是事宜數組的話,則離別對數組內的每一項挪用$on綁定事宜。接下來重點看看else塊內的代碼。

(vm._events[event] || (vm._events[event] = [])).push(fn)

我們曉得_events是示意直接綁定在組件上的事宜,假如是經由歷程$on新增加的事宜(也相當於直接綁定在組件上的事宜),我們也要把事宜和回調要領傳入到_events對象中。
回到源碼中

// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash look
if (hookRE.test(event)) {
vm._hasHookEvent = true
}

關於這句代碼的詮釋,網上許多文章說的都是相似下面的話

這個bool標誌位來表明是不是存在鈎子,而不須要經由歷程哈希表的要領來查找是不是有鈎子,如許做能夠削減不必要的開支,優化機能。

這句話除了是翻譯原文詮釋以外,還存在顯著的毛病,這個tag不是表明是不是存在鈎子,而是示意是不是運用下面的體式格局綁定鈎子。
假如是以下情勢綁定的鈎子,則_hasHookEvent屬性為true。

@hook:created="hookFromParent"
>

而像下面這類情勢,它也存在鈎子函數,然則它的_hasHookEvent就是false。

const childCompOnent= Vue.component('child', {
...
created () {
console.log('child created')
}
})

所以_hasHookEvent不是示意是不是存在鈎子,它示意的是父組件有無直接綁定鈎子函數在當前組件上。說這麼多,只是願望人人盡量的少被誤導。那末,那句詮釋究竟是什麼意義呢?我們能夠從callHook的源碼中來尋覓答案。

export function callHook (vm: Component, hook: string) {
const handlers = vm.$options[hook]
if (handlers) {
for (let i = 0, j = handlers.length; i try {
handlers[i].call(vm)
} catch (e) {
handleError(e, vm, `${hook} hook`)
}
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
}

當前實例的鈎子函數假如是經由歷程父組件的:hook體式格局來指定的,那末它在實行鈎子函數的回調要領時就是直接觸發vm.$emit來實行。(這類體式格局相似於dom中的addEventListener監聽事宜和dispatchEvent觸發事宜)
假如不是上面這類要領指定的鈎子函數,就須要實行callhook源碼上半部份的代碼邏輯。找到vm實例上的鈎子函數,然後實行綁定在它上面的回調。至於實行效力的題目,沒有去研討過,然則原文詮釋里都說了是優化鈎子,那末證實第一種要領實行效力應當是優於第二種要領。
我們回到$on的源碼中,末了是返回vm實例對象。

return vm

如今我們曉得了vm.$on要領重要就是把傳入的要領給push到_events屬性里,輕易以後被$emit挪用。

vm.$once

講過了vm.$on的重要作用以後,我們接着來剖析vm.$once的源碼,先看文檔中關於vm.$once的定義。

監聽一個自定義事宜,然則只觸發一次,在第一次觸發以後移除監聽器。

相識了vm.$once的定義以後,我們再來看源碼

Vue.prototype.$Once= function (event: string, fn: Function): Component {
const vm: CompOnent= this
function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}

連繫上面的定義和之前講的vm.$on要領,我們應當比較輕易明白解$once了,它和$on要領的中心區分重要在on要領

function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}

on要領包裝了event的回調事宜,這是on和once最實質的區分,當觸發once綁定的回調時刻,實行on要領,先挪用$off要領(這個要領是移除監聽的要領,我們待會兒就會講)移除監聽,然後再實行回調函數。如許就完成了只觸發一次的功用,講到這裏,add要領中一切的內容就已講完了。
因為文章篇幅的緣由,其他關於initEvents的內容我們下篇文章繼承講,重要有$off,$emit和updateListeners相干的完成。敬請期待。


推荐阅读
  • #ReactivityFundamentals#DeclaringReactiveState ... [详细]
  • node.jsrequire和ES6导入导出的区别原 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 先看看ElementUI里关于el-table的template数据结构:<template><el-table:datatableData><e ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 【Vue基础】监听属性watch
    Vue监听属性是watch,我们可以通过watch来响应数据的变化。代码示例: ... [详细]
  • 本文涉及源码版本为2.6.9准备工作down一份Vue源码,从package.json入手,找我们需要的代码1、package.json中的scripts,build:nodesc ... [详细]
  • Vue的组件化
    文章目录Vue的组件化一、认识组件化1.什么是组件化2.组件化思想二、注册组件1.组件的使用步骤三、组件其他补充1.全局组件和局部组件2.父组件和子组件3.组件的语法糖写法4.组件 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 面试中2次被问到过这个知识点,实际开发中,应用事件委托也比较常见。JS中事件委托的实现主要依赖于事件冒泡。那什么是事件冒泡?就是事件从最深 ... [详细]
  • 在分页时,我想让点过的页码变色.应该怎么做?比如:12345我点2跳到第2页然后2变成红色其他为蓝色 ... [详细]
author-avatar
甄之恋
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有