Voir le principe de l'événement (vous amener à l'analyse du point de vue du code source) (4)

Huihui Bar _ Nouveau 2022-05-14 12:04:37 阅读数:984

voirleprincipenementvous

Cet article a été impliqué dans「Le nouveau rite de création」Activités,Ensemble pour ouvrir la voie à la création de Nuggets.

Nous avons déjà présentéeventLe processus de compilation de(Cliquez ici pour sauter),Ensuite, nous analysons ce queVueLors de l'initialisation et de la mise à joureventComment l'intérieur de.

eventÉvénements personnalisés générés

VueMoyenneeventLes événements sont divisés en natifsDOMÉvénements et événements personnalisés,PrimitiveDOMGestion des événements(Cliquez ici pour sauter),Nous avons analysé la section précédente.Dans cette section, nous allons analyser les événements personnalisés.

Les événements personnalisés sont utilisés sur les noeuds de composants,Les événements définis sur les noeuds de composants peuvent être divisés en deux catégories:L'un est natifDOMÉvénements( Invue2.xLa version utilise Native sur le noeud composantDOML'événement doit être ajouténativeModificateur),Une autre catégorie est les événements personnalisés.

Nous allons maintenant analyser le processus de personnalisation des événements:

Créer un composantvnode

Créer une constructionvnode(Noeud virtuel)Sera exécutécreateComponentFonctions, Il y a la logique suivante :

export function createComponent ( Ctor: Class<Component> | Function | Object | void, data: ?VNodeData, context: Component, children: ?Array<VNode>, tag?: string ): VNode | Array<VNode> | void {
......
// extract listeners, since these needs to be treated as
// child component listeners instead of DOM listeners
// Les événements personnalisés sont assignés à listeners
const listeners = data.on
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
// native Événement assigné à data.on, De cette façon, la méthode Native suit directement la même logique que dans la section précédente 
data.on = data.nativeOn
......
// return a placeholder vnode
// Créer un substituantvnode
const name = Ctor.options.name || tag
// Quand un noeud virtuel est généré ,Oui.listenersQuand le paramètre passe
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
// Retourvnode
return vnode
}
Copier le Code

Créer un composantvnode Les événements personnalisés définis sur le noeud composant sont assignés à listenersVariables, Assigner également les événements natifs définis sur le noeud composant à data.onPropriétés,Voilà., L'événement natif du composant exécute la même logique que la section précédente pour générer l'événement natif .Et puis en créant le composantvnodeQuand,Oui.listeners( Les événements personnalisés sont mis en cache ) Comme septième argument (componentOptions)Valeur de l'attribut pour.

vnodeAprès la création, Lors de l'initialisation du composant ,Sera exécutéinitInternalComponentFonctions:

Initialisation des composants

initInternalComponent


export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
// Pour le constructeur de sous - ensembles options(Éléments de configuration)
const opts = vm.$options = Object.create(vm.constructor.options)
// ....
// Le septième paramètre du noeud que nous avons créé précédemment (componentOptions)
const vnodeComponentOptions = parentVnode.componentOptions
// Pour le constructeur de sous - ensembles _parentListeners Propriété pointant vers listeners(Component Custom Event)
opts._parentListeners = vnodeComponentOptions.listeners
// ...
}
Copier le Code

Après avoir effectué la construction de ces éléments de configuration , L'événement sous - composant est initialisé


export function initEvents (vm: Component) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
// Oui.listeners,Mise en œuvreupdateComponentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
Copier le Code

listenersNon vide,Mise en œuvreupdateComponentListenersFonctions:


let target: any
export function updateComponentListeners ( vm: Component, listeners: Object, oldListeners: ?Object ) {
// targetPointez vers l'Instance courante
target = vm
// Mise en œuvreupdateListeners
updateListeners(listeners, oldListeners || {}, add, remove, vm)
target = undefined
}
Copier le Code

C'est pareil ici updateListenersFonctions, Avec la section précédente Native DOM La génération de l'événement est la même , Mais avec l'original DOM Il y a plusieurs différences dans la génération d'événements ,Comme suitaddAvecremoveDéfinition de la fonction.

function add (event, fn, once) {
if (once) {
// S'il y aoncePropriétés,Mise en œuvre$onceMéthodes
target.$once(event, fn)
} else {
Sinon, exécutez$onMéthodes
target.$on(event, fn)
}
}
function remove (event, fn) {
// removeLa méthode est d'exécuter$offMéthodes
target.$off(event, fn)
}
Copier le Code

