Le SharePoint Framework: dans la vraie vie, ça donne quoi?

Le SharePoint Framework: dans la vraie vie, ça donne quoi?


On dit souvent qu’entre la théorie et la pratique, il y a parfois quelques différences…L’objectif de cet article est de vous livrer, sur une base personnelle, un retour d’expérience sur l’utilisation du SharePoint Framework dans le contexte d’un véritable projet SharePoint Online/Office 365 et ainsi vous permettre d’éviter les pièges et écueils courants lors de la prise en main de cette nouvelle technologie.

J’ai eu l’occasion de développer avec le SharePoint Framework quasiment depuis sa sortie au mois de mars de cette année 2017 dans le cadre d’un projet d’intranet de publication utilisant les sites de communications. L’objectif était de développer plusieurs composants personnalisés pour enrichir le portail natif. Sans entrer dans les détails, voici les différentes notes que j’ai prises pendant cette période, classées selon différentes thématiques.

Pour information, le projet a été développé en utilisant la version  du SharePoint Framework (pas la dernière donc).

Environnement de développement

Commençons par l’environnement de développement et les outils utilisés:

  • Visual Studio Code: pour le développement de la solution. Je l’utilise principalement pour ses fonctionnalités de débogage avec SPFx.
  • Cmder: console Node.js pratique et simple. Elle permet de lancer plusieurs consoles en même temps.
  • Source Tree: le client Git de Atlassian gratuit et simple à utiliser. Pour les plus hardcores, vous pouvez toujours utiliser les lignes de commandes Git.
  • Office 365 « Developer tenant »: pour les développements uniquement et un par développeur idéalement.
  • PowerShell ISE: pour l’exécution de scripts de déploiement et cmdlets PnP.

Débogage de la solution

Pour déboguer les différents composants SPFx (Web Parts et extensions), j’utilise principalement les outils suivants:

  • Visual Studio Code: une configuration prédéfinie du fichier launch.json pour le débogage SPFx peut être récupérée directement sur la documentation officielle. Utile lorsque l’on ne souhaite pas déboguer directement dans le navigateur. Attention cependant: selon la profondeur de dossiers et de fichiers de votre solution, vous aurez à refléter cette structure dans le fichier launch.json dans la section « sourceMapPathOverrides » au risque de ne jamais voir vos points d’arrêts exécutés:

Développement en général

Je liste ici mes commentaires généraux concernant l’expérience de développement. Ils ne portent pas forcément que sur le SharePoint Framework:

Packages npm

Éviter les packages npm « douteux ». Par douteux, j’entends ceux qui ne sont pas très populaires dans la communauté. Par exemple, un faible nombre de téléchargements est déjà une bonne indication. Même si ils peuvent se révéler très utiles, ils peuvent parfois causer de l’instabilité dans votre application et il y a fort à parier qu’en cas de problème, peu de personnes pourront vous répondre faute d’une maintenance régulière. En règle générale, lorsque vous développez en JavaScript, essayez de ne pas avoir la « packagite aigue » en ayant recours systématiquement à des libraires tierces pour résoudre vos problèmes. Dans la plupart des cas, utiliser les mécanismes de base JavaScript suffit, même si cela demande un peu plus de travail.

De plus, utiliser beaucoup de packages peut, si ils ne sont pas externes, faire augmenter considérablement la taille du bundle final et ainsi impacter les performances (comme par exemple lodash).

Développement avec React

Possédant davantage d’expérience avec Knockout.js que React, j’ai dû m’adapter à cette nouvelle manière de développer. Voici les erreurs les plus courantes que j’ai commises lors de mes premières implémentations ainsi que les méthodes que j’utilise maintenant couramment:

Oubli de la « key » dans une boucle: dans une boucle, oublier d’ajouter une clé unique sur chacun des éléments causant ainsi des effets de bord (incohérence de données par exemple):

Récupération de données (appels REST par exemple) à de multiples endroits dans le code: par exemple, accéder aux données dans un composant « enfant » d’une hiérarchie de composants. Cette pratique rend en effet plus complexe la lecture du code (pour vous et les autres développeurs) et ne permet surtout pas de « mocker » facilement les objets à des fins de tests en fonction de l’environnement courant:

