L'une des caractéristiques agréables de la technologie 1C:Entreprise est que la solution applicative, développée selon la technologie des formulaires gérés, peut être lancée à la fois dans un client léger (exécutable) sous Windows, Linux, MacOS X, et comme client web sous 5 navigateurs – Chrome, Internet Explorer, Firefox, Safari, Edge – et tout cela sans modification du code source de l'application. De plus, l'application en client léger et dans le navigateur fonctionne et apparaît pratiquement de manière identique.
Trouvez 10 différences (2 images sous le pli) :
Fenêtre du client léger sur Linux :

La même fenêtre dans le client web (dans le navigateur Chrome) :

Pourquoi avons-nous créé le client web ? Pour dire les choses de manière un peu pompeuse, cette tâche nous a été imposée par le temps. Cela fait longtemps que le travail via Internet est devenu une condition nécessaire pour les applications professionnelles. Au début, nous avons ajouté la possibilité de travailler via Internet pour notre client léger (certains de nos concurrents, d'ailleurs, se sont arrêtés là ; d'autres, au contraire, ont abandonné le client léger et se sont limités à la mise en œuvre du client web). Nous avons décidé de donner à nos utilisateurs la possibilité de choisir l'option de client qui leur convient le mieux.

L'ajout de la possibilité de travailler via Internet pour le client léger était un grand projet avec un changement complet de l'architecture de l'interaction client-serveur. Créer le client web, c'est un projet complètement nouveau, commencé from scratch.
Définition du problème
Ainsi, les exigences du projet sont : le client web doit faire la même chose que le client léger, à savoir :
- Afficher l'interface utilisateur
- Exécuter le code client écrit dans le langage 1C
L'interface utilisateur dans 1C est décrite dans un éditeur visuel, mais de manière déclarative, sans placement pixel par pixel des éléments ; environ une trentaine de types d'éléments d'interface sont utilisés — boutons, champs de saisie (textes, numériques, date/heure), listes, tableaux, graphiques, etc.
Le code client en langage 1C peut contenir des appels serveur, travailler avec des ressources locales (fichiers, etc.), imprimer et bien d'autres choses.
Tant le client léger (lorsqu'il est utilisé via le web) que le client web utilisent le même ensemble de services web pour communiquer avec le serveur d'applications 1C. La mise en œuvre pour les clients est, bien sûr, différente – le client léger est écrit en C++, le client web est en JavaScript.
Un peu d'histoire
Le projet de création d'un client web a débuté en 2006, avec une équipe d'environ 5 personnes en moyenne. À différentes étapes du projet, des développeurs ont été engagés pour mettre en œuvre des fonctionnalités spécifiques (documents tableurs, diagrammes, etc.); en règle générale, ce étaient les mêmes développeurs qui avaient conçu ces fonctionnalités dans le client léger. C'est-à-dire que les développeurs ont réécrit en JavaScript les composants qu'ils avaient auparavant créés en C++.
Dès le départ, nous avons rejeté l'idée d'une conversion automatique (même partielle) du code C++ du client léger en JavaScript pour le client web en raison des fortes différences conceptuelles entre ces deux langages; le client web a été écrit en JavaScript à partir de zéro.
Lors des premières itérations du projet, le client web convertissait le code client écrit en 1C dans JavaScript. Le client léger fonctionne différemment: le code en 1C est compilé en bytecode, puis ce bytecode est interprété côté client. Par la suite, le client web a adopté ce même processus – d'une part, cela a amélioré les performances, d'autre part, cela a permis d'unifier l'architecture des clients léger et web.
La première version de la plateforme 1C:Entreprise avec un client web a été publiée en 2009. À l'époque, le client web supportait 2 navigateurs – Internet Explorer et Firefox. Les plans initiaux incluaient la prise en charge d'Opera, mais en raison de problèmes insurmontables avec les gestionnaires de fermeture d'application dans Opera (il était impossible de suivre avec 100 % de certitude si l'application se fermait, et à ce moment-là d'effectuer la procédure de déconnexion du serveur d'applications 1C), ces plans ont dû être abandonnés.
Structure du projet
Il y a au total 4 projets dans la plateforme 1C:Entreprise, écrits en JavaScript:
- WebTools – bibliothèques communes utilisées par les autres projets (nous incluons également ici ).
- Élément de contrôle (réalisé en JavaScript tant dans le client léger que dans le client web)
- Élément de contrôle (réalisé en JavaScript tant dans le client léger que dans le client web)
- Un client web
La structure de chaque projet ressemble à celle des projets Java (ou des projets .NET – selon les préférences); nous avons des namespaces, et chaque namespace est dans un dossier séparé. À l'intérieur de chaque dossier se trouvent les fichiers et les classes du namespace. Le projet du client web contient environ 1000 fichiers.
Structurellement, le client web est principalement divisé en les sous-systèmes suivants:
- Interface gérée de l'application cliente
- Interface général de l'application (menus système, panneaux)
- Interface des formulaires gérés, comprenant notamment environ 30 éléments de contrôle (boutons, différents types de champs d'entrée – texte, numérique, date/heure, etc., tableaux, listes, graphiques, etc.)
- Modèle d'objet accessible aux développeurs côté client (plus de 400 types au total : modèle d'objet d'interface gérée, paramètres de mise en page des données, mise en forme conditionnelle, etc.)
- Interpréteur du langage embarqué 1C
- Extensions de navigateur (utilisées pour des fonctionnalités non prises en charge par JavaScript)
- Travail avec la cryptographie
- Travailler avec des fichiers
- Technologie des composants externes, permettant de les utiliser à la fois dans le client léger et le client Web
Particularités du développement
La mise en œuvre de tout ce qui précède en JavaScript n'est pas une tâche facile. Peut-être que le client Web 1C est l'une des plus grandes applications côté client écrites en JavaScript – environ 450 000 lignes. Nous utilisons activement dans le code du client Web une approche orientée objet qui simplifie le travail avec un projet aussi vaste.
Pour minimiser la taille du code client, nous avons d'abord utilisé notre propre obfuscateur, et depuis la version 8.3.6 de la plateforme (octobre 2014), nous utilisons . L'effet de cette utilisation en chiffres – la taille du framework du client Web après obfuscation :
- Notre propre obfuscateur – 1556 Ko
- Google Closure Compiler – 1073 Ko
L'utilisation de Google Closure Compiler nous a permis d'améliorer la performance du client Web de 30 % par rapport à notre propre obfuscateur. De plus, la consommation mémoire de l'application a diminué de 15 à 25 % (selon le navigateur).
Google Closure Compiler fonctionne très bien avec du code orienté objet, donc son efficacité pour le client Web est maximale. Closure Compiler nous fournit plusieurs avantages :
- Vérification statique des types au moment de la construction du projet (assurée par le fait que nous complétons le code avec des annotations JSDoc). Le résultat est une typage statique très proche de celui en C++. Cela aide à attraper un pourcentage assez élevé d'erreurs lors de la compilation du projet.
- Réduction de la taille du code par obfuscation
- Un certain nombre d'optimisations du code exécuté, par exemple, telles que :
- remplacements inline des fonctions. Appeler une fonction en JavaScript est une opération relativement coûteuse, et les remplacements inline de petites méthodes couramment utilisées accélèrent considérablement le code.
- Calcul des constantes à la compilation. Si une expression dépend d'une constante, la valeur réelle de la constante sera substituée.
Pour le développement du client web, nous utilisons WebStorm.
Pour l'analyse du code, nous utilisons , où nous intégrons des analyseurs de code statiques. Grâce à ces analyseurs, nous surveillons la dégradation de la qualité du code source en JavaScript et nous faisons en sorte de l'éviter.

Quelles tâches avons-nous résolues/résolvons-nous
Au cours de la mise en œuvre du projet, nous avons été confrontés à plusieurs tâches intéressantes que nous avons dû résoudre.
Échange de données avec le serveur et entre les fenêtres
Il existe des situations où l'obfuscation du code source peut entraver le fonctionnement du système. Le code externe par rapport au code exécutable du client web, à cause de l'obfuscation, peut avoir des noms de fonctions et de paramètres différents de ceux que notre code exécutable attend. Pour nous, le code externe est :
- Le code qui arrive du serveur sous forme de structures de données
- Le code d'une autre fenêtre de l'application
Pour éviter l'obfuscation lors de l'interaction avec le serveur, nous utilisons la balise @expose :
/**
* @constructor
* @extends {Base.SrvObject}
*/
Srv.Core.GenericException = function ()
{
/**
* @type {string}
* @expose
*/
this.descr;
/**
* @type {Srv.Core.GenericException}
* @expose
*/
this.inner;
/**
* @type {string}
* @expose
*/
this.clsid;
/**
* @type {boolean}
* @expose
*/
this.encoded;
}Et pour éviter l'obfuscation lors de l'interaction avec d'autres fenêtres, nous utilisons ce que l'on appelle des interfaces exportables (interfaces dont toutes les méthodes sont exportables).
/**
* Экспортируемый интерфейс контрола DropDownWindow
*
* @interface
* @struct
*/
WebUI.IDropDownWindowExp = function(){}
/**
* Перемещает выделение на 1 вперед или назад
*
* @param {boolean} isForward
* @param {boolean} checkOnly
* @return {boolean}
* @expose
*/
WebUI.IDropDownWindowExp.prototype.moveMarker = function (isForward, checkOnly){}
/**
* Перемещает выделение в начало или конец
*
* @param {boolean} isFirst
* @param {boolean} checkOnly
* @return {boolean}
* @expose
*/
WebUI.IDropDownWindowExp.prototype.moveMarkerTo = function (isFirst, checkOnly){}
/**
* @return {boolean}
* @expose
*/
WebUI.IDropDownWindowExp.prototype.selectValue = function (){}Nous avons utilisé le Virtual DOM avant qu'il ne devienne courant)
Comme tous les développeurs traitant avec des interfaces utilisateur web complexes, nous avons rapidement compris que le DOM ne convient pas au travail avec une interface utilisateur dynamique. Très rapidement, un équivalent du Virtual DOM a été mis en œuvre pour optimiser le travail avec l'UI. Lors du traitement d'un événement, toutes les modifications du DOM sont enregistrées en mémoire et, seulement à la fin de toutes les opérations, les modifications accumulées sont appliquées à l'arbre DOM.
Optimisation de l'efficacité du client web
Pour que notre client web fonctionne plus rapidement, nous essayons d'exploiter au maximum les capacités natives du navigateur (CSS, etc.). Ainsi, la barre de commande du formulaire (présente dans presque tous les formulaires de l'application) est rendue exclusivement avec les outils du navigateur, grâce à une mise en page dynamique basée sur CSS.

