1C:Enterprise 技术的优点之一是,使用托管表单技术开发的应用程序解决方案可以在 Windows、Linux、MacOS X 的瘦(可执行)客户端中启动,也可以作为 5 种浏览器的 Web 客户端启动 - Chrome、Internet Explorer、Firefox、Safari、Edge 等所有这些都无需更改应用程序源代码。 此外,从外部看,瘦客户端中的应用程序和浏览器中的应用程序的功能和外观几乎相同。
找出10个不同点(剪下2张图):
Linux 上的瘦客户端窗口:
Web 客户端中的同一窗口(在 Chrome 浏览器中):
我们为什么要做网络客户端? 说得有些可悲的是,时间给我们安排了这样的任务。 长期以来,通过 Internet 工作一直是业务应用程序的先决条件。 首先,我们为瘦客户端添加了通过互联网工作的能力(顺便说一句,我们的一些竞争对手就此止步;相反,其他竞争对手则放弃了瘦客户端,只限于实现 Web 客户端)。 我们决定让用户有机会选择最适合他们的客户端选项。
向瘦客户端添加基于 Web 的功能是一个大项目,需要彻底改变客户端-服务器架构。 创建 Web 客户端是一个全新的项目,从头开始。
制定问题
所以,项目要求:Web客户端必须做与瘦客户端相同的事情,即:
- 显示用户界面
- 执行1C语言编写的客户端代码
1C 中的用户界面是在可视化编辑器中描述的,但是是声明性的,没有元素的逐像素排列; 使用了大约三打类型的界面元素 - 按钮、输入字段(文本、数字、日期/时间)、列表、表格、图表等。
1C 语言的客户端代码可以包含服务器调用、使用本地资源(文件等)、打印等等。
瘦客户端(通过 Web 工作时)和 Web 客户端都使用同一组 Web 服务与 1C 应用服务器进行通信。 当然,客户端实现是不同的 - 瘦客户端是用 C++ 编写的,Web 客户端是用 JavaScript 编写的。
有一点历史
Web 客户端项目始于 2006 年,团队(平均)有 5 人。 在项目的某些阶段,开发人员参与实现特定功能(电子表格文档、图表等); 通常,这些开发人员在瘦客户端中执行此功能。 那些。 开发人员用 JavaScript 重新编写了他们之前用 C++ 创建的组件。
从一开始,我们就拒绝将 C++ 瘦客户端代码自动(甚至部分)转换为 JavaScript Web 客户端的想法,因为两种语言之间存在强烈的概念差异; Web 客户端是用 JavaScript 从头开始编写的。
在项目的第一次迭代中,Web 客户端将内置 1C 语言的客户端代码直接转换为 JavaScript。 瘦客户端的行为有所不同——内置1C语言中的代码被编译成字节码,然后这个字节码在客户端上被解释。 随后,Web 客户端开始做同样的事情 - 首先,它提供了性能增益,其次,它使得统一瘦客户端和 Web 客户端的架构成为可能。
具有 Web 客户端支持的 1C:Enterprise 平台的第一个版本于 2009 年发布。 当时的 Web 客户端支持 2 种浏览器 - Internet Explorer 和 Firefox。 最初的计划包括对 Opera 的支持,但由于当时 Opera 中的应用程序关闭处理程序存在无法克服的问题(无法 100% 确定地跟踪应用程序正在关闭,并在此时执行从这些计划中的 1C 应用程序服务器)不得不被放弃。
项目结构
1C:Enterprise 平台总共有 4 个用 JavaScript 编写的项目:
- WebTools – 其他项目使用的共享库(我们还包括
谷歌关闭库 ). - 控制元素
格式化文档 (在瘦客户端和 Web 客户端中均以 JavaScript 实现) - 控制元素
调度器 (在瘦客户端和 Web 客户端中均以 JavaScript 实现) - 网页客户端
每个项目的结构类似于 Java 项目(或 .NET 项目 - 以更接近者为准)的结构; 我们有命名空间,每个命名空间都位于单独的文件夹中。 文件夹内有文件和命名空间类。 Web客户端项目中有大约1000个文件。
从结构上来说,Web客户端主要分为以下子系统:
- 托管客户端应用程序接口
- 通用应用程序界面(系统菜单、面板)
- 托管表单的界面,其中包括约 30 个控件(按钮、各种类型的输入字段 - 文本、数字、日期/时间等、表格、列表、图表等)
- 客户端上可供开发人员使用的对象模型(总共超过400种:托管接口对象模型、数据布局设置、条件样式等)
- 内置1C语言解释器
- 浏览器扩展(用于 JavaScript 不支持的功能)
- 使用密码学
- 处理文件
- 外部组件技术,允许它们在瘦客户端和 Web 客户端中使用
设计特色
在 JavaScript 中实现上述所有内容并不容易。 也许 1C Web 客户端是用 JavaScript 编写的最大的客户端应用程序之一 - 大约 450.000 行。 我们在 Web 客户端代码中积极使用面向对象的方法,这简化了如此大的项目的工作。
为了最小化客户端代码的大小,我们首先使用我们自己的混淆器,并从平台版本8.3.6(2014年XNUMX月)开始我们开始使用
- 自己的混淆器 – 1556 kb
- Google 闭包编译器 – 1073 kb
与我们自己的混淆器相比,使用 Google Closure Compiler 帮助我们将 Web 客户端的性能提高了 30%。 此外,应用程序消耗的内存量减少了 15-25%(取决于浏览器)。
Google Closure Compiler 可以很好地处理面向对象的代码,因此它对于 Web 客户端的效率是尽可能高的。 闭包编译器为我们做了一些好事:
- 项目构建阶段的静态类型检查(确保我们用 JSDoc 注释覆盖代码)。 结果是静态类型,与 C++ 中的类型非常接近。 这有助于在项目编译阶段捕获相当大比例的错误。
- 通过混淆减少代码大小
- 对执行代码进行多项优化,例如:
- 内联函数替换。 在 JavaScript 中调用函数是一项相当昂贵的操作,频繁使用的小方法的内联替换可显着加快代码速度。
- 在编译时计算常量。 如果表达式依赖于常量,则常量的实际值将代入其中
我们使用 WebStorm 作为我们的 Web 客户端开发环境。
对于代码分析,我们使用
我们已经/正在解决什么问题?
在项目实施过程中,我们遇到了许多必须解决的有趣问题。
与服务器以及窗口之间交换数据
在某些情况下,源代码的混淆可能会干扰系统的运行。 由于混淆,Web 客户端可执行代码外部的代码可能具有与我们的可执行代码期望的函数和参数名称不同的函数和参数名称。 我们的外部代码是:
- 来自服务器的代码以数据结构的形式
- 另一个应用程序窗口的代码
为了避免与服务器交互时出现混淆,我们使用@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;
}
为了避免与其他窗口交互时出现混淆,我们使用所谓的导出接口(导出所有方法的接口)。
/**
* Экспортируемый интерфейс контрола 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 (){}
我们在虚拟 DOM 成为主流之前就使用了它)
与所有处理复杂 Web UI 的开发人员一样,我们很快意识到 DOM 不太适合使用动态用户界面。 几乎立即,虚拟 DOM 的类似物就被实现来优化 UI 的工作。 在事件处理期间,所有 DOM 更改都存储在内存中,并且只有当所有操作完成时,累积的更改才会应用到 DOM 树。
优化网络客户端
为了让我们的网络客户端运行得更快,我们尝试最大限度地使用标准浏览器功能(CSS等)。 因此,表单命令面板(几乎位于应用程序的每个表单上)都是使用浏览器工具、基于 CSS 的动态布局专门呈现的。
测试
对于功能和性能测试,我们使用专有工具(用 Java 和 C++ 编写)以及基于
我们的工具是通用的 - 它允许您测试几乎所有窗口程序,因此适合测试瘦客户端和 Web 客户端。 该工具将启动 1C 应用程序解决方案的用户的操作记录到脚本文件中。 同时,记录屏幕工作区域(标准)的图像。 监控新版本的 Web 客户端时,无需用户参与即可播放脚本。 如果屏幕截图在任何步骤中与参考屏幕不匹配,则测试被视为失败,之后质量专家将进行调查以确定这是一个错误还是系统行为的计划更改。 如果出现计划行为,标准将自动替换为新标准。
该工具还可以测量应用程序性能,精度高达 25 毫秒。 在某些情况下,我们会循环部分脚本(例如,多次重复订单输入)来分析执行时间随时间的下降情况。 所有测量的结果都记录在日志中以供分析。
我们的测试工具和被测应用程序
我们的工具和 Selenium 相辅相成; 例如,如果其中一个屏幕上的某个按钮改变了位置,Selenium 可能不会跟踪这一点,但我们的工具会注意到,因为将屏幕截图与标准进行逐像素比较。 该工具还能够跟踪处理键盘或鼠标输入的问题,因为这正是它再现的内容。
对这两种工具(我们的工具和 Selenium)的测试都运行我们应用程序解决方案中的典型工作场景。 1C:Enterprise 平台每日构建后会自动启动测试。 如果脚本速度较慢(与之前的版本相比),我们会调查并解决速度缓慢的原因。 我们的标准很简单 - 新版本的运行速度不应比前一个版本慢。
开发人员使用不同的工具来调查减速事件; 主要用
另外,性能下降的原因之一可能是 Google Closure Compiler 由于某种原因无法执行函数的内联替换(例如,因为函数是递归函数或虚拟函数)。 在这种情况下,我们尝试通过重写源代码来纠正这种情况。
浏览器扩展
当应用程序解决方案需要 JavaScript 中不可用的功能时,我们使用浏览器扩展:
- 使用文件
- 用于密码学工作
- 与...一起工作
外部元件
我们的扩展由两部分组成。 第一部分是所谓的浏览器扩展(通常是用 JavaScript 编写的 Chrome 和 Firefox 扩展),它与第二部分交互 - 一个实现我们需要的功能的二进制扩展。 应该提到的是,我们编写了 3 个版本的二进制扩展 - 适用于 Windows、Linux 和 MacOS。 二进制扩展作为 1C:Enterprise 平台的一部分提供,位于 1C 应用程序服务器上。 当第一次从 Web 客户端调用时,它会下载到客户端计算机并安装在浏览器中。
当在 Safari 中运行时,我们的扩展使用 NPAPI;当在 Internet Explorer 中运行时,它们使用 ActiveX 技术。
进一步发展
Web 客户端开发团队的任务之一是进一步开发功能。 Web 客户端的功能应与瘦客户端的功能相同;所有新功能均在瘦客户端和 Web 客户端中同时实现。
其他任务包括开发架构、重构、提高性能和可靠性。 例如,方向之一是进一步转向异步工作模型。 Web 客户端的某些功能当前构建在与服务器交互的同步模型上。 异步模型现在在浏览器中变得越来越重要(而不仅仅是在浏览器中),这迫使我们通过用异步调用替换同步调用来修改 Web 客户端(并相应地重构代码)。 逐渐过渡到异步模型的原因是需要支持已发布的解决方案及其逐步适应。
来源: habr.com