À la place, j’utilise toujours la même structure pour développer mes composants: le « container components pattern« . Le principe est simple: un composant « conteneur » se charge seul de la manipulation des données (ajout, mise à jour et suppression) et transmet les résultats dans les composants « enfants » qui se contentent simplement de les afficher (via les « props« ). Ainsi, l’initialisation des classes permettant la récupération des données se fait toujours dans la méthode onInit() dans la classe de base de la hiérarchie de composants (celle héritant de BaseClientSideWebPart). De même, l’échange de données entre les composants « enfants » et le conteneur se fait grâce à des méthodes (callbacks) passées en paramètres. Un exemple concret d’implémentation de cette méthode peut être consulté dans l’exemple react-search-refiners, lui-même inspiré d’un autre exemple: react-todo-basic.

Mettre à jour les valeurs de l’état pour des valeurs de type « tableau » ou « objet »: pour rappel, lorsque vous développez avec React, la notion d’état (« state » en anglais) et sa manipulation est essentielle. En effet, une des premières règles que vous apprenez est que celui-ci est non-mutable, c’est à dire que vous ne pouvez pas changer sa valeur directement via une simple assignation (par exemple this.state.myValue = newValue n’est pas correct). Vous devez obligatoirement utiliser la méthode setState().

Partant de là, il est alors bien important de comprendre les fondamentaux de JavaScript concernant l’assignation de variables. En effet, lorsque le type de la variable à mettre à jour est primitif (string, number, etc.), alors la copie se fait par valeur (et dans ce cas, cela ne pose pas de problème outre mesure). En revanche lorsque celui-ci représente un tableau ou un objet (ce qui est, il faut le dire, le cas la plupart du temps lorsque l’on manipule des notions métier), celle-ci se fait par référence et non par valeur (voir cet article pour plus d’informations). Ainsi, pour éviter toute incohérence au niveau des données lors de la mise à jour, il est donc important de réaliser une copie profonde (« deep copy« ) des variables avant de mettre à jour l’état du composant.

Pour ma part, j’utilise la librairie immutability-helper, suggérée dans la documentation officielle React qui utilise une syntaxe plus intuitive inspirée de MongoDB pour réaliser ces opérations rapidement (il est possible également de le faire en JavaScript natif via les fonctions filter() ou map() par exemple):

Configuration globale vs locale PnP

Si vous utilisez la librairie JavaScript PnP, vous savez peut être qu’il est possible de spécifier une configuration de l’objet global « pnp » pour, par exemple, indiquer des valeurs d’en têtes pour toutes les requêtes:

Le problème avec cette approche est qui si plusieurs composants de votre solution effectuent leurs « réglages » de cette manière, il se peut alors que ceux-ci rentrent en conflits les uns les autres selon le moment où ils s’exécutent. Ainsi, pour éviter toute mauvaise configuration, il est préférable de spécifier ces paramètres localement à votre classe ou composant au lieu de globalement de la manière suivante:

La problématique principale amenée par cette pratique concernait principalement les paramètres de cache (qui était définis globalement). Dans ce cas, de même que pour la configuration générale, préférez la définition des paramètres de cache localement à vos requêtes via usingCaching():

Utilisation d’Office UI Fabric

Certainement un des points les plus frustrant lors de mes débuts avec le SharePoint Framework. Pour faire simple, vous ne pouvez pas utiliser la dernière version d’Office UI Fabric dans votre solution, SPFx ne supportant qu’uniquement une version spécifique pré-packagée. Si vous tentez d’ajouter la dernière version du package, armez-vous alors de patience pour résoudre une à une les nombreuses erreurs de compilation qui vont en résulter…pour finalement abandonner tellement la tâche s’avère impossible pour certains composants (pas tous). Cet avertissement est même écrit dans la documentation officielle:

En d’autres termes, ce que je veux signifier ici est que vous ne devez pas prendre pas pour acquises, lors de vos estimations, les différentes options et possibilités des composants qui sont listés sur le portail officiel Office UI Fabric, sous peine d’une grosse déconvenue au moment de l’implémentation. Le problème vient du fait que SPFx utilise des versions spécifiques de packages fondamentaux comme React, Typescript ou même Office UI Fabric…qui ne sont pas toutes compatibles avec les dernières nouveautés. En résumé, lorsque vous travaillez avec SPFx, vous travaillez toujours avec un petit temps de retard sur ce qui ce fait dans l’écosystème et il est important de s’en souvenir.

Pour résoudre le problème avec Office UI Fabric, il est donc conseiller de rester avec la version livrée avec SPFx pour la version que vous utilisez (dans mon projet 1.3.4 par exemple). Vous pouvez voir cette version en explorant le dossier node_modules\@microsoft\office-ui-fabric-react-bundle\package.json:

Écriture de code asynchrone

Pour faciliter la lecture du code, j’ai tranquillement migré, dans la mesure du possible, de la syntaxe des « Promises » classique (avec then() et catch()) vers la syntaxe async/await, beaucoup plus simple à utiliser et à lire. Aussi, n’oubliez pas de récupérer vos erreurs via un try/catch.

Tests

Pour tout dire, je n’ai pas utilisé la structure de tests unitaires fournie avec le Framework. En effet, à moins d’écrire des classes purement « métier » respectant une logique bien spécifique (ce qui n’était pas le cas ici), ceux-ci ne s’avèrent pas très pertinents dans un simple contexte affichage/récupération de données dans SharePoint. En revanche, le test de composants React et leur logique intrinsèque d’affichage aurait été lui, bien plus pertinent. Malheureusement, comme mentionné dans cette erreur, il n’est toujours pas possible d’en faire pour le moment avec SPFx pour cause d’erreur de compilation. Bien dommage…Cependant, pour ceux que cela intéresse, vous pouvez toujours vous référez à cet article.

Prise en compte d’Internet Explorer

Ne pas oublier de prendre en compte Internet Explorer (11 généralement). En plus d’être une contrainte assez courante dans les entreprises, c’est souvent ce navigateur qui va niveler vers le bas les fonctionnalités JavaScript que vous pourrez utiliser. Dans la pratique, il s’agit donc de vérifier que votre code, incluant les packages et surtout eux, sont compatibles avec cette version. Si ce n’est pas le cas, vous devrez utiliser les polyfills adéquats.

Cycle de vie de l’application

Mettre à jour la version du SPFx

Tout au long du projet, la version du SharePoint Framework a beaucoup évoluée et la solution a du être mise à jour en conséquence. Cette mise à jour comprend celle du générateur ainsi que la versions des packages de votre fichier package.json. Bien que la procédure soit indiquée sur le site officiel, je vous suggère fortement cet article qui fait également mention de la commande gulp –upgrade permettant de modifier la structure de vos fichiers automatiquement si besoin. Globalement, il s’agira de mettre à jour les versions pour les packages SPFx et quelques autres packages important en fouillant dans les « release notes » comme mentionné dans la documentation:

Pour éviter de vous casser la tête, et comme il se peut qu’entre les versions de SPFx, des packages fondamentaux comme Typescript ou React aient également été mis à jour, le mieux est encore de créer un nouveau projet vide utilisant la dernière version du générateur et comparer le contenu du fichier package.json avec celui de votre solution. Cela vous permettra d’avoir la bonne combinaison de packages sans crainte d’erreurs de compilation. Je vous conseille également de supprimer complètement le dossier node_modules avant de mettre à jour (via npm i).

Ajout de Web Parts ou extensions dans le projet

Ajouter un composant Web Part ou une extension dans la solution globale est quelques chose qui survient assez fréquemment dans un projet. Pour cela, rien de très compliqué, il suffit simplement d’exécuter la commande « yo @microsoft/sharepoint » à l’emplacement du projet. Le générateur sera assez intelligent pour adapter la structure de votre projet dynamiquement sans modifier vos composants existants. Attention cependant, il n’est pas possible de revenir en arrière facilement donc faites bien attention avant d’ajouter un nouveau composant 😉