Test
Pour les tests fonctionnels et de performance, nous utilisons un outil développé en interne (écrit en Java et C++), ainsi qu'un ensemble de tests basé sur .
Notre outil est polyvalent : il permet de tester pratiquement toutes les applications de bureau, ce qui le rend adapté pour tester à la fois les clients lourds et les clients web. L'outil enregistre les actions de l'utilisateur ayant lancé l'application « 1C » dans un fichier de script. En même temps, des images de l'interface utilisateur sont capturées – les références. Lors du contrôle des nouvelles versions du client web, les scénarios sont exécutés sans intervention de l'utilisateur. En cas de non-conformité entre la capture d'écran et la référence à un moment donné, le test est considéré comme échoué, après quoi un spécialiste qualité enquête – il s'agit d'une erreur ou d'un changement de comportement prévu du système. En cas de comportement prévu, les références sont automatiquement remplacées par de nouvelles.
L'outil mesure également la performance des applications avec une précision de 25 millisecondes. Dans certains cas, nous répétons des parties du scénario (par exemple, la saisie d'une commande plusieurs fois) pour analyser la dégradation des temps d'exécution au fil du temps. Tous les résultats des mesures sont enregistrés dans un journal pour analyse.

Notre outil de test et l'application testée
Notre outil et Selenium se complètent ; par exemple, si un bouton sur l'un des écrans change de position, Selenium peut ne pas le détecter, mais notre outil le remarquera, car il effectue une comparaison pixel par pixel de la capture d'écran avec la référence. De plus, l'outil est capable de détecter des problèmes de traitement des entrées au clavier ou à la souris, puisqu'il les reproduit.
Les tests sur les deux outils (le nôtre et Selenium) exécutent des scénarios types de fonctionnement à partir de nos solutions applicatives. Les tests sont automatiquement lancés après la compilation quotidienne de la plateforme « 1C:Enterprise ». En cas de ralentissement des scénarios (par rapport à la compilation précédente), nous menons une enquête et éliminons la cause de ce ralentissement. Notre critère est simple : la nouvelle compilation ne doit pas être plus lente que la précédente.
Pour enquêter sur les incidents de ralentissement, les développeurs utilisent différents outils ; principalement, il est utilisé de la société . L'enregistrement des journaux des opérations problématiques est effectué à la fois sur l'ancienne et la nouvelle version, puis les journaux sont analysés. Dans ce cas, le temps d'exécution des opérations individuelles (en millisecondes) peut ne pas être un facteur décisif – le navigateur exécute périodiquement des processus de maintenance comme le nettoyage, ce qui peut interférer avec le temps d'exécution des fonctions et fausser les résultats. Les paramètres plus pertinents dans ce cas seront le nombre d'instructions JavaScript exécutées, le nombre d'opérations atomiques sur le DOM, etc. Si le nombre d'instructions/opérations dans le même scénario augmente dans la nouvelle version, cela signifie presque toujours une dégradation des performances qui doit être corrigée.
Une autre raison de la baisse des performances peut être le fait que Google Closure Compiler n'a pas pu effectuer le remplacement en ligne de la fonction (par exemple, parce que la fonction est récursive ou virtuelle). Dans ce cas, nous essayons de résoudre le problème en réécrivant le code source.
Extensions de navigateur
Lorsque l'application nécessite une fonctionnalité qui n'est pas disponible dans JavaScript, nous utilisons des extensions de navigateur :
- pour travailler avec des fichiers
- pour travailler avec la cryptographie
- interagir avec
Nos extensions se composent de deux parties. La première partie est ce que l'on appelle une extension de navigateur (généralement des extensions écrites en JavaScript pour Chrome et Firefox), qui interagit avec la seconde partie — l'extension binaire, réalisant la fonctionnalité dont nous avons besoin. Il convient de mentionner que nous écrivons 3 versions d'extensions binaires – pour Windows, Linux et MacOS. L'extension binaire est fournie avec la plateforme 1C:Entreprise et se trouve sur le serveur des applications 1C. Lors de la première invocation depuis le client web, elle est téléchargée sur l'ordinateur client et installée dans le navigateur.
Lors de l'utilisation de Safari, nos extensions utilisent NPAPI, tandis qu'avec Internet Explorer, elles utilisent la technologie ActiveX. ne prend pas encore en charge les extensions, donc le client web fonctionne avec des limitations.
Développement futur
Un des groupes de tâches pour l'équipe de développement du client web est d'améliorer les fonctionnalités. Les fonctionnalités du client web doivent être identiques à celles du client léger, toutes les nouvelles fonctionnalités étant mises en œuvre simultanément dans les deux versions.
D'autres tâches consistent à développer l'architecture, refactoriser, améliorer les performances et la fiabilité. Par exemple, l'une des directions est de s'orienter davantage vers un modèle de travail asynchrone. Une partie des fonctionnalités du client web est actuellement basée sur un modèle d'interaction synchrone avec le serveur. Le modèle asynchrone devient de plus en plus pertinent dans les navigateurs (et pas seulement dans les navigateurs), ce qui nous pousse à modifier le client web en remplaçant les appels synchrones par des appels asynchrones (et à refactoriser le code en conséquence). La transition progressive vers le modèle asynchrone s'explique par la nécessité de supporter les solutions déjà publiées et leur adaptation progressive.
Source : habr.com
