Maison > interface Web > js tutoriel > le corps du texte

Une brève discussion sur le « principe de compilation » de Sizzle_Autres

WBOY
Libérer: 2016-05-16 16:04:20
original
807 Les gens l'ont consulté

Sizzle est un moteur de sélection DOM écrit par l'auteur de jQuery John Resig. Sa vitesse est connue comme la première du secteur. En tant que nouveau moteur de sélection indépendant, il est apparu après la version 1.3 de jQuery et a été adopté comme projet open source par John Resig. Sizzle est une partie indépendante et ne dépend d'aucune bibliothèque. Si vous ne souhaitez pas utiliser jQuery, vous pouvez simplement utiliser Sizzle, ou il peut être utilisé dans d'autres frameworks tels que Mool, Dojo, YUI, etc.

Il y a quelques jours, je préparais un PPT de partage sur jQuery, et j'ai demandé à mes collègues s'ils voulaient savoir autre chose sur jQuery à part comment l'utiliser. Quelqu'un a mentionné qu'il voulait savoir comment son sélecteur est implémenté. , et certaines personnes ont mentionné comment la vitesse de requête de jQuery se compare à celle d'autres frameworks. Concernant la vitesse, vous pouvez télécharger des exemples de tests sur le site officiel de sizzle. La vitesse est en effet très avantageuse. Mais la raison pour laquelle sa vitesse de fonctionnement est si efficace est liée au principe de mise en œuvre dont je souhaite discuter ici.

Avant de comprendre Sizzle, vous devez d'abord comprendre ce qu'est son sélecteur. Voici un exemple simple. Les étudiants qui connaissent jQuery doivent également être familiers avec ce format de sélecteur :

Copier le code Le code est le suivant :

tag #id .class , a:first

Il filtre essentiellement en profondeur de gauche à droite pour trouver les éléments DOM correspondants. Cette instruction n'est pas trop compliquée. Il n’est pas difficile d’implémenter nous-mêmes cette instruction de requête. Cependant, les instructions de requête n'ont que des règles de base et aucun nombre ni ordre fixe de sélecteurs. Comment pouvons-nous nous adapter à cet arrangement et à cette combinaison arbitraires lorsque nous écrivons notre propre code ? Sizzle peut réaliser une analyse et une exécution normales de diverses situations.

Le code source de Sizzle est en effet compliqué et difficile à comprendre ses idées. Laissons de côté les couches extérieures de l'emballage et examinons directement les trois méthodes qui, selon moi, sont au cœur de toute la mise en œuvre :

La première méthode de base. Il y a une fonction tokenize à la ligne 1052 du code source :

Copier le code Le code est le suivant :

function tokenize(sélecteur, parseOnly) { }

Le deuxième paramètre parseOnly est faux, ce qui signifie que seule l'opération de sérialisation du jeton est effectuée et qu'aucun résultat n'est renvoyé. Dans ce cas, le résultat de la sérialisation sera mis en cache pour une utilisation ultérieure. Le sélecteur est l'instruction de requête.

Après avoir été traité par cette fonction, par exemple, si selector="#idtag.class, a:first" est transmis, vous pouvez obtenir un résultat similaire au format suivant :

[
[
{matches:" id ",type:"ID"},
{matches:" tag ",type:"TAG"},
{matches:" class ",type:"CLASS"},
...
],
[
    {matches:" a",type:"TAG"},
    ...
],
[…],
…
]
Copier après la connexion


En voyant le nom de la fonction tokenize et sa fonction, je peux facilement penser au mot « principe de compilation ». C'est un peu comme l'analyse lexicale ici, mais cette analyse lexicale est plus simple que l'analyse lexicale effectuée lors de la compilation du programme.