Déploiements

Avant l’apparition des « Application Lifecycle Management » APIs, le déploiement automatisé complet des solutions SPFx s’avérait plutôt chaotique. Étant donné que ces APIs existent désormais, je mentionne juste ici que j’utilisais précédemment les tâches gulp du repository sp-dev-build-extensions, permettant d’automatiser toutes les opérations de déploiement. Vous pouvez toujours les utiliser dans l’immédiat mais il est maintenant préférable (et plus simple) d’utiliser les cmdlets PnP encapsulant l’ALM API en complément des tâches gulp offertes de base avec SPFx.

Gestion des valeurs de configurations par environnement

Lorsqu’une solution est destinée à être déployée sur plusieurs environnements, il est commun de décliner des valeurs de configuration selon chacun d’eux (par exemple DEV, QA, etc.) qui seront utilisées dans le code. Cela peut être par exemple, une clé Azure Application Insights à utiliser, ou bien l’URL d’un endpoint REST, etc. Pour résoudre cette problématique, j’ai listé les solutions qui, globalement, peuvent être utilisées:

Option: fichier de configuration par environnement

Dans cette solution, un fichier de configuration au format JSON est défini et stocké par environnement. Celui-ci est lu dynamiquement par la solution pour récupérer les bonnes valeurs selon celui-ci.

Permet une structure de données plus complexe (JSON) offrant donc plus de flexibilité si les besoins du projet sont complexes.

Format facile à lire programmatiquement (JSON).

Versionné (équivaut à un simple document dans SharePoint)

Choix de l’emplacement de stockage important, notamment au niveau des permissions.

Oblige à implémenter la logique de lecture du fichier et des ses valeurs (front-end ou back-end peu importe).

Oblige à utiliser des mécanismes de cache.

Difficilement maintenable du côté client. Nécessite obligatoirement une documentation avec toutes les contraintes qui vont avec (maintenance, versions, coût).

Risqué. Des erreurs de structure, syntaxe, encodage, etc. peuvent fortement arriver.

 

Option: utilisation des SharePoint Online Tenant Properties

Dans cette solution, la nouvelle fonctionnalité de propriétés globales par tenant est utilisée.

API REST disponible + Cmdlets PnP

Maintenable plus facilement côté client grâce aux cmdlets PnP.

Moins de risques d’erreurs.

Destiné avant tout à des valeurs simples. Il est possible de stocker des structures JSON également mais dans ce cas, il faudra faire attention à la limite de stockage imposée.

Oblige à implémenter la logique de lecture du fichier et des ses valeurs (front-end ou back-end peu importe). Un exemple est disponible ici.

Oblige à utiliser des mécanismes de cache.

 

Étant donné que la fonctionnalité de propriétés propres au tenant n’existait pas encore lors du développement du projet, le choix a été fait d’utiliser un fichier de configuration pour gérer les valeurs par environnement (stocké directement dans une bibliothèque de documents de la collection de sites). Cependant, pour vos prochaines implémentations et sauf indications contraires spécifiques à votre projet, je vous conseille clairement l’utilisation de la fonctionnalités native d’Office 365 pour tous les avantages qu’elle comporte.

Logs

Gestion des erreurs

Bien qu’il existe un logger par défaut dans SPFx, j’utilise principalement celui de la libraire JavaScript PnP:

Son grand avantage par rapport à celui de base est qu’il permet d’ajouter un ou plusieurs « listeners » pour une seule et même opération de log (celui par défaut étant bien évidemment la console du navigateur). Dans les faits, cela permet surtout d’avoir une solution évolutive, permettant par exemple l’ajout d’outils de monitoring d’exceptions plus poussés comme Azure Application Insights. En résumé, même si vous n’utilisez pas (encore) d’autres « listeners » que celui par défaut, vous pourrez tout de même en ajouter très facilement par la suite sans modifier votre code. Un exemple est disponible dans la solution PnP Starter Intranet avec Azure Application Insights si jamais vous souhaitez le faire.

Note 1: la même instance Azure Application Insights peut être utilisée à la fois pour le code JavaScript mais aussi le code back-end. Pratique donc.

Note 2: la configuration du logger de PnP est soumis aux mêmes contraintes énoncées plus haut dans cet article (« Configuration globale vs locale PnP« ). Veillez-donc à ce qu’il soit initialisé une seule fois.

Performances

Gestion des « bundles »

Si votre application comporte à la fois des extensions et des Web Parts, il peut être alors préférable de les séparer dans des « bundles » différents . En effet, il est fort possible que ceux-ci ne nécessitent pas d’être chargés au même moment selon le cycle de vie de l’application globale:

Mise en cache

Utilisez au maximum les mécanismes de cache pour les données qui n’ont pas besoin d’être récupérées souvent, comme par exemple des valeurs de configuration ou des liens de menus. Personnellement, j’utilise beaucoup la librairie JavaScript de PnP (sp-pnp-js) qui fournit des utilitaires pour manipuler à la fois le localStorage et le sessionStorage du navigateur. Ils proposent en plus de la fonctionnalité de cache, des options d’expiration bien pratiques pour contrôler la durée de vie de vos données mises en cache. Aussi, n’oubliez pas de supprimer explicitement les éléments expirés avant toute manipulation:

Librairies externes

Si vous utilisez des librairies externes, faites attention car si vous les référencez dans le fichier config.json (par exemple textbox.io dans le cas de l’exemple react-textboxio), celles-ci seront chargées à chaque fois avec votre bundle même si elles ne sont pas utilisées:

Si votre librairie est utilisée à un moment bien précis dans votre code, préférez alors la charger dynamiquement via l’utilitaire SPComponentLoader:

Divers

Développer avec les sites de communications en plusieurs langues

Étant basé au Québec, la gestion du multilinguisme anglais/français est toujours un incontournable. Malheureusement, il semblerait que Microsoft soit toujours peu enclin à considérer les autres langues que la sienne dans ses solutions. En effet, en dehors de l’interface graphique, il n’est pas possible de créer des sites de communications dans un autre langue que l’anglais que ce soit par l’interface ou programmatiquement (bien que le paramètre LCID existe):

Même si cela peu paraître anodin, cela peut devenir une vraie problématique notamment pour la recherche de contenu, car les valeurs, notamment celles de taxonomie, seront sauvegardées dans cette langue si la personne le fait via l’interface idoine. Par exemple, cela vous imposera un travail supplémentaire pour la récupération des valeurs de filtres de recherche dans le panneau d’affinements, si vous choisissez d’en réaliser un personnalisé (voir mon autre article « Build dynamic SharePoint search experience using refiners and paging with SPFx, Office UI Fabric and PnP JS library » pour plus d’informations).

De plus, les variantes SharePoint ne sont pas disponibles avec l’expérience de page moderne. Il vous faudra donc développer votre propre système multilingue.

Attention aux tenants en mode « First release »

Il est bien important de vérifier que vos environnements Office 365, ou les comptes que vous utilisez pour vos tests, ne soient pas marqués comme « First Release« .

Premièrement il est peu probable que votre environnement de production soit dans cette configuration et deuxièmement, certaines différences peuvent apparaître en termes de comportements selon l’environnement où vous effectuez vos tests (cas vécu). Assurez-vous donc d’avoir la même configuration pour tous vos tenants et ainsi éviter les surprises.

Test avec plusieurs comptes utilisateurs et permissions

Cela peut paraître trivial, mais cela peut vous valoir quelques surprises. En effet, dans ce genre de solutions, les API SharePoint (ou autres) sont généralement très sollicitées et il est bien important de vérifier que celles-ci peuvent effectivement être appelées par les utilisateurs auxquels elles sont destinées en fonction de leurs droits attendus. En tant que développeurs, nous sommes très souvent administrateurs sur le tenant et ces problématiques ne font surface que beaucoup plus loin dans l’avancement du projet.

Je pourrais encore mentionner plusieurs autres points mais je pense qu’avec ceux décrits plus haut, l’essentiel y est. J’espère que cela pourra vous faire gagner un peu de temps sur lors de vos prochains projets!

+ There are no comments

Add yours