Les ingénieurs frontaux savent tous que JavaScript possède des capacités de gestion d'exception de base. Nous pouvons lancer une nouvelle erreur (), et le navigateur lèvera également une exception lorsque nous appelons l'erreur de l'API. Mais on estime que la plupart des ingénieurs frontaux n'ont jamais envisagé de collecter ces informations d'exception
Quoi qu'il en soit, tant que le rafraîchissement ne se reproduit pas après une erreur JavaScript, l'utilisateur peut résoudre le problème en rafraîchissant, et le navigateur ne se bloquera pas, et ce sera bien si cela ne s'est pas produit. Cette hypothèse était vraie avant que l'application à page unique ne devienne populaire. L'application actuelle de page unique est extrêmement complexe après avoir fonctionné pendant un certain temps. Les utilisateurs ont peut-être effectué plusieurs opérations d'entrée avant leur arrivée ici. Comment peuvent-ils actualiser s'ils disent qu'ils veulent? Ne retravougeriez-vous pas complètement les opérations précédentes? Il nous est donc toujours nécessaire de capturer et d'analyser ces informations d'exception, puis nous pouvons modifier le code pour éviter d'affecter l'expérience utilisateur.
Comment prendre des exceptions
Nous nous sommes écrits lancer une nouvelle erreur (). Si nous voulons capturer, nous pouvons certainement le capturer parce que nous savons très bien où le lancer est écrit. Cependant, les exceptions qui se produisent lors de l'appel de l'API du navigateur ne sont pas nécessairement si faciles à attraper. Certaines API disent que des exceptions seront lancées dans la norme, et certaines API ne disposent que de navigateurs individuels qui lancent des exceptions en raison des différences de mise en œuvre ou des défauts. Pour le premier, nous pouvons également l'attraper grâce à Try-Catch, pour le second, nous devons écouter des exceptions mondiales, puis l'attraper.
coup de passe
Si certaines API du navigateur sont connues pour lancer des exceptions, nous devons mettre l'appel dans un coup de main pour éviter que l'ensemble du programme entre dans un état illégal en raison d'erreurs. Par exemple, Window.Localstorage est une telle API. Une exception sera lancée après la rédaction de données dépasse la limite de capacité, et cela sera également vrai dans le mode de navigation privé de Safari.
La copie de code est la suivante:
essayer {
localStorage.SetItem ('Date', date.Now ());
} catch (erreur) {
reporterror (erreur);
}
Un autre scénario applicable de couple d'essai courant est les rappels. Parce que le code de la fonction de rappel est incontrôlable, nous ne savons pas à quel point le code est bon et si d'autres API qui lancent des exceptions seront appelées. Afin de ne pas faire exécuter d'autres codes après avoir appelé le rappel en raison d'erreurs de rappel, il est nécessaire de remettre le rappel dans un coup de main.
La copie de code est la suivante:
auditeurs.ForEach (fonction (auditeur) {
essayer {
auditeur();
} catch (erreur) {
reporterror (erreur);
}
});
fenêtre.
Pour les endroits que TRY-Catch ne peut pas couvrir, si une exception se produit, elle ne peut être capturée que via Window.onerror.
La copie de code est la suivante:
window.onerror =
fonction (errorMessage, scripturi, linenumber) {
reporterror ({
Message: errorMessage,
script: scripturi,
Ligne: Livreau en line
});
}
Faites attention à ne pas être intelligent et à utiliser Window.AddeventListener ou Window.Attachevent pour écouter Window.onerror. De nombreux navigateurs implémentent uniquement Window.onerror, ou uniquement Window.IlError l'implémentation est standard. Étant donné que le projet standard définit également Window.onerror, nous avons juste besoin d'utiliser Window.onerror.
Propriété manquante
Supposons que nous ayons une fonction Reporterror pour collecter des exceptions capturées, puis les envoyer par lots au stockage côté serveur pour la requête et l'analyse, quelles informations voulons-nous collecter? Des informations plus utiles incluent: type d'erreur (nom), message d'erreur (message), adresse de fichier de script (script), numéro de ligne (ligne), numéro de colonne (colonne) et trace de pile. Si une exception est capturée par l'essai, toutes ces informations sont sur l'objet d'erreur (pris en charge par les navigateurs grand public), donc Reporterror peut également collecter ces informations. Mais s'il est capturé via Window.onerror, nous savons tous que cette fonction d'événement n'a que 3 paramètres, de sorte que les informations inattendues de ces 3 paramètres sont perdues.
Sérialiser les messages
Si l'objet d'erreur est créé par nous-mêmes, Error.Message est contrôlé par nous. Fondamentalement, ce que nous mettons dans l'erreur.Message, et quel sera le premier paramètre (message) de Window.onerror. (Le navigateur sera en fait légèrement modifié, comme l'ajout de l'erreur non apprise: 'Prefix.) Par conséquent, nous pouvons sérialiser les attributs qui nous préoccupent (comme JSON.Stringify) et les stocker dans Error.Message, puis les lire dans Window.onerror pour les désérialiser. Bien sûr, cela est limité à l'objet d'erreur que nous avons créé nous-mêmes.
Le cinquième paramètre
Les fabricants de navigateurs connaissent également les restrictions auxquelles les gens sont soumis lors de l'utilisation de Window.onerror, afin qu'ils commencent à ajouter de nouveaux paramètres à Window.onerror. Étant donné que seuls les numéros de ligne et les numéros de colonne ne semblent être très symétriques, c'est-à-dire d'abord ajouté les numéros de colonne et les a placés dans le quatrième paramètre. Cependant, ce qui est plus préoccupé par tout le monde, c'est de savoir s'il peut obtenir la pile complète, alors Firefox a dit qu'il serait préférable de mettre la pile dans le cinquième paramètre. Mais Chrome a dit qu'il serait préférable de mettre l'ensemble de l'objet d'erreur dans le cinquième paramètre. Tout attribut que vous souhaitez lire, y compris les attributs personnalisés. En conséquence, parce que Chrome est plus rapide, une nouvelle fenêtre. La signature d'onserror a été implémentée dans Chrome 30, ce qui a entraîné la rédaction suivante du projet standard.
La copie de code est la suivante:
window.onerror = fonction (
ErrorMessage,
scripturi,
Livre de linge,
ColumnNumber,
erreur
) {
if (error) {
reporterror (erreur);
} autre {
reporterror ({
Message: errorMessage,
script: scripturi,
Ligne: Linen Dumber,
colonne: colonne
});
}
}
Régularité des attributs
Les noms des propriétés d'objet d'erreur dont nous avons discuté auparavant sont basées sur des méthodes de dénomination chromées. Cependant, différents navigateurs nomment les propriétés des objets d'erreur différemment. Par exemple, l'adresse du fichier de script est appelée script dans Chrome mais nom de fichier dans Firefox. Par conséquent, nous avons également besoin d'une fonction spéciale pour normaliser l'objet d'erreur, c'est-à-dire pour cartographier différents noms d'attribut à un nom d'attribut unifié. Pour des pratiques spécifiques, veuillez vous référer à cet article. Bien que la mise en œuvre du navigateur soit mise à jour, il ne sera pas trop difficile pour les humains de maintenir une telle table de cartographie.
Le format de trace de pile similaire est similaire. Cette propriété enregistre les informations de pile d'une exception lorsqu'elle se produit en texte brut. Étant donné que les formats de texte utilisés par chaque navigateur sont différents, il est également nécessaire de maintenir une expression régulière pour extraire le nom de la fonction (identifiant), le fichier (script), le numéro de ligne (ligne) et le numéro de colonne (colonne) de chaque trame à partir de texte brut.
Limitations de sécurité
Si vous avez également rencontré une erreur avec le message «Erreur de script». La raison de cette restriction de sécurité est la suivante: Supposons que le HTML renvoyé par un banquier en ligne après la connexion soit différent du HTML vu par un utilisateur anonyme, un site Web tiers peut mettre l'URI de cette banque en ligne dans l'attribut Script.src. Bien sûr, HTML ne peut pas être analysé comme JS, donc le navigateur lancera une exception, et ce site Web tiers peut déterminer si l'utilisateur est connecté en analysant l'emplacement de l'exception. Pour cette raison, le navigateur filtre toutes les exceptions lancées par différents fichiers de script source, ne laissant qu'un message inchangé comme «erreur de script»., Et tous les autres attributs disparaissent.
Pour les sites Web d'une certaine échelle, il est normal que les fichiers de script soient placés sur les CDN et différentes sources sont placées. Maintenant, même si vous créez vous-même un petit site Web, des frameworks communs tels que jQuery et Backbone peuvent directement référencer la version sur le CDN public pour accélérer les téléchargements des utilisateurs. Ainsi, cette restriction de sécurité cause des problèmes, provoquant des informations d'exception que nous collectons auprès de Chrome et de Firefox comme une «erreur de script» inutile. ».
Cors
Si vous souhaitez contourner cette restriction, assurez-vous simplement que le fichier de script et la page eux-mêmes sont de la même origine. Mais ne placerait-il pas des fichiers de script sur des serveurs qui ne sont pas accélérés par CDN pour ralentir la vitesse de téléchargement de l'utilisateur? Une solution consiste à continuer à placer le fichier de script sur le CDN, à utiliser XMLHTTPRequest pour télécharger le contenu via CORS, puis à créer une balise <cript> pour l'injecter dans la page. Le code intégré dans la page est bien sûr la même origine.
C'est simple à dire, mais il y a beaucoup de détails à mettre en œuvre. Pour donner un exemple simple:
La copie de code est la suivante:
<script src = "http://cdn.com/step1.js"> </ script>
<cript>
(fonction Step2 () {}) ();
</cript>
<script src = "http://cdn.com/step3.js"> </ script>
Nous savons tous que s'il y a des dépendances dans Step1, Step2 et Step3, elle doit être exécutée strictement dans cet ordre, sinon une erreur peut se produire. Le navigateur peut demander des fichiers Step1 et Step3 en parallèle, mais la commande est garantie lorsqu'elle est exécutée. Si nous obtenons le contenu du fichier de Step1 et Step3 en utilisant XMLHttpRequest, nous devons assurer l'ordre correct par nous-mêmes. De plus, n'oubliez pas Step2. L'étape2 peut être exécutée lorsque l'étape1 est téléchargée sous un formulaire non bloquant, nous devons donc également interférer avec Step2 et laisser attendre l'étape1 avant l'exécution.
Si nous avons déjà un ensemble complet d'outils pour générer des balises <cript> pour différentes pages sur le site Web, nous devons ajuster cet ensemble d'outils pour apporter des modifications aux balises <cript>:
La copie de code est la suivante:
<cript>
SchedUleMotescript ('http://cdn.com/step1.js');
</cript>
<cript>
ScheduleInlinescript (Code de fonction () {
(fonction Step2 () {}) ();
});
</cript>
<cript>
SchedUleMotescript ('http://cdn.com/step3.js');
</cript>
Nous devons implémenter les deux fonctions scheduleReMoScript et calendrierInlinescript, et nous assurer qu'elles sont définies avant la première balise <cript> qui fait référence au fichier de script externe, puis les balises <cript> restantes seront réécrites dans le formulaire ci-dessus. Notez que la fonction Step2 qui a été exécutée a été immédiatement placée dans une fonction de code plus grande. La fonction de code ne sera pas exécutée, ce n'est qu'un conteneur, afin que le code Step2 d'origine puisse être conservé sans s'échapper, mais ne sera pas exécuté immédiatement.
Ensuite, nous devons implémenter un mécanisme complet pour nous assurer que le contenu de fichier téléchargé par ScheduleReMoScript en fonction de l'adresse et du code directement obtenu par scheduleInscript peuvent être exécutés un par un dans le bon ordre. Je ne donnerai pas le code détaillé ici. Si vous êtes intéressé, vous pouvez le mettre en œuvre vous-même.
Vérification du numéro de ligne
Obtenir du contenu via CORS et injecter du code dans la page peut percer les restrictions de sécurité, mais il introduira un nouveau problème, c'est-à-dire les conflits de numéro de ligne. À l'origine, le fichier de script unique pourrait être situé via Error.Script, puis le numéro de ligne unique pourrait être situé via Error.line. Maintenant, comme tous les codes intégrés dans la page sont tous des codes, plusieurs balises <cript> ne peuvent pas être distinguées par error.script. Cependant, le numéro de ligne à l'intérieur de chaque balise <cript> est calculé à partir de 1, ce qui nous fait ne pas être en mesure d'utiliser des informations d'exception pour localiser l'emplacement du code source où l'erreur est située.
Pour éviter les conflits de numéro de ligne, nous pouvons gaspiller certains numéros de ligne afin que les intervalles de numéro de ligne utilisé par le code réel dans chaque balise <cript> ne se chevauchent pas. Par exemple, en supposant que le code réel de chaque balise <cript> ne dépasse pas 1000 lignes, alors je peux laisser le code dans la première galerie <cript> Occupy Line 11000, le code dans le deuxième <cript> balise Occupy Line 10012000 (1000 lignes vides ont été insérées avant), le code du troisième script <script> prend la ligne 20013000 (2000 lignes vides ont été insérées avant), et ainsi de suite. Ensuite, nous utilisons l'attribut Data- * pour enregistrer ces informations pour une vérification simple.
La copie de code est la suivante:
<script
data-src = "http://cdn.com/step1.js"
data-line-start = "1"
>
// code pour l'étape 1
</cript>
<script data-line-start = "1001">
// '/ n' * 1000
// code pour l'étape 2
</cript>
<script
data-src = "http://cdn.com/step3.js"
data-line-start = "2001"
>
// '/ n' * 2000
// code pour l'étape 3
</cript>
Après ce traitement, si une erreur d'erreur est 3005, cela signifie que l'erreur réelle.script doit être «http://cdn.com/step3.js», et l'erreur réelle. Ligne doit être 5. Nous pouvons compléter ce numéro de ligne de vérification inverse dans la fonction reporterror mentionnée précédemment.
Bien sûr, comme nous ne pouvons pas garantir que chaque fichier de script n'a que 1000 lignes, il est également possible que certains fichiers de script soient nettement moins de 1000 lignes, il n'est donc pas nécessaire d'allouer correctement un intervalle de 1000 ligne à chaque balise <cript>. Nous pouvons allouer des intervalles en fonction du nombre réel de lignes de script, nous assurer que les intervalles utilisés par chaque balise <cript> ne se chevauchent pas.
Attribut de crossorigin
Les restrictions de sécurité imposées par les navigateurs sur le contenu de différentes sources ne sont bien sûr pas limitées aux balises <cript>. Étant donné que XMLHttpRequest peut percer cette limitation par le biais de COR, pourquoi les ressources sont-elles directement référencées par le biais de balises non autorisées? C'est certainement OK.
La limitation de la référence à différents fichiers de script source pour les balises <cript> s'applique également à la référence à différents fichiers d'image source pour les balises <MG>. Si une balise <MG> est une source différente, une fois utilisée dans le dessin <Canvas>, le <Canvas> deviendra un état d'écriture uniquement, garantissant que le site Web ne peut pas voler des données d'image non autorisées à partir de différentes sources via JavaScript. Plus tard, la balise <MG> a résolu ce problème en introduisant l'attribut Crossorigin. Si Crossorigin = "Anonymous", il équivaut à des COR anonymes; Si Crossorigin = "Use-Credentials", il équivaut à un COR certifié.
Puisque la balise <Mg> peut le faire, pourquoi la balise <cript> ne peut-elle pas faire cela? Le fabricant du navigateur a donc ajouté le même attribut de crossorigin à la balise <cript> pour résoudre les restrictions de sécurité ci-dessus. Maintenant, le support Chrome et Firefox pour cette propriété est entièrement gratuit. Safari traitera Crossorigin = "Anonymous" comme Crossorigin = "Use-Credentials", et le résultat est que si le serveur ne prend en charge que les COR anonymes, Safari traitera l'authentification comme un échec. Étant donné que le serveur CDN est conçu pour retourner uniquement le contenu statique pour des raisons de performances, il est impossible de renvoyer dynamiquement l'en-tête HTTP requis pour authentifier les COR en fonction des demandes. Safari équivaut à ne pas pouvoir utiliser cette fonctionnalité pour résoudre le problème ci-dessus.
Résumer
La manipulation des exceptions JavaScript semble simple et n'est pas différente des autres langues, mais il n'est pas si facile d'attraper toutes les exceptions et d'analyser les propriétés. Maintenant, bien que certains services tiers fournissent des services de type Google Analytics qui captent des exceptions JavaScript, si vous souhaitez comprendre les détails et les principes, vous devez toujours le faire vous-même.