Una dintre trăsăturile plăcute ale tehnologiei 1C:Enterprise este că soluția aplicațională, dezvoltată pe baza tehnologiei formelor gestionate, poate fi rulată atât în clientul subțire (executabil) pe Windows, Linux, MacOS X, cât și ca client web pe 5 browsere – Chrome, Internet Explorer, Firefox, Safari, Edge, toate acestea fără a modifica codul sursă al aplicației. Mai mult decât atât – vizual, aplicația în clientul subțire și în browser se comportă și arată practic identic.
Găsiți 10 diferențe (sub cat, 2 imagini):
Fereastra clientului subțire pe Linux:

Aceeași fereastră în clientul web (în browserul Chrome):

De ce am creat clientul web? Spus ceva mai pompos, această sarcină ne-a fost impusă de vremuri. De mult timp, lucrul prin Internet a devenit o condiție necesară pentru aplicațiile de afaceri. La început, am adăugat posibilitatea de a lucra prin Internet pentru clientul nostru subțire (unii dintre concurenții noștri, de altfel, s-au oprit la acest punct; alții, dimpotrivă, au renunțat la clientul subțire și s-au limitat la implementarea clientului web). Noi am decis să oferim utilizatorilor noștri posibilitatea de a alege varianta de client care li se potrivește cel mai bine.

