ASP.net 3.5 SP1 : combiner les fichiers JavaScript grace au CompositeScript du ScriptManager (ex ScriptCombining du toolkitScriptManager)
ASP.net 3.5 SP1 apporte principalement 2 nouveautés pour le ScriptManager : la combinaison de script et l'historique coté client.
Qu'est-ce que la combinaison de script ?
Lorsque l'on utilise ASP.net Ajax, nous incluons de plus en plus de fichiers JavaScript dans notre page, il n'est pas rare d'avoir besoin d'une douzaine de fichiers JavaScript par page surtout lorsque l'on utilise les Ajax toolkits. Cela pose un problème de performance. En effet, télécharger pleins de petits fichiers est généralement plus coûteux que télécharger un seul gros fichier, il est donc préférable d'avoir un seul gros fichier qui regroupe les différents petits fichiers. Malheureusement travailler sur un seul gros fichier n'est pas des plus agréables, l'application sur laquelle je travaille actuellement contient plus de 60 000 lignes de JavaScript répartis dans une 60aine de fichiers... Il est donc nécessaire d'avoir un système permettant de regrouper les différents fichiers JavaScript lors de l'exécution de l'application. C'est exactement ce que fait le ScriptCombining du ScriptManager.
Comment cela se passe ? Prenons un exemple.
Ajoutons classiquement 4 fichiers JavaScript via notre ScriptManager :
<asp:ScriptManager runat="server">
<Scripts>
<asp:ScriptReference Path="~/file1.js" />
<asp:ScriptReference Path="~/file2.js" />
<asp:ScriptReference Path="~/file3.js" />
<asp:ScriptReference Path="~/file4.js" />
</Scripts>
</asp:ScriptManager>
Analysons le traffic réseau avec HttpWatch
On voit que notre page génère 8 requêtes :
- La 1ère pour la page elle même
- La 2ème pour les JavaScript propres à ASP.net 2.0
- La 3ème pour le coeur du framework ASP.net Ajax
- La 4ème pour la liaison ASP.net <=> ASP.net Ajax. On peut ne pas inclure ce fichier en mettant EnablePartialRendering à false au niveau du ScriptManager
- Les 4 autres pour nos différents fichiers JavaScript.
Pour regrouper les différents fichiers JavaScript avec le ScriptManager on va utiliser la propriété CompositeScript du ScriptManager à la place de la propriété Scripts :
<asp:ScriptManager runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference Path="~/file1.js" />
<asp:ScriptReference Path="~/file2.js" />
<asp:ScriptReference Path="~/file3.js" />
<asp:ScriptReference Path="~/file4.js" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
Après analyse on obtient :
On observe que l'on ne fait plus que 5 requêtes, la dernière correspond à la combinaison de nos différents fichiers JavaScript. On observe également que l'on a transféré un petit moins de données, cela vient du fait que l'on a 3 requêtes de moins et donc 3 headers HTTP en moins.
Peux t'on combiner nos fichiers JavaScript avec les fichiers inclus par ASP.net Ajax ? Oui ! Pour cela on utilise toujours le ScriptReference mais non pas avec la propriété Path mais Name.
<asp:ScriptManager ID="ScriptManager1" runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference name="MicrosoftAjax.js"/>
<asp:ScriptReference name="MicrosoftAjaxWebForms.js"/>
<asp:ScriptReference Path="~/file1.js" />
<asp:ScriptReference Path="~/file2.js" />
<asp:ScriptReference Path="~/file3.js" />
<asp:ScriptReference Path="~/file4.js" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
Plus que 3 requêtes ! Peux t'on diminuer à 2 requêtes ? Non ! Le premier fichier JavaScript, celui qui correspond au fonctionnement de ASP.net 2.0, n'est pas inclus via le ScriptManager, on ne peut donc pas le combiner. Pour que la combinaison de script fonctionne, il faut que le fichier soit inclus via un ScriptReference. A ma connaissance, on ne peut d'ailleurs même pas le supprimer.
Utilisation du ScriptCombining avec les fichiers JavaScript automatiquement inclus via des contrôles comme les toolkits.
Lorsque l'on inclut manuellement ces différents fichiers JavaScript, le ScriptCombining semble simple à utiliser mais peut-on s'en servir avec les fichiers JavaScript automatiquement inclut lorsque l'on utilise un control des toolkits ?
<asp:ScriptManager runat="server" />
<asp:TextBox ID="tbFirstName" runat="server" />
<ajaxToolkit:TextBoxWatermarkExtender runat="server"
TargetControlID="tbFirstName" WatermarkText="Type First Name Here" />