À propos de$once$on$off Les fonctions sont définies dans eventsMixinMoyenne:

export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
......
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
......
}
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
......
}
Vue.prototype.$emit = function (event: string): Component {
......
}
}
Copier le Code

$on

Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
// L'Instance courante est l'instance qui appelle la méthode 
const vm: Component = this
// SieventEst un tableau,Traverser le tableau,Exécution séquentielle$onFonctions
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$on(event[i], fn)
}
} else {
// Placez l'Instance courante _events La propriété est initialisée dans un tableau vide et push Fonctions actuellement ajoutées 
(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
}
Copier le Code

$on La logique est d'enregistrer la méthode courante dans l'Instance courante vm._eventsDans les propriétés.

$once

Vue.prototype.$once = function (event: string, fn: Function): Component {
// L'Instance courante est l'instance qui appelle la méthode 
const vm: Component = this
// DéfinitiononFonctions
function on () {
// Mise en œuvre$off Détruire l'événement actuel 
vm.$off(event, on)
// Exécuter la fonctionfn
fn.apply(vm, arguments)
}
// onDefn La propriété indique la fonction actuellement passée 
on.fn = fn
// Oui.onFonction stockée dansvm._eventsMoyenne
vm.$on(event, on)
return vm
}
Copier le Code

$once La logique est de passer fnLa fonction est encapsulée, Une fonction interne a été générée on,on.fn Propriété pointant vers la fonction entrante fn,Oui.on Fonction stockée dans l'Instance _events Dans l'objet de propriété , Après avoir exécuté cette fonction une fois , La fonction est détruite .

$off

Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
// L'Instance courante est l'instance qui appelle la méthode 
const vm: Component = this
// all
// Si aucun paramètre n'est passé,Oui.vm._eventsDéfinir à null
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
// eventSi c'est un tableau,Traverser le tableau,Appelez à tour de rôle$offFonctions
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$off(event[i], fn)
}
// Retour
return vm
}
// specific event
// Uniqueevent
const cbs = vm._events[event]
// cbsNon défini,Retour direct
if (!cbs) {
return vm
}
// fnNon défini(Non passéfnDans le cas de),vm._events[event]Assigner NULL,Retour direct
if (!fn) {
vm._events[event] = null
return vm
}
// fnDéfini
if (fn) {
// specific handler
let cb
let i = cbs.length
// TraverséecbsObjet
while (i--) {
cb = cbs[i]
// Si vous trouvez des propriétés avec fnMême chose.
if (cb === fn || cb.fn === fn) {
// Supprimer la propriété,Sortir de la boucle
cbs.splice(i, 1)
break
}
}
}
return vm
}
Copier le Code

$off Le but est d'enlever vm._events Fonction d'événement définie sur l'objet .

eventsMixin Une fonction est également définie dans $emit, Souvent utilisé lors de la communication des composants :

$emit

Vue.prototype.$emit = function (event: string): Component {
// L'Instance courante est l'instance qui appelle la méthode 
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
// Prends ça.vm._eventsDeevent Toutes les fonctions de l'événement 
let cbs = vm._events[event]
// Existecbs
if (cbs) {
// cbsTransformation
cbs = cbs.length > 1 ? toArray(cbs) : cbs
// D'autres paramètres sont convertis en tableaux 
const args = toArray(arguments, 1)
// Traverséecbs, Exécuter les fonctions dans l'ordre 
for (let i = 0, l = cbs.length; i < l; i++) {
try {
cbs[i].apply(vm, args)
} catch (e) {
handleError(e, vm, `event handler for "${event}"`)
}
}
}
return vm
}
Copier le Code

D'après le code source,Dans notre processus habituel de développement, En fait, ça semble passer $emit La méthode appelle une fonction sur le composant parent , Essentiellement, les fonctions définies sur l'instance du composant lui - même sont appelées , Et cette fonction est passée dans l'élément de configuration du sous - composant pendant la génération du composant .

Un autre point mérite d'être mentionné , Appels d'événements pour les événements personnalisés des composants , En fait, c'est l'implémentation d'un centre d'événements très classique .Et nous sommesVue Souvent utilisé dans le processus de développement eventBusRéalisation, Le principe est le même que ci - dessus .

C'est fini.,À propos deVueDeevent Le principe est presque terminé ,Bienvenue à la discussion d'échange.

Copyright:Cet article est[Huihui Bar _ Nouveau]Établi,Veuillez apporter le lien original pour réimprimer,remercier。 https://fra.fheadline.com/2022/134/202205141158167017.html