web前端应对4k屏幕

    科技2022-07-12  136

    web前端应对4k屏幕

    In the GOGOX client app, one of the rapidly evolving screens is the delivery package information screen. The complexity grows in the following ways:

    在GOGOX客户端应用程序中,快速发展的屏幕之一是交货包裹信息屏幕。 复杂性以下列方式增长:

    Rich experiments. We have done a lot of experiments to improve the UI/UX design. The screen is really dynamic and controlled by feature flags. There are 17 feature flags for this single screen. Some experiments are minor changes such as adding a merchant order number component, and some of them are major changes such as replacing the size and weight component with brand new design and rearrange the components layout of the screen.

    丰富的实验 。 我们已经做了很多实验来改善UI / UX设计。 屏幕实际上是动态的,由功能标志控制。 此单个屏幕有17个功能标志。 一些实验是较小的更改,例如添加了商家订单号组件,而有些则是重大更改,例如以全新的设计替换了尺寸和重量组件,并重新排列了屏幕的组件布局。

    Multiple countries support. The user interface and behavior may be different for each country to cater to country-specific needs.

    多个国家的支持 。 每个国家/地区的用户界面和行为可能有所不同,以满足特定国家/地区的需求。

    Multiple account types support. The user interface and behavior may be different for personal and business users. For example, we present a set of predefined size options for personal users but allow custom sizing for business users.

    多种账户类型支持 。 个人和企业用户的用户界面和行为可能有所不同。 例如,我们为个人用户提供了一组预定义的大小选项,但允许企业用户自定义大小。

    Without a proper design, the screen complexity will grow out of control as we have to take into account different combinations of scenarios. It is tempting to embed if-else conditions inline for different scenarios. However, the logic becomes tightly coupled and hard to understand, which makes it easy to break the working code unintentionally. For instance, updating the feature for country A may break the feature for country B. As a result, the developer productivity will be slowed down as part of the time is wasted to fix bugs and puzzle over workarounds. And product manager also has to compromise the pace of feature rollout.

    如果没有适当的设计,屏幕的复杂性将失去控制,因为我们必须考虑到场景的不同组合。 可以将if-else条件嵌入到不同的场景中,这很诱人。 但是,逻辑变得紧密耦合且难以理解,这使得容易无意间破坏工作代码。 例如,更新国家(地区)的功能可能会破坏国家(地区)的功能。结果,开发人员的工作效率将降低,这是因为浪费了一部分时间来修复错误并困扰于解决方法。 产品经理还必须牺牲功能推出的速度。

    我们的目标是什么? (What are our goals?)

    🎯 Easy to adapt to changing requirements.

    🎯易于适应不断变化的要求。

    The meaning of the term easy may be different for different people. To define the definition in our case:

    对于不同的人,“ 容易 ”一词的含义可能有所不同。 在我们的案例中定义定义:

    Easy to add a feature — We can add a new component in isolation without changing the existing working code.

    易于添加功能 -我们可以单独添加新组件,而无需更改现有工作代码。

    Easy to remove a feature — We can remove a component by just deleting the linkages.

    易于删除的功能 -我们只需删除链接即可删除组件。

    Easy to update a feature — We can update a component in isolation without creating side effects breaking other components.

    易于更新功能 -我们可以隔离更新组件,而不会产生破坏其他组件的副作用。

    如何实现我们的目标? (How to accomplish our goals?)

    One of the key techniques to tackle software complexity is the separation of concerns. A concern is the details of a specific building block. For example, in mobile app development, a screen is composed of smaller components. The presentation and behavior of a single component is a concern that we want to separate from other components; the composition of all the components in a screen and interaction between them are other concerns.

    解决软件复杂性的关键技术之一是关注点分离 。 一个问题是特定构建基块的详细信息。 例如,在移动应用程序开发中,屏幕由较小的组件组​​成。 单个组件的表示形式和行为是我们要与其他组件分开的关注点; 屏幕中所有组件的组成以及它们之间的交互是其他问题。

    When requirements change, you extend the behaviour of such modules by adding new code, not by changing old code that already works. — Robert C. Martin

    当需求改变时,您可以通过添加新代码而不是通过更改已经起作用的旧代码来扩展此类模块的行为。 -罗伯特·马丁

    Unsurprisingly, a well-known approach can help accomplish our goals — Open Closed Principle. Let’s see how to apply this approach to make the package information module open for extension, but close for modification.

    毫不奇怪,众所周知的方法可以帮助实现我们的目标- 开放式封闭原则 。 让我们看看如何应用这种方法使包信息模块可以扩展,但可以修改。

    1.分解屏幕 (1. Decomposing screen)

    The term “component” is abstract and the implementation may differ in backend and frontend. Basically, it is a building block encompassing cohesive pieces of functions and data in order to encapsulate reusable application functionality. In mobile app development, it is a molecule or an organism in terms of atomic design.

    术语“组件”是抽象的,其实现在后端和前端可能有所不同。 基本上,它是一个包含功能和数据的内联块的构建块,以便封装可重用的应用程序功能。 在移动应用程序开发中,就原子设计而言,它是分子或有机体。

    将屏幕分成组件 (Breaking the screen into components)

    Breaking a big screen into small independent components may roughly reduce its complexity by N times with N being the number of components, so we can focus on one component at a time. If you have no idea of how to break down the screen, the designer can help you with this because they are the authors of the screen. Cooperating with them can lead us to produce a set of reasonable small components. Furthermore, the article Package by Feature by Philipp Hauer has explained why this approach is better than package by technical concerns.

    将大屏幕拆分为小的独立组件可能会使其复杂度大致降低N倍,其中N为组件数,因此我们可以一次专注于一个组件。 如果您不知道如何分解屏幕,设计人员可以帮助您,因为他们是屏幕的作者。 与他们合作可以使我们生产出一系列合理的小零件。 此外,Philipp Hauer 的Feature by Package文章解释了为什么这种方法比技术上的考虑要好。

    组件结构 (Component structure)

    In the GOGOX client app, we have adapted RIBs as a high-level architectural pattern to encapsulate the app state as a tree, and we will use components as the internal pattern of the node if the node is complex enough.

    在GOGOX客户端应用程序中,我们已将RIB调整为高级架构模式,以将应用程序状态封装为树,并且如果节点足够复杂,我们将使用组件作为节点的内部模式。

    State

    A Presentation Model encapsulates the state and behavior of the component. This is the model shared in the component as the single source of truth. The formula UI = f(state) denotes that the UI of the component can be derived from the state. The state model coordinates with the domain layer and the UI layer to provide an interface to the view. Minimizing the decision making in the view helps identify the culprit of UI bugs. For example, the description text is computed from the user property. If the final result of the description is not expected, it is most likely caused by either the wrong user state or wrong computation logic.

    表示模型封装了组件的状态和行为。 这是组件中共享的模型,是真实的唯一来源。 公式UI = f(state)表示可以从状态导出组件的UI。 状态模型与域层和UI层协调以提供视图的接口。 最小化视图中的决策有助于确定UI错误的元凶。 例如, description文字是根据user属性计算的。 如果不希望最终的描述结果,则很可能是由于错误的user状态或错误的计算逻辑引起的。

    To test whether the transformations of the state are expected, it is simple to feed different inputs and verify the outputs using parameterized tests, no mocking framework is required; it is also fast, no UI environment is required.

    为了测试是否需要状态转换,使用参数化测试输入不同的输入并验证输出很简单,不需要模拟框架; 它也很快,不需要UI环境。

    View

    视图

    A platform UI renders the component with state and emits events. This is the place to interact with the platform such as accessing system photo library. The application level UI logic are already pulled to the state model and been well tested, so the main responsibility of View is making the UI beautiful and pixel perfect.

    平台用户界面使用状态呈现组件并发出事件。 这是与平台进行交互的地方,例如访问系统照片库。 应用程序级别的UI逻辑已经被拉到状态模型中,并且已经过良好的测试,因此View的主要职责是使UI美观和像素完美。

    The view is composed using the UI components from the GOGOX mobile design system, in which we use screenshot tests to guard against broken UI.

    该视图是使用GOGOX移动设计系统中的UI组件组成的,在其中,我们使用屏幕截图测试来防止损坏的UI。

    Presenter

    主持人

    A handler handles the application logic which coordinates the domain layer and UI layer. It observes the input events (from view or other external systems such as Pusher) and processes them. For example, PhotoPresenter observes the photo picked event and save the photo to the order stream for later processing.

    处理程序处理协调域层和UI层的应用程序逻辑。 它观察输入事件(从视图或其他外部系统,例如Pusher )并进行处理。 例如, PhotoPresenter观察照片拾取事件并将照片保存到订单流中以供以后处理。

    To test whether the presenter works as intended, we stub the collaborators (e.g. input event stream and state model) and verify the interaction logic using behavior verification.

    为了测试演示者是否按预期工作,我们对协作者(例如输入事件流和状态模型)进行存根,并使用行为验证来验证交互逻辑。

    Component

    零件

    An aggregate object groups Presenter and View as a single unit. It plays the facade role to communicate with others within the host module. The other components or the host module should not be aware of the internal details of the component.

    聚合对象将Presenter和View分组为一个单元。 它扮演立面角色,以便与主机模块中的其他人进行通信。 其他组件或主机模块不应了解该组件的内部详细信息。

    To test whether the component is integrated correctly as a whole unit, we use real or fake collaborators for integration tests.

    为了测试组件是否正确集成为一个整体,我们使用真实或虚假的合作者进行集成测试。

    Builder

    建造者

    A factory encapsulates the component creation logic. We can config the component with different State/Presenter/View combinations based on the application requirements (e.g. country, account type). At GOGOX, we use split.io feature management platform to manage the requirements. Moving the component configuration to a remote service enables dynamic and targeted feature rollout. The target rule system controls the enablement of the component, we can easily enable/disable the component or update the rules remotely without any changes in the app.

    工厂封装了组件创建逻辑。 我们可以根据应用程序要求(例如国家/地区,帐户类型)为组件配置不同的State / Presenter / View组合。 在GOGOX,我们使用split.io功能管理平台来管理需求。 将组件配置移至远程服务可启用动态和目标功能。 目标规则系统控制组件的启用,我们可以轻松地启用/禁用组件或远程更新规则,而无需在应用程序中进行任何更改。

    To test whether the component configuration logic is correct, we can stub the application requirements and verify if the correct State/Presenter/View combinations are provided using state verification. The remote target rules will be tested by our QAs manually because those are external dependencies that the app doesn’t own directly, mocking what you don’t own makes no sense. The tests passed, but that is fake news, the actual things won’t work.

    为了测试组件配置逻辑是否正确,我们可以对应用程序需求进行存根,并使用状态验证来验证是否提供了正确的State / Presenter / View组合。 远程目标规则将由我们的质量检查人员手动测试,因为这些是应用程序不直接拥有的外部依赖关系, 嘲笑您不拥有的东西是没有道理的 。 测试通过了,但这是假新闻,实际情况不起作用。

    RIB或组件? (RIB or component?)

    The major factors to determine the choice is whether the module is intended for reuse and the underlying context. A RIB module is self-contained and able to be attached as a child node to any parent node conforming to its contract, which makes it reusable. For example, the payment footer is actually a RIB and be attached to the package info RIB as a child node. The footer is shared between the transport service and delivery service and perhaps other future services, so it is a good candidate to make it as a RIB. On the other hand, in the case of the package info node, we found that making each component as a RIB makes not much sense as the Router doesn’t fit into any role and will make the structure more complex. Also, the components are not intended for reuse, it’s unlikely to be shared because they are designed for delivery package information context in mind.

    确定选择的主要因素是模块是否打算重用以及其基础上下文 。 RIB模块是自包含的,可以作为子节点连接到符合其合同的任何父节点,这使得它可以重复使用。 例如,付款页脚实际上是RIB,并作为子节点附加到包裹信息RIB。 页脚在运输服务和交付服务以及可能的其他将来服务之间共享,因此使其成为RIB是一个很好的选择。 另一方面,在包信息节点的情况下,我们发现将每个组件都设置为RIB并没有多大意义,因为路由器不适合任何角色,并且会使结构更加复杂。 而且,这些组件不打算重用,因为它们是为交付包信息上下文而设计的,因此不太可能共享。

    2.连接组件 (2. Connecting components)

    We have a set of isolated pluggable components at the current stage. The next step is straightforward. The PackageInfoBuilder installs the component builders and provides an organised list of components based on the application requirements. Then we connect these components in the PackageInfoInteractor and PackageInfoScreen.

    当前阶段,我们有一组隔离的可插拔组件。 下一步很简单。 PackageInfoBuilder将安装组件构建器,并根据应用程序要求提供有组织的组件列表。 然后,将这些组件连接到PackageInfoInteractor和PackageInfoScreen 。

    够容易吗? (Is it easy enough?)

    要添加功能, (To add a feature,)

    Create a component in isolation.

    隔离创建一个组件。

    Register the component to the host module.

    将组件注册到主机模块。

    2.1. Install the builder.

    2.1。 安装构建器。

    2.2. Add it to the component list.

    2.2。 将其添加到组件列表。

    要删除功能, (To remove a feature,)

    Delete the component package.

    删除组件包。

    Unregister the component from the host module.

    从主机模块中注销该组件。

    2.1. Uninstall its builder.

    2.1。 卸载其生成器。

    2.2. Remove it from the component list.

    2.2。 从组件列表中删除它。

    要更新功能, (To update a feature,)

    Find the component by the package name.

    通过软件包名称查找组件。 Update it based on the requirement changes.

    根据需求更改进行更新。

    服务器驱动的用户界面怎么样? (How about Server-Driven UI?)

    The idea of Server-Driven UI (SDUI) is simple: given the app has defined a set of components, the server returns instructions to tell the app what to render and how to interact with the server. SDUI has different maturity levels:

    服务器驱动的UI(SDUI)的想法很简单:给定应用程序定义了一组组件,服务器返回指令以告诉应用程序呈现什么以及如何与服务器交互。 SDUI具有不同的成熟度级别:

    Level 1 — Server returns content and visual structure to instruct the client what components should be rendered, and most of the logic still lies on the client-side. This is more like a CMS approach that provides a way to config the server response. It is a good fit for a campaign driven app so that the marketing and product teams have a portal to control how the app changes. One of the good examples is the real-time survey feature.

    级别1-服务器返回内容和可视化结构,以指示客户端应呈现哪些组件,并且大多数逻辑仍位于客户端。 这更像是CMS方法,它提供了一种配置服务器响应的方法。 它非常适合以广告系列驱动的应用程序,因此营销和产品团队具有门户网站来控制应用程序的更改方式。 实时调查功能就是一个很好的例子。

    Level 2 — Server returns content, visual structure, and actions to instruct the client what components should be rendered and how the components should perform actions such as navigation, asynchronous request. Most of the logic lies on the server-side. Tom Lokhorst had written a great article about this topic in detail.

    级别2-服务器返回内容,视觉结构和操作,以指示客户端应呈现哪些组件以及这些组件应如何执行诸如导航,异步请求之类的操作。 大多数逻辑都位于服务器端。 汤姆·洛霍斯特(Tom Lokhorst)详细撰写了一篇有关该主题的精彩文章 。

    Do we need SDUI in our app? Unsurprisingly, the answer is “it depends”. Given we have implemented a set of reusable native components, the effort to update the screen by either backend or frontend is not much different. The question should be changed to “Should we trade simplicity for speed of iteration?”. Inevitably, adopting SDUI requires upfront investment in time and engineering resources, and on top of the complexity of the backend system, it also adds an additional level of complexity to the system.

    我们的应用程序中需要SDUI吗? 毫不奇怪,答案是“取决于情况”。 鉴于我们已经实现了一组可重用的本机组件,通过后端或前端来更新屏幕的工作没有太大不同。 应该将问题更改为“ 我们是否应该为了简化迭代速度而交换简单性? ”。 不可避免地,采用SDUI要求对时间和工程资源进行前期投资,并且除了后端系统的复杂性之外,还增加了系统的复杂性。

    关键点 (Key Points)

    Componentization results in high cohesive and loosely coupled module. The module is open for extension but closed for modification, it can behave in new and different ways by plugging in different components based on the application requirements. This enables our rapid product iteration, and it will improve our developer productivity as the complexity is under control.

    组件化导致高凝聚力和松散耦合的模块。 该模块可以扩展,但可以修改,但可以根据应用需求插入不同的组件,从而以新的和不同的方式运行。 这使我们能够快速进行产品迭代,并且由于控制了复杂性,因此将提高开发人员的生产率。 There’s no silver bullet, it is not possible that this solution will work for all the apps magically as the context are different, even the complexity of each screen within the same app is also different. Therefore, it makes more sense to use polyglot patterns in an application to tackle different problems.

    没有灵丹妙药,因为上下文不同,即使同一应用程序中每个屏幕的复杂性也不同,该解决方案也不可能神奇地适用于所有应用程序。 因此,在应用程序中使用多语言模式来解决不同的问题更有意义。 When we attempt to design a solution to attack a problem, “How to reduce the cost of change” will be a crucial question that the solution needs to answer. Making the solution to adhere OCP is one of the approaches.

    当我们尝试设计解决问题的解决方案时,“如何降低变更成本”将是解决方案需要回答的关键问题。 使解决方案符合OCP是其中一种方法。

    翻译自: https://medium.com/gogox-technology/tackling-complex-screen-e79857c6f79a

    web前端应对4k屏幕

    Processed: 0.011, SQL: 8