On voit ici qu'on a demandé 6 fichiers JavaScript, les 3 premiers correspondent à ASP.net et ASP.net Ajax alors que les 3 derniers correspondent aux différents fichiers JavaScript requis par le TextBoxWatermark
Pour combiner tous ces fichiers JavaScript, il faut les rajouter dans le ScriptManager via la propriété CompositeScript :
<asp:ScriptManager runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference name="MicrosoftAjax.js"/>
<asp:ScriptReference name="MicrosoftAjaxWebForms.js"/>
<asp:ScriptReference name="AjaxControlToolkit.Common.Common.js"
assembly="AjaxControlToolkit"/>
<asp:ScriptReference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js"
assembly="AjaxControlToolkit"/>
<asp:ScriptReference name="AjaxControlToolkit.TextboxWatermark.TextboxWatermark.js"
assembly="AjaxControlToolkit"/>
</Scripts>
</CompositeScript>
</asp:ScriptManager>
Ainsi nous n'avons plus que 2 fichiers JavaScript.
Comment connaitre les différents fichiers à inclure ?
Pour cela l'équipe ASP.net a mis en place un contrôle disponible sur Codeplex nous permettant de facilement analyser une page afin de connaître les différents fichiers JavaScript à ajouter, il s'agit du ScriptReferenceProfiler disponible à cette adresse : http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=13356
Pour l'utiliser, il faut mettre l'assembly (le fichier ScriptReferenceProfiler.dll) dans le dossier /bin de votre site web puis :
<%@ Register TagPrefix="srp" Assembly="ScriptReferenceProfiler"
Namespace="ScriptReferenceProfiler" %>
...
<srp:ScriptReferenceProfiler runat="server" />
Lorsque vous afficherez la page, vous aurez alors la liste des différents fichiers JavaScript à rajouter :
On voit que le ScriptReferenceProfiler est un contrôle bien sympathique. Bertrand Leroy a mis en ligne une vidéo présentant le ScriptCombining : Using Script Combining to improve AJAX performance
Attention, il faut bien comprendre comment cela fonctionne, le CompositeScript va combiner plusieurs fichiers JavaScript en un seul puis le mettre en cache coté client. Il faut donc utiliser les mêmes combinaisons de ScriptReference entres toutes les pages. Sans cela nous allons regrouper plusieurs fois les mêmes fichiers JavaScript dans des urls différentes. Au final nous téléchargerons plusieurs fois le même fichier JavaScript mais combinés dans des urls différentes. Si vous utilisez mal le ScriptCombining vous risquez d'avoir des perfs moins bonnes qu'en ne l'utilisant pas.
Peux t'on encore optimiser le chargement des fichiers JavaScript ? Oui !
Par défaut le ScriptManager envoie tous les fichiers en gzip. Cela veut dire qu'au lieu d'envoyer le texte brut au client, ASP.net l'envoie compressé en gzip, le client se charge alors de le décompresser. On voit sur les différentes captures que cela nous fait gagner de nombreux octets. De plus le ScriptManager ajoute automatiquement les en-têtes HTTP pour mettre en cache côté client les différents fichiers JavaScript. Il s'agit d'une très bonne chose !
Malgré tout, on peut encore optimiser tout cela. Puisque nous sommes de bons développeurs nos fichiers JavaScript possèdent de nombreux commentaires et espace/saut de lignes inutiles, ces éléments prennent de la place et sont nullement nécessaire pour le client.
J'ai essayé différentes options mais le ScriptManager ne permet pas de supprimer automatiquement les différents éléments inutiles des fichiers JavaScript :
Bien sûr il existe de nombreux outils permettant de supprimer les commentaires inutiles : jsmin est celui que j'utilise.
Pourquoi le ScriptManager ne le fait pas automatiquement ? Aucune idée ! Il s'agit d'un énorme manque et c'est une fonctionnalité nécessaire ! A cause de ce manque je vais continuer à utiliser mon système de "Script Combining".
L'autre manque du CompositeScript est l'impossibilité de créer des groupes de regroupement. Imagions que vous ayez un site où toutes vos pages utilisent file1.js ainsi que file2.js, quelques une de vos pages utilisent en plus file3.js et file4.js. Il serait bon de pouvoir regrouper file1.js avec file2.js et file3.js avec file4.js dans 2 urls distincts, ce n'est malheureusement pas possible.
Pour moi la syntaxe logique serait de passer par le ScriptManagerProxy, ou alors modifier la propriété CompositeScript pour prendre en compte une List<List<ScriptReference>> et non pas une List<ScriptReference> comme c'est le cas actuellement.
Qu'en pensez-vous ?
Avez-vous des suppositions sur l'absence de compactage et de la création des groupes ? Un simple "oubli" de la part de Microsoft ?
Bref, cette nouveauté est une nouveauté nécessaire et bien pensée, sans compactage des fichiers JavaScript on ne peut malheureusement pas utiliser cette nouveauté !
Et vous ? Utilisez-vous déjà cette nouveauté ? Pensez-vous l'utiliser ? Que pensez-vous de cet ajout ?
Pour les curieux : j'utilise HttpWatch afin d'analyser le trafic réseau, il s'agit d'un plugin à IE qui permet d'indiquer de nombreuses choses ! Il a de loin détrôner Fiddler.