La méthode tokenize effectuera une "segmentation de mots" basée sur l'expression régulière de virgules, d'espaces et de sélecteurs relationnels dans le sélecteur, et obtiendra un tableau bidimensionnel (permettez-moi d'utiliser ce nom inexact), dans lequel le premier -tableau dimensionnel Ils sont séparés par des virgules et sont appelés groupes dans le code source.

Regardons à nouveau le code source. Il y a une définition de Expr = Sizzle.selectors = {} à partir de la ligne 405. Il y a une définition de filtre à la ligne 567. Ici nous pouvons trouver les types de filtrage de base : "ID ", "TAG", "CLASS", "ATTR", "CHILD", "PSEUDO", tels sont les types finalement classés par tokenize.

Une fois la "segmentation des mots" terminée, regardez toujours le Expr= Sizzle.selectors = {} défini à la ligne 405. Tous les sélecteurs que nous connaissons se trouvent ici, et chaque sélecteur correspond à une définition de méthode. À ce stade, vous devriez vous demander si Sizzle effectue réellement une « segmentation de mots » sur le sélecteur, le décompose, puis trouve les méthodes correspondantes dans Expr pour effectuer des opérations de requête ou de filtrage spécifiques

 ?

La réponse est fondamentalement oui. Mais Sizzle a une approche plus spécifique et plus intelligente. Regardons la deuxième méthode qui me semble très essentielle :

Il y a une fonction matcherFromTokens à la ligne 1293 du code source :

Copier le code Le code est le suivant :

function matcherFromTokens(jetons) { }

Les paramètres transmis sont obtenus à partir de la méthode tokenize. Matcher peut être compris comme « programme de correspondance ». Littéralement, la fonction de cette fonction est de générer un programme de correspondance via des jetons. En fait, c'est le cas. En raison du manque d'espace, cet article ne partagera que certains des principes d'implémentation de Sizzle que je comprends et ne publiera pas le code source. Quand j'aurai le temps plus tard, je pourrai compiler un article d'analyse du code source plus détaillé.

La méthode matcherFromTokens confirme l'hypothèse précédente. Elle agit comme une connexion et un lien entre le sélecteur "segmentation de mots" et la méthode de correspondance définie dans Expr. On peut dire que diverses permutations et combinaisons de sélecteurs sont adaptables. La chose intelligente à propos de Sizzle est qu'il ne fait pas directement correspondre les résultats de "segmentation de mots" obtenus avec les méthodes de Expr une par une et ne les exécute pas une par une. Au lieu de cela, il combine d'abord une grande méthode de correspondance selon les règles et l'exécute. dans la dernière étape. Mais comment l'exécuter après la combinaison dépend de la troisième méthode clé :

Il existe une méthode superMatcher à la ligne 1350 du code source :

Copier le code Le code est le suivant :

superMatcher = function (graine, contexte, xml, résultats, expandContext) { }

Cette méthode n'est pas une méthode directement définie, mais est renvoyée via la méthode matcherFromGroupMatchers(elementMatchers, setMatchers) à la ligne 1345, mais elle joue un rôle important dans l'exécution finale.

La méthode superMatcher déterminera une plage de requête de départ en fonction des paramètres seed, expandContext et context. Il peut s'agir d'une requête et d'un filtre directement à partir de la graine, ou elle peut être dans le contexte ou la plage de nœuds parent du contexte. S'il ne démarre pas à partir de la graine, il exécutera d'abord le code Expr.find["TAG"]( "*", expandContext && context.parentNode || context ) et attendra une collection d'éléments (tableau). Effectuez ensuite un parcours des éléments et utilisez la méthode matcher pré-générée pour faire correspondre les éléments un par un. Si le résultat est vrai, les éléments sont directement empilés dans le jeu de résultats renvoyé.

D'accord, voyant que les résultats originaux de la méthode matcher ici sont tous des valeurs booléennes, revenons à la ligne 405 et jetons un œil aux méthodes incluses dans le filtre dans Expr. Elles renvoient toutes des valeurs booléennes. Y compris davantage de méthodes de pseudo-classe correspondant à PSEUDO (pseudo-classe) sont les mêmes. Cela semble un peu subversif par rapport à mon idée originale. Il s'avère qu'il ne s'agit pas de vérifier couche par couche, mais c'est un peu comme revenir en arrière pour faire la correspondance et le filtrage. Dans Expr, seuls les ensembles de résultats find et preFilter sont renvoyés.

Bien que j'ai encore quelques doutes sur la raison pour laquelle il utilise des méthodes de correspondance et de filtrage une par une pour obtenir l'ensemble de résultats, je pense que le "principe de compilation" le plus basique de Sizzle aurait dû être clairement expliqué.

Mais on ne peut laisser aucun doute, continuons. En fait, cet article lui-même semble avoir été écrit à l’envers. Les étudiants qui souhaitent examiner le code source ne verront pas ces trois méthodes clés au début. En fait, Sizzle effectue également une série d’autres tâches avant d’entrer dans ces trois méthodes.

On peut dire que la véritable entrée de Sizzle se trouve à la ligne 220 du code source :

Copier le code Le code est le suivant :

function Sizzle (sélecteur, contexte, résultats, graine){ }

Le premier paragraphe de cette méthode est relativement facile à comprendre. Si le sélecteur peut être associé à un seul sélecteur d'ID (#id), alors l'élément peut être trouvé directement à l'aide de la méthode context.getElementById(m) basée sur le identifiant. Si le sélecteur peut être associé à un seul sélecteur TAG, utilisez d’abord la méthode context.getElementsByTagName(selector) pour rechercher les éléments pertinents. Si le navigateur actuel utilise uniquement getElementsByClassName natif et que le sélecteur correspondant est un seul sélecteur CLASS, la méthode context.getElementsByClassName(m) sera également utilisée pour rechercher les éléments pertinents. Ces trois méthodes sont toutes des méthodes natives prises en charge par les navigateurs, et leur efficacité d'exécution est certainement la plus élevée.

Si les méthodes les plus élémentaires ne sont pas disponibles, vous entrerez dans la méthode de sélection. La ligne 1480 du code source a sa définition :

Copier le code Le code est le suivant :

function select (sélecteur, contexte, résultats, graine, xml) { }

Dans la méthode select, l'opération de "segmentation de mots" que nous avons mentionnée plus tôt sera d'abord effectuée sur le sélecteur. Cependant, après cette opération, nous n'avons pas directement commencé à assembler la méthode de correspondance, mais avons d'abord effectué quelques opérations de recherche. L'opération de recherche ici peut correspondre à la recherche dans Expr. Elle effectue une opération de requête et renvoie un ensemble de résultats.

On comprend que select utilise le sélecteur obtenu par "segmentation de mots" pour connaître d'abord l'ensemble de résultats qui peut être recherché à l'aide de la méthode find en fonction de son type. Lors de l'opération de recherche, l'ensemble de résultats est réduit de gauche à droite dans l'ordre des sélecteurs. Si après un parcours, tous les sélecteurs du sélecteur peuvent effectuer l'opération de recherche, le résultat sera renvoyé directement. Sinon, il entrera dans le processus de « compilation » et de filtrage décrit précédemment.

À ce stade, vous pouvez également venir et comprendre essentiellement le flux de travail de Sizzle. Les questions laissées ci-dessus ne sont en fait plus des questions à ce stade, car lors de l'exécution d'un filtrage par correspondance inverse, sa plage de recherche est déjà le plus petit ensemble filtré couche par couche. La méthode de filtrage par correspondance inverse est en fait un choix efficace pour les sélecteurs auxquels elle correspond, comme les pseudo-classes.

Résumons brièvement pourquoi Sizzle est si efficace.

Tout d'abord, En termes de flux de traitement, il utilise toujours en premier la méthode native la plus efficace pour le traitement. Ce qui a été introduit auparavant n'est que la propre méthode d'implémentation du sélecteur de Sizzle. Lorsque Sizzle est réellement exécuté, il déterminera d'abord si le navigateur actuel prend en charge la méthode native querySelectorAll (ligne de code source 1545). Si elle est prise en charge, cette méthode sera utilisée en premier. La méthode nativement prise en charge par le navigateur est nettement plus efficace que la méthode écrite par le propre js de Sizzle. Son utilisation en premier peut également garantir une efficacité de travail plus élevée de Sizzle. (Vous pouvez consulter plus d'informations en ligne sur querySelectorAll). Lorsque la méthode querySelectorAll n'est pas prise en charge, Sizzle donne également la priorité à la détermination s'il peut utiliser directement getElementById, getElementsByTag, getElementsByClassName et d'autres méthodes pour résoudre le problème.

Deuxièmement, dans des situations relativement complexes, Sizzle choisit toujours d'utiliser d'abord des méthodes natives pour interroger et sélectionner autant que possible afin de réduire la plage de sélection, puis d'utiliser le « principe de compilation » introduit précédemment pour restreindre la plage de sélection. Les éléments sont appariés un par un et filtrés. Le flux de travail jusqu'à l'étape de « compilation » est un peu compliqué, et l'efficacité sera certainement légèrement inférieure à celle des méthodes précédentes, mais Sizzle essaie d'utiliser ces méthodes le moins possible, et en même temps, il essaie également de rendre les ensembles de résultats traités par ces méthodes aussi petits et simples que possible, afin d'obtenir une plus grande efficacité.

Encore une fois , même après être entré dans ce processus de "compilation", Sizzle a également implémenté un mécanisme de mise en cache que nous avons temporairement ignoré et que nous n'avons pas introduit afin d'expliquer le processus en premier. La ligne 1535 du code source est ce que nous appelons l'entrée « compilation », ce qui signifie qu'elle appelle la troisième méthode principale superMatcher. Tracez et voyez la ligne 1466. La méthode de compilation met en cache la fonction correspondante générée en fonction du sélecteur. Non seulement cela, mais regardez la méthode tokenize à la ligne 1052. Elle met en cache les résultats de la segmentation des mots en fonction du sélecteur. En d'autres termes, après avoir exécuté la méthode Sizzle (sélecteur) une fois et appelé la méthode Sizzle (sélecteur) directement la prochaine fois, le processus de "compilation" le plus gourmand en performances à l'intérieur ne consommera pas trop de performances et le cache précédent le fera. être directement récupéré. Je pense que l'un des plus grands avantages de la soi-disant « compilation » est peut-être qu'elle facilite la mise en cache. La soi-disant « compilation » ici peut également être comprise comme la génération de fonctions prétraitées et leur stockage pour une utilisation ultérieure.

À ce stade, j'espère pouvoir répondre essentiellement aux questions des étudiants qui étaient auparavant préoccupés par les principes de mise en œuvre et l'efficacité d'exécution des sélecteurs. De plus, la conclusion de l'analyse de cet article est dérivée du code source de la dernière version de Sizzle. Les numéros de lignes de code mentionnés dans l'article sont soumis au code source de cette version, qui peut être téléchargé depuis http : //sizzlejs.com/. Le temps presse, alors soyez indulgent s'il y a des imperfections dans l'analyse. Les étudiants qui ont encore des questions sont invités à continuer de communiquer hors ligne.

Ce qui précède représente l’intégralité du contenu de cet article, j’espère que vous l’aimerez tous.

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal