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 la page est extrêmement complexe après avoir fonctionné pendant une période de temps. 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 throw new Error() que nous pouvons certainement capturer si nous voulons capturer, car nous savons très bien où throw 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. Pour le premier, nous pouvons également l'attraper grâce à try-catch , pour le second, nous devons écouter des exceptions mondiales, puis l'attraper.
Si certaines API du navigateur sont connues pour lancer des exceptions, nous devons mettre l'appel dans try-catch 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.
try {localStorage.SetItem ('Date', date.Now ());
} catch (erreur) {
reporterror (erreur);
}
Un autre scénario applicable try-catch 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 try-catch .
listeners.forEach(function(listener) {essayer {
auditeur();
} catch (erreur) {
reporterror (erreur);
}
});
Pour les endroits que try-catch ne peut pas couvrir, si une exception se produit, elle ne peut être attrapée que par window.onerror .
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.onerror l'implémentation est standard. Étant donné que le projet standard définit également window.onerror , nous avons juste besoin d'utiliser window.onerror .
Supposons que nous ayons une fonction reportError pour collecter des exceptions capturées, puis les envoyer à un stockage côté serveur par lots pour la requête et l'analyse, quelles informations voulons-nous collecter? Des informations plus utiles incluent: type d'erreur ( name ), message d'erreur ( message ), adresse de fichier de script ( script ), numéro de ligne ( line ), numéro de colonne ( column ) et trace de pile ( stack ). Si une exception est capturée par try-catch , toutes ces informations sont sur l'objet Error (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.
Si l'objet Error est créé par nous-mêmes, error.message est contrôlé par nous. Fondamentalement, ce que nous mettons dans error.message , quel sera le premier paramètre ( message ) de window.onerror . (Le navigateur sera en fait légèrement modifié, comme l'ajout 'Uncaught Error: ' Préfixe.) Par conséquent, nous pouvons sérialiser les attributs qui nous préoccupent (comme JSON.Stringify ) et les stocker dans error.message , puis lire eux dans window.onerror Bien sûr, cela se limite aux objets Error que nous créons nous-mêmes.
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'intégralité de l'objet Error dans le cinquième paramètre, et vous pouvez lire tous les attributs, y compris les attributs personnalisés. En conséquence, Chrome se déplace plus rapidement, une nouvelle window.onerror La signature de l'onserror est implémentée dans Chrome 30, ce qui entraîne la rédaction suivante du projet standard.
Régularité des attributswindow.onerror = function(ErrorMessage,
scripturi,
Livre de linge,
ColumnNumber,
erreur
) {
if (error) {
reporterror (erreur);
} autre {
reporterror ({
Message: errorMessage,
script: scripturi,
Ligne: Linen Dumber,
colonne: colonne
});
}
}
Les noms des attributs d'objet Error Error script avons discuté filename sont basés sur des méthodes de dénomination chromées. Par conséquent, nous avons également besoin d'une fonction spéciale pour normaliser l'objet Error , 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 stack similaire est similaire. Cette propriété enregistre les informations de pile d'une exception lorsqu'elle se produit sous la forme de texte brut. Nom ( identifier ), fichier ( script ), numéro de ligne ( line ) et numéro de colonne ( column ).
Si vous avez également rencontré une erreur avec la 'Script error.' 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 script.src 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 'Script error.'
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, ce qui entraîne des informations d'exception à Chrome et Firefox comme 'Script error.'
Si vous souhaitez contourner cette restriction, assurez-vous simplement que le fichier de script et la page eux-mêmes sont les mêmes. Mais la mise en place de fichiers de script sur des serveurs qui ne sont pas accélérés par CDN ne réduiraient-ils pas 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 <script> 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:
<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 la bonne commande de notre propre ordre. De plus, n'oubliez pas que Step2.
Si nous avons déjà un ensemble complet d'outils pour générer des balises <script> pour différentes pages sur le site Web, nous devons ajuster cet ensemble d'outils pour apporter des modifications aux balises <script> :
<script>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 de scheduleRemoteScript et scheduleInlineScript , et nous assurer qu'elles sont définies avant la première balise <script> qui fait référence au fichier de script externe, puis les balises <script> 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 code plus grande. La fonction 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 il 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 scheduleRemoteScript en fonction de l'adresse et du code directement obtenu par scheduleInlineScript peuvent être exécutés un par un dans le bon ordre. Je ne donnerai pas le code détaillé ici.
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 ils sont tous des codes intégrés dans la page, plusieurs balises <script> ne peuvent <script> être distinguées par error.script . .
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 <script> ne se chevauchent pas. Par exemple, en supposant que le code réel dans chaque balise <script> ne dépasse pas 1000 lignes, alors je peux laisser le code dans la première balise <script> reprendre la ligne 11000 et laisser le deuxième <script> marquer le code dans le code occupe la ligne 10012000 (1000 ligne vide insérée avant l'insertion), le code de la troisième balise <script> occupe la ligne 20013000 (ligne vide 2000 insérée avant l'insertion), etc. Ensuite, nous utilisons l'attribut data-* pour enregistrer ces informations pour une vérification simple.
<scriptdata-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 error.line d'erreur est 3005 , cela signifie que l' error.script réelle.script doit être 'http://cdn.com/step3.js' , tandis que l' error.line réelle.Leline doit être 5 .. Nous pouvons compléter ce numéro de ligne Vérification inverse de 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 1000 lignes à chaque balise <script> . 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 <script> ne se chevauchent pas.
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 à la balise <script> . É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 <script> s'applique également à la référence à différents fichiers d'image source pour les balises <img> . Si une balise <img> est une source différente, une fois qu'elle est utilisée lors du 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 <img> a résolu ce problème en introduisant l'attribut crossorigin . Si crossorigin="anonymous" est utilisé, il est équivalent aux CORS anonymes;
Puisque la balise <img> peut le faire, pourquoi la balise <script> ne peut-elle pas faire cela? Par conséquent, le fabricant du navigateur a ajouté le même attribut crossorigin à la balise <script> 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 renvoyer 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.
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. Bien que certains services tiers fournissent désormais des services Google Analytics qui captent des exceptions JavaScript, si vous souhaitez comprendre les détails et les principes, vous devez le faire vous-même.