Adăugarea posibilității de a lucra prin Internet pentru clientul subțire a fost un proiect mare, având o schimbare completă a arhitecturii interacțiunii client-server. Crearea clientului web a fost un proiect complet nou, început de la zero.
Formularea problemei
Așadar, cerințele pentru proiect sunt: clientul web trebuie să îndeplinească aceleași funcții ca și clientul subțire, mai precis:
- Afișarea interfeței utilizatorului
- Executarea codului clientului, scris în limbajul 1C
Interfața utilizatorului în 1C este descrisă în editorul vizual, dar declarativ, fără a plasa elementele pixel cu pixel; se utilizează aproximativ trei zeci de tipuri de elemente de interfață – butoane, câmpuri de introducere (text, numerice, dată/timp), liste, tabele, grafice etc.
Codul clientului în limbajul 1C poate conține apeluri către server, lucrul cu resurse locale (fișiere etc.), imprimare și multe altele.
Atât clientul subțire (când lucrează prin web), cât și clientul web folosesc același set de servicii web pentru a comunica cu serverul aplicațiilor 1C. Implementarea la clienți este, desigur, diferită – clientul subțire este scris în C++, iar clientul web – în JavaScript.
Puțin istorie
Proiectul de creare a clientului web a început în 2006, având o echipă medie de 5 persoane. În anumite etape ale proiectului, au fost implicați dezvoltatori pentru implementarea unor funcționalități specifice (documente tabelare, diagrame etc.); de obicei, aceștia erau aceiași dezvoltatori care realizau acele funcționalități în clientul subțire. Deci, dezvoltatorii au scris din nou în JavaScript componentele pe care le creaseră anterior în C++.
Încă de la început, am respins ideea oricărei conversii automate (chiar și parțiale) a codului C++ al clientului subțire în JavaScript pentru clientul web din cauza diferentelor conceptuale semnificative dintre cele două limbaje; clientul web a fost scris în JavaScript de la zero.
În primele iterații ale proiectului, clientul web convertit codul clientului scris în limbajul încorporat 1C direct în JavaScript. Clientul subțire funcționează diferit — codul în limbajul încorporat 1C este compilat în bytecode și apoi acest bytecode este interpretat pe client. Ulterior, clientul web a început să facă la fel – mai întâi, acest lucru a adus un câștig în performanță, iar în al doilea rând – a permis unificarea arhitecturii clientului subțire și a clientului web.
Prima versiune a platformei 1C:Enterprise cu suport pentru clientul web a fost lansată în 2009. La acel moment, clientul web suporta 2 browsere – Internet Explorer și Firefox. În planurile inițiale era inclusă și suportul pentru Opera, dar din cauza unor probleme insurmontabile la acel moment cu gestionarii de închidere a aplicației în Opera (nu se reușea cu o siguranță de 100% să se urmărească închiderea aplicației și în acel moment să se efectueze procedura de deconectare de la serverul de aplicații 1C) a fost necesar să renunțăm la aceste planuri.
Structura proiectului
În total, platforma 1C:Enterprise are 4 proiecte scrise în JavaScript:
- WebTools – biblioteci comune utilizate de celelalte proiecte (aici includem ).
- Element de control (implementat atât în JavaScript, cât și în clientul subțire, cât și în clientul web)
- Element de control (implementat atât în JavaScript, cât și în clientul subțire, cât și în clientul web)
- Client web
Structura fiecărui proiect se aseamănă cu structura proiectelor Java (sau a proiectelor .NET – fiecare cu ceea ce îi este mai familiar); avem spații de nume, iar fiecare spațiu de nume se află într-un folder separat. În interiorul folderului se află fișierele și clasele spațiului de nume. În proiectul clientului web sunt aproximativ 1000 de fișiere.
Structura clientului web este împărțită în următoarele subsisteme:
- Interfața gestionată a aplicației client
- Interfața generală a aplicației (meniuri de sistem, panouri)
- Interfața formularelor gestionate, inclusiv aproximativ 30 de elemente de control (buton, diverse tipuri de câmpuri de introducere – text, numeric, dată/oră etc., tabele, liste, grafice etc.)
- Modelul obiectual disponibil pentru dezvoltatori pe client (în total peste 400 de tipuri: modelul obiectual al interfeței gestionate, configurațiile aranjamentului de date, formatarea condiționată etc.)
- Interpretul limbajului încorporat 1C
- Extensii de browser (utilizate pentru funcționalități care nu sunt suportate în JavaScript)
- Lucrul cu criptografia
- Lucrul cu fișierele
- Tehnologia componentelor externe, care permite utilizarea lor atât în clientul subțire, cât și în cel web
Caracteristici de dezvoltare
Implementarea tuturor celor descrise mai sus în JavaScript este o sarcină complexă. Probabil că web clientul 1C este una dintre cele mai mari aplicații client-side scrise în JavaScript – aproximativ 450.000 de linii. Folosim activ în codul web-clientului o abordare orientată pe obiect, care simplifică lucrul cu un astfel de proiect mare.
Pentru a minimiza dimensiunea codului client, am folosit inițial propriul nostru obfuscator, iar începând cu versiunea platformei 8.3.6 (octombrie 2014), am început să folosim . Efectul utilizării în cifre – dimensiunea framework-ului clientului web după obfuscare:
- Obfuscatorul propriu – 1556 kB
- Google Closure Compiler – 1073 kB
Utilizarea Google Closure Compiler ne-a ajutat să îmbunătățim performanța clientului web cu 30% în comparație cu obfuscatorul nostru propriu. În plus, cu 15-25% (în funcție de browser) a scăzut volumul de memorie consumat de aplicație.
Google Closure Compiler funcționează foarte bine cu codul orientat pe obiect, de aceea eficiența sa pentru clientul web este maximă. Closure Compiler face pentru noi câteva lucruri bune:
- Verificarea statică a tipurilor în timpul compilării proiectului (asigurată prin faptul că acoperim codul cu anotații JSDoc). Rezultatul este o tipizare statică, foarte asemănătoare cu tipizarea din C++. Acest lucru ajută la depistarea unui procent semnificativ de erori în etapa de compilare a proiectului.
- Reducerea dimensiunii codului prin obfuscare
- O serie de optimizări ale codului executat, de exemplu, cele precum:
- substituțiile inline ale funcțiilor. Apelul unei funcții în JavaScript este o operațiune destul de costisitoare, iar substituțiile inline ale metodelor mici utilizate frecvent accelerează semnificativ execuția codului.
- Calculul constantelor în timpul compilării. Dacă expresia depinde de o constantă, acesta va fi înlocuit cu valoarea reală a constantei.
Ca mediu de dezvoltare pentru clientul web, folosim WebStorm.
Pentru analiza codului folosim , unde integrăm analyzer-uri statice de cod. Folosind aceste analizări, monitorizăm degradarea calității codului sursă în JavaScript și încercăm să o evităm.

Ce probleme am rezolvat/rezolvăm
În timpul implementării proiectului, ne-am confruntat cu o serie de probleme interesante pe care a trebuit să le rezolvăm.
Schimbul de date cu serverul și între ferontele
Există situații în care obfuscația codului sursă poate împiedica funcționarea sistemului. Codul extern față de codul executabil al clientului web, ca urmare a obfuscării, poate avea denumiri de funcții și parametri diferite de cele așteptate de codul nostru executabil. Codul extern pentru noi este:
- Codul care vine de pe server sub formă de structuri de date
- Codul dintr-o altă fereastră a aplicației
Pentru a evita obfuscația în timpul interacțiunii cu serverul, folosim tag-ul @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;
}Iar pentru a evita obfuscația în timpul interacțiunii cu alte ferontele, folosim așa-numitele interfețe exportabile (interfețe ale căror metode sunt complet exportabile).
/**
* Экспортируемый интерфейс контрола 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 (){}Am folosit Virtual DOM înainte de a deveni mainstream)
Ca toți dezvoltatorii care se ocupă de UI web complex, ne-am dat seama rapid că DOM nu este potrivit pentru interfețe de utilizator dinamice. Practic, imediat a fost implementat un analog al Virtual DOM pentru optimizarea lucrului cu UI. În procesul de gestionare a evenimentelor, toate modificările DOM sunt memorate în memorie și, abia după finalizarea tuturor operațiunilor, modificările acumulate sunt aplicate arborelui DOM.
Optimizarea funcționării clientului web
Pentru ca clientul nostru web să funcționeze mai repede, ne străduim să maximizăm utilizarea capacităților native ale browserului (CSS etc.). Astfel, panoul de comenzi al formularului (situat practic pe fiecare formular al aplicației) este redat exclusiv prin intermediul browserului, cu design dinamic bazat pe CSS.

Testare
Pentru testarea funcțională și a performanței, folosim un instrument dezvoltat intern (scris în Java și C++), precum și un set de teste bazate pe .
Instrumentul nostru este versatil – permite testarea aproape oricăror aplicații de tip desktop, fiind astfel potrivit atât pentru testarea clienților subțiri, cât și pentru clienții web. Instrumentul înregistrează acțiunile utilizatorului care a rulat aplicația "1C" într-un fișier-scenariu. În același timp, se realizează înregistrarea imaginilor zonei de lucru a ecranului – etaloanele. Când se controlează noile versiuni ale clientului web, scenariile sunt rulate fără intervenția utilizatorului. În caz de neconcordanță a ecranului cu etalonul la orice pas, testul este considerat eșuat, după care un specialist în calitate efectuează o investigație – este o eroare sau o modificare planificată a comportamentului sistemului. În cazul unui comportament planificat, etaloanele sunt înlocuite automat cu altele noi.
Instrumentul efectuează, de asemenea, măsurători ale performanței aplicațiilor cu o precizie de 25 de milisecunde. În anumite situații, recirculăm părți ale scenariului (de exemplu, rulând de mai multe ori introducerea unei comenzi) pentru a analiza degradarea timpului de execuție în timp. Rezultatele tuturor măsurătorilor sunt înregistrate într-un jurnal pentru analiză.

Instrumentul nostru de testare și aplicația testată
Instrumentul nostru și Selenium se completează reciproc; de exemplu, dacă un buton de pe unul dintre ecrane și-a schimbat locația – Selenium nu poate monitoriza acest lucru, dar instrumentul nostru va observa, deoarece efectuează o comparație pixel cu pixel a ecranului cu etalonul. De asemenea, instrumentul este capabil să detecteze probleme cu procesarea input-ului de la tastatură sau mouse, dat fiind că acestea sunt exact ce reproduce.
Testele din ambele instrumente (al nostru și Selenium) rulează scenarii tipice de funcționare din soluțiile noastre aplicaționale. Testele sunt lansate automat după construirea zilnică a platformei "1C:Enterprise". În cazul încetinirii execuției scenariilor (comparativ cu construcția anterioară), efectuăm o investigație și remediem cauza încetinirii. Criteriul nostru este simplu – noua construcție trebuie să funcționeze nu mai lent decât cea anterioară.
Pentru investigarea incidentelor de încetinire a funcționării, dezvoltatorii utilizează diferite instrumente; în principal se utilizează producție a companiei . Se realizează înregistrarea jurnalelor de execuție pentru operațiunea problematică în versiunea anterioară și în noua versiune, apoi jurnalele sunt analizate. În acest caz, timpul de execuție al operațiunilor individuale (în milisecunde) poate să nu fie un factor decisiv – în browser se lansează periodic procese de întreținere cum ar fi curățarea memoriei, care pot influența timpul de execuție al funcțiilor și deforma imaginea. Parametrii mai relevanți în acest caz vor fi numărul de instrucțiuni JavaScript executate, numărul de operații atomice asupra DOM etc. Dacă numărul de instrucțiuni/operații într-un același scenariu în noua versiune a crescut – aceasta înseamnă aproape întotdeauna o cădere a performanței, care trebuie corectată.
De asemenea, una dintre cauzele scăderii performanței poate fi că Google Closure Compiler nu a reușit dintr-un anumit motiv să facă substituția inline a funcției (de exemplu, deoarece funcția este recursivă sau virtuală). În acest caz, încercăm să corectăm situația, rescriind codul sursă.
Extensii de browser
În cazul în care soluția aplicației are nevoie de funcționalitate care nu este disponibilă în JavaScript, folosim extensii de browser:
- pentru lucrul cu fișiere
- pentru lucrul cu criptografie
- lucrează cu
Extensiile noastre constau în două părți. Prima parte – ceea ce se numește extensie de browser (de obicei, extensii scrise în JavaScript pentru Chrome și Firefox), care interacționează cu a doua parte – extensia binary, care realizează funcționalitatea necesară. Trebuie menționat că scriem 3 versiuni ale extensiilor binare – pentru Windows, Linux și MacOS. Extensia binară este livrată împreună cu platforma 1C:Enterprise și se află pe serverul aplicației 1C. La prima apelare din clientul web, aceasta este descărcată pe computerul client și instalată în browser.
Când lucrăm în Safari, extensiile noastre folosesc NPAPI, în timp ce în Internet Explorer folosesc tehnologia ActiveX. pentru că nu suportă extensii, astfel încât clientul web în acesta funcționează cu restricții.
Dezvoltarea ulterioară
Unul dintre grupurile de sarcini pentru echipa de dezvoltare a clientului web este extinderea funcționalității. Funcționalitatea clientului web trebuie să fie identică cu cea a clientului subțire, toată noua funcționalitate fiind implementată simultan atât în clientul subțire, cât și în cel web.
Alte sarcini includ dezvoltarea arhitecturii, refactorizarea, creșterea performanței și fiabilității. De exemplu, o direcție este avansarea către un model de lucru asincron. O parte din funcționalitatea clientului web este construită în prezent pe un model de interacțiune sincron cu serverul. Modelul asincron devine acum mai relevant în browsere (și nu doar în browsere), impunându-ne să modificăm clientul web prin înlocuirea apelurilor sincrone cu cele asincrone (și să efectuăm refactorizarea corespunzătoare a codului). Trecerea treptată la modelul asincron este justificată de necesitatea de a susține soluțiile lansate și de adaptarea lor treptată.
Sursa: habr.com
