前端开发 转unity
by Maciej Gurban
通过Maciej Gurban
Unity仪表板-汲取的经验教训可扩展我们的前端,开发文化和流程 (Unity Dashboard — lessons learned scaling our frontends, development culture, and processes)
At Unity, we’ve recently set out to improve our Dashboards — an undertaking which dramatically changed not only our frontend tech stack, but also the ways we work and collaborate.
在Unity,我们最近着手改进仪表板-这项工作不仅极大地改变了我们的前端技术堆栈,而且极大地改变了我们的工作和协作方式。
We’ve developed best practices and tooling to help us scale our frontend architecture, build products with great UX and performance, and to ship new features sooner.
我们已经开发了最佳实践和工具,以帮助我们扩展前端体系结构,构建具有出色UX和性能的产品以及更快地发布新功能。
This article gathers these practices and aims to provide as much reasoning behind each decision as possible. But first, some context.
本文收集了这些实践,旨在为每个决策提供尽可能多的理由。 但是首先,一些背景。
传统 (The Legacy)
Looking at the number of engineers, Unity more than quadrupled its headcount in the last 4 years. As the company grew both organically and through acquisitions, its product offering grew as well. While the products developed originally at Unity were largely uniform in terms of tech and design language, the newly acquired ones naturally were not.
从工程师人数来看,Unity在过去4年中的员工人数增长了三倍多。 随着公司有机增长和通过收购实现增长,其产品供应也随之增长。 虽然最初在Unity开发的产品在技术和设计语言方面基本上是统一的,但是新收购的产品自然不是。
As a result we had multiple visually distinct dashboards which worked and behaved differently and which shared no common navigational elements. This resulted in poor user experience and frustrated users. In the very literal sense, the state of frontends of our products was costing us revenue.
结果,我们有了多个视觉上截然不同的仪表板,它们的工作方式和行为均不同,并且没有共享通用的导航元素。 这导致不良的用户体验和沮丧的用户。 从字面上看,我们产品的前端状态使我们损失了收入。
After analyzing the portfolio of our products, we’ve elicited three distinct sections Unity Dashboard would be split into: Develop, Operate and Acquire , each satisfying a different business need and meant for different customer groups, thus containing feature sets largely independent from each other.
在分析了我们产品的组合之后,我们得出了Unity Dashboard的三个不同部分:开发,操作和获取,分别满足不同的业务需求并针对不同的客户群,因此包含彼此基本独立的功能集。
This new structure, and the introduction of common navigational elements aimed to solve the first major issue our users were facing — where to find the information and configuration options they’re looking for, and while it all looked good on paper, the journey how to get there were far from obvious.
这种新结构以及引入通用导航元素的目的是解决用户所面临的第一个主要问题-在哪里可以找到他们正在寻找的信息和配置选项,尽管在纸面上看起来都不错,但是该旅程如何到达那里远非显而易见。
注意事项 (Considerations)
Many of our developers were very excited about the possibility of moving to React and its more modern tech stack. As these solutions had been battle tested in large applications, and had their best practices and conventions mostly ironed out, things looked very promising.
我们的许多开发人员对迁移到React及其更现代的技术堆栈的可能性感到非常兴奋。 这些解决方案已经在大型应用程序中经过了实战测试,并且大多数最佳实践和惯例都被淘汰了,因此事情看起来很有希望。
Nevertheless, what our developers knew best and what most of our actively developed applications were written in was AngularJS. Deciding to start migrating everything in one go would have been a disaster waiting to happen. Instead we set out to test our assumptions on a much smaller scale first.
尽管如此,我们的开发人员最了解的是什么,而大多数积极开发的应用程序是用AngularJS编写的。 决定一次迁移所有内容将是一场灾难,等待发生。 相反,我们着手首先以较小的规模测试我们的假设。
Perhaps the most disjointed group of products we’ve had were the Monetization dashboards. These projects, which would eventually end up under the umbrella of the Operate dashboard, were vastly different in almost any way possible: technologies used, approach to UI/UX, development practices, coding conventions — you name it.
也许我们拥有的最脱节的产品是获利仪表板 。 这些项目最终将在Operate仪表板的保护下结束,在几乎所有可能的方式上都大不相同:所使用的技术,UI / UX的方法,开发实践,编码约定-随便您。
Here’s what the situation roughly looked like:
情况大致如下:
After some brainstorming we identified the main areas which we’d need to work on to bring all the products together:
经过一些头脑风暴,我们确定了将所有产品整合在一起所需的主要领域:
1.单一产品 (1. A single product)
We needed these dashboards (split across multiple applications, domains and tech stacks) to:
我们需要这些仪表板(拆分成多个应用程序,域和技术堆栈)来:
- Feel like a single product (no full page redirects as the user navigates through pages of all the different applications) 感觉像一个产品(当用户浏览所有不同应用程序的页面时,没有完整的页面重定向)
- Have a consistent look and feel 具有一致的外观
- Include common navigational elements are always visible and look the same, no matter which part of the dashboard the user is visiting 无论用户正在访问仪表板的哪个部分,包含通用导航元素始终可见并且外观相同
2.旧版支持 (2. Legacy support)
While we did have a clean slate when it comes to the technology choice of our new frontend solution, we had to accommodate for the legacy projects which needed to be integrated into the new system. A solution, which didn’t involve big refactoring efforts, and which wouldn’t stop feature development, or drag for months without end in sight.
尽管在新前端解决方案的技术选择方面确实做到了明确,但我们必须适应需要集成到新系统中的旧项目。 一个解决方案,不需要大量的重构工作,也不会停止功能开发,也不会拖延几个月而无人问津。
3.实践和工具 (3. Practices and tooling)
While nearly all the teams used AngularJS, different tools were being used to address the same set of challenges. Different test runners and assertion libraries, state management solutions or lack thereof, jQuery vs native browser selectors, SASS vs LESS, charting libraries etc.
尽管几乎所有团队都使用AngularJS,但使用了不同的工具来应对相同的挑战。 不同的测试运行程序和断言库,状态管理解决方案或缺少它们,jQuery与本机浏览器选择器,SASS与LESS,图表库等。
4.开发人员的生产力 (4. Developer productivity)
Since every team had their own solution to developing, testing and building their application, the development environment was often riddled with bugs, manual steps, and inefficiencies.
由于每个团队都有自己的开发,测试和构建应用程序的解决方案,因此开发环境中经常会出现bug,手动步骤和效率低下的情况。
Additionally, many of our teams work in locations separated by a 10 hour difference (Helsinki, Finland and San Francisco), which makes efficient decision-making on any shared pieces a real challenge.
此外,我们的许多团队在相差10小时的地点(赫尔辛基,芬兰和旧金山)工作,这使得对任何共享作品进行有效决策成为一个真正的挑战。
新的 (The New)
Our main areas of focus were to:
我们的主要重点领域是:
- Encourage and preserve agile ways of working in our teams, and to let the teams be largely independent from one another 鼓励和保留敏捷的团队工作方式,并让团队在很大程度上彼此独立
- Leverage and develop common tooling and conventions as much as possible, to document them, and make them easily accessible and usable 尽可能利用和开发通用工具和约定,以对其进行记录,并使其易于访问和使用
We believed that achieving these the goals would significantly improve our time to market and developer productivity. For that to happen, we required a solution which would:
我们认为,实现这些目标将大大缩短我们的上市时间和开发人员的生产力。 为此,我们需要一个解决方案,该解决方案将:
Build product features with better user experience
建立具有更好用户体验的产品功能
Improve code quality
提高代码质量
Allow for better collaboration without blocking anybody’s work progress in the process.
允许更好的协作,而不会妨碍任何人在此过程中的工作进度。
We also wanted to encourage and ease-in the move to a modern tech stack to make our developers more satisfied with their work, and to over time move away from our antiquated frameworks and tooling.
我们还希望鼓励并简化向现代技术堆栈的过渡,以使我们的开发人员对他们的工作更加满意,并逐渐摆脱我们过时的框架和工具。
The ever-evolving result of our work is a React-based SPA built inside a monorepository where all the pages and bigger features get built into largely independent code bundles loaded on demand, and which can be developed and deployed by multiple teams at the same time.
我们工作不断发展的结果是在单一存储库中构建基于React的SPA,其中所有页面和更大的功能都内置到按需加载的很大程度上独立的代码束中,并且可以由多个团队同时开发和部署。
As a means of sandboxing all the legacy applications but still displaying them in the context of the same new application, we load them inside an iframe from within which they can communicate with the main SPA using a message bus implemented using the postMessage()
API.
作为对所有旧应用程序进行沙箱处理,但仍将它们显示在同一新应用程序上下文中的一种方法,我们将它们加载到iframe中,在其中它们可以使用通过postMessage()
API实现的消息总线与主SPA通信。
单一存储库 (The monorepository)
Here’s the directory structure we started out with:
这是我们开始的目录结构:
/src /components /scenes /foo /components package.json foo.js /bar /components package.json bar.js package.json index.js
The package.json
in the root directory contains a set of devDependencies
responsible for development, test and build environment of the whole application, but also contains dependencies
of the core of the application (more on that a bit later).
根目录中的package.json
包含一组devDependencies
负责整个应用程序的开发,测试和构建环境,但还包含应用程序核心的dependencies
项(稍后会详细介绍)。
All the larger UI chunks are referred to as scenes. Each scene contains a package.json
where dependencies
used by that scene’s components are defined. This makes two things possible:
所有较大的UI块均称为场景 。 每个场景都包含package.json
,其中定义了该场景的组件所使用的dependencies
。 这使两件事成为可能:
Deployment updates only the files which have changed
部署仅更新已更改的文件
The build step compiles separate vendor and app bundles for each scene, naming each using a hash which will change only when contents of the file have changed. This means our users only download files which have changed since their last visit, and nothing more.
构建步骤针对每个场景分别编译供应商和应用程序捆绑包,并使用散列命名每个散列,仅当文件内容更改时,散列才会更改。 这意味着我们的用户仅下载自上次访问以来已更改的文件,仅此而已。
Scenes are loaded only when needed
仅在需要时才加载场景
We load all scenes asynchronously and on demand which drastically improves the load times of the whole application. The “on demand” here usually means visiting a specific route, or performing a UI action which performs a
我们异步并按需加载所有场景,从而大大缩短了整个应用程序的加载时间。 这里的“按需”通常是指访问特定的路线,或执行执行以下操作的UI操作:
动态模块导入 。
Here’s how such setup looks in practice (simplified for readability):
这是实际中这种设置的外观(为便于阅读而简化):
// In src/routes.jsconst FooLoader = AsyncLoadComponent( () => import(‘src/scenes/foo/foo’), GenericPagePreloader,);
<Route path=”/foo” component={FooLoader) />
// In src/scenes/foo/foo.js<React.Suspense fallback={GenericPagePreloader}> <Component /></React.Suspense>
The AsyncLoadComponent
is a thin wrapper around React.lazy()
, additionally accepting a preloader component, the same one passed through fallback to React.Suspense()
, and a delay after which the preloader should be rendered if the scene hasn’t finished loading.
AsyncLoadComponent
是React.lazy()
一个瘦包装器,还接受一个预加载器组件,该组件通过回退传递到React.Suspense()
,并且延迟了一个延迟,如果场景尚未完成加载,则应渲染该预加载器。
This is useful when making sure our users see the same preloader without any interruption or flash of content from the moment a scene is requested to the moment when all of its files have been downloaded, all of the critical API requests have completed, and the component has finished rendering.
当确保我们的用户从请求场景到下载所有文件,完成所有关键API请求以及该组件的过程中,我们的用户看到的是相同的预加载器而没有任何内容中断或内容闪烁时,此功能非常有用完成渲染。
组件层 (Component tiers)
As each application grows, its directory structure and abstractions evolve along with it. After roughly half a year of building and moving features to the new codebase, having a single components directory proved insufficient.We needed our directory structure to inform us about:
随着每个应用程序的增长,其目录结构和抽象也随之发展。 经过大约半年的构建并将功能迁移到新代码库后,仅拥有一个components目录被证明是不够的。我们需要使用目录结构来告知我们:
- Have the components been developed to be generic, or are they meant only for a specific use-case? 这些组件是否已开发为通用的,还是仅针对特定用例?
- Are they generic enough to be used across all the application, or should they used only in the certain contexts? 它们是否通用到足以在所有应用程序中使用,还是仅应在某些情况下使用?
- Who’s responsible for and most knowledgable about the code? 谁负责并最了解代码?
Based on that we’ve defined the following Component Tiers:
基于此,我们定义了以下组件层 :
1.特定于应用程序(src / app) (1. Application-specific (src/app))
Single-use components which cater to specific use-cases within this application, and which are not meant to be re-used or extracted to the component library (routes, footer, page header etc.).
满足此应用程序中特定用例的一次性组件,并且不打算被重复使用或提取到组件库(路由,页脚,页面页眉等)。
2.通用(src /组件) (2. Generic (src/components))
Generic multi-purpose components to be used all across the application and its scenes. Once we’ve arrived at a stable API for these components, they could be moved into the common component library (more on that below)
通用多功能组件可在整个应用程序及其场景中使用。 一旦我们为这些组件找到了稳定的API,就可以将它们移到公共组件库中了(下面有更多内容)
3.单个场景的组件(src / scenes / my-scene / components) (3. Components of a single scene (src/scenes/my-scene/components))
Components developed with a specific use case in mind; not meant to be used in any other scenes. For cases when a component from one scene needs to be used in another one, we’d use:
考虑到特定用例开发的组件; 不打算用于任何其他场景。 对于一个场景中的某个组件需要在另一个场景中使用的情况,我们将使用:
4.多场景组件(src / scenes / components / my-feature) (4. Multi-scene components (src/scenes/components/my-feature))
Components used across multiples scenes, but not meant to be generic enough to be used anywhere else. To illustrate why simply moving them to src/components
isn’t good enough:
跨多个场景使用的组件,但并不意味着通用性足以在其他任何地方使用。 为了说明为什么仅将它们移动到src/components
还不够好:
Imagine that so far you’ve had a single scene which contained components used to build some rather specific data charts. Your team is now building a second scene which will use different data for the charts, but visually the two will look pretty much the same.
想象一下,到目前为止,您只有一个场景,其中包含用于构建一些相当特定的数据图表的组件。 您的团队现在正在构建第二个场景,该场景将为图表使用不同的数据,但是在视觉上,两者看起来几乎相同。
Importing components from one scene into another would break the encapsulation of the scene and would mean that we can no longer be certain whether changes made to a single scene’s components only affect that one scene.
将组件从一个场景导入到另一个场景将破坏场景的封装,这意味着我们不再可以确定对单个场景的组件所做的更改是否仅会影响该场景。
For this purpose, any component or group of components, roughly referred to as a feature, would be placed in src/scenes/components
from where it can be imported and used by any other team, however:
为此,可以将任何组件或一组组件(大致称为功能)放置在src/scenes/components
中,其他任何团队都可以从中导入和使用它,但是:
Whenever a team would like to start using scene components which another team developed, the best practice would be to reach out to that team first to figure out whether the use case you intend these components for can safely be supported in the future. Giving a heads up to the team who originally developed the code will prevent shipping broken features in the future when code you’ve taken into use inevitably gets changed in ways you didn’t expect (because of course, how could you!), and which might not always be caught by the unit tests.
每当一个团队想要开始使用另一个团队开发的场景组件时,最佳实践就是首先与该团队联系,以确定将来是否可以安全地支持您打算将这些组件用于的用例。 与最初开发该代码的团队保持联系,可以防止将来当您使用的代码不可避免地以意想不到的方式发生变化时(当然,因为您怎么可能!)更改已发布的功能,以及这可能并不总是被单元测试捕获。
5.公共图书馆 (5. Common library)
Components which we’ve battle-tested in production and want to extract to our shared component library, used by other dashboard teams at Unity.
我们已经在生产中进行了实战测试的组件,希望将其提取到共享的组件库中,供Unity的其他仪表板团队使用。
颂扬共享依赖 (Ode to shared dependencies)
While it would be very convenient to be able to build and deploy every piece of our application in a fully isolated environment, certain dependencies — both external libraries and internal application code — are simply going to be used all across the codebase. Things like React itself, Redux and all redux-related logic, common navigational components etc.
能够在完全隔离的环境中构建和部署我们的应用程序的每一部分将非常方便,但是某些依赖项(包括外部库和内部应用程序代码)将仅在整个代码库中使用。 像React本身,Redux以及所有与Redux相关的逻辑,通用导航组件之类的东西。
推出变更 (Rolling out the changes)
At the moment, fully encapsulating the scenes isn’t practical and in many cases simply impossible. It would take either shipping many dependencies multiple times over and in the process slowing down pages loads, or building abstractions meant to make certain libraries work in aways they’ve not been designed to.
目前,完全封装场景是不实际的,在许多情况下根本不可能。 它可能需要多次交付许多依赖项,并在此过程中减慢页面加载速度,或者需要构建抽象化结构以使某些库在设计初衷时能够正常工作。
As the web development and its ecosystem evolves though, the libraries seem to become more and more standalone and encapsulated, which we hope in the future will mean little to no shared dependencies, and true isolation between all the modules.
但是,随着Web开发及其生态系统的发展,这些库似乎变得越来越独立和封装,我们希望在将来这将意味着几乎没有共享的依赖关系以及所有模块之间的真正隔离。
Perhaps the biggest drawback of authoring large-scale applications is performing code changes and dependency updates without breaking something in the process
编写大型应用程序的最大缺点可能是执行代码更改和依赖更新,而又不会破坏流程
Using a monorepository makes it possible (though not mandatory) to roll out changes and updates to the code in more gradual and safe manner — if a change causes issues, these issues will only affect a small part of the application, not the whole system.
使用单一存储库可以(尽管不是强制性的)以更渐进和安全的方式推出对代码的更改和更新-如果更改引起问题,则这些问题只会影响应用程序的一小部分,而不会影响整个系统。
And while for some the ability to perform updates on multiple unrelated areas of the codebase at the same time would come off as a benefit, the reality of having multiple teams working on the same codebase and not knowing all the other teams’ features thoroughly means that a great deal of caution is needed when building the application scaffolding and taking measures to minimize the risk of breakage.
虽然对于某些人而言,可以同时在代码库的多个不相关区域执行更新会有所好处,但实际上有多个团队在同一个代码库上工作,而又不完全了解其他所有团队的功能,这意味着在构建应用程序支架并采取措施以最大程度地降低损坏风险时,需要非常谨慎。
如何避免破东西 (How to avoid breaking things)
Perhaps the most fundamental strategy which helps us to do so, other than scene isolation, is having a high unit test coverage.
除了场景隔离以外,也许最有助于我们做到这一点的基本策略是拥有较高的单元测试覆盖率 。
Testing
测试中
The unit tests aren’t of course everything — many mature products on even a moderate scale do after all invest in suites of integration and e2e tests which do a better job at verifying whether the application works as expected overall. However, as the number of features grows so does the maintenance cost and time needed to run them — a cost which cannot always be justified for less crucial but still important features.
单元测试当然不是所有的东西,即使是中等规模的许多成熟产品,毕竟都投资了集成和e2e测试套件,可以更好地验证应用程序是否按预期整体工作。 但是,随着功能部件数量的增加,维护成本和运行这些功能所需的时间也增加了-对于不太重要但仍然很重要的功能部件,总不能证明这是合理的。
Some lessons we’ve learned from various testing strategies:
我们从各种测试策略中学到了一些教训:
- Try to unit test as much of the code as possible, especially: conditional logic, data transformations and function calls 尝试对尽可能多的代码进行单元测试,尤其是:条件逻辑,数据转换和函数调用
- Invest in and leverage integration tests to their full extent before deciding to write any e2e tests. The initial cost of integration tests is much higher, but pales in comparison to the price of upkeep of an e2e suite 在决定编写任何端到端测试之前,请充分利用并充分利用集成测试。 集成测试的初始成本要高得多,但与e2e套件的维护价格相比却相形见pale
- Try not to over-react by starting to write e2e tests for things that weren’t caught by unit or integration tests. Sometimes, the processes or tooling are at fault 开始编写针对单元或集成测试未发现的东西的端到端测试,以免React过度。 有时,过程或工具有问题
- Let test cases explain UI behavior rather than implementation details 让测试用例解释UI行为而不是实现细节
- Automated tests cannot fully replace manual testing 自动化测试无法完全替代手动测试
2. Minimize the surface of shared code
2.最小化共享代码的表面
Aside from testing, code re-used across the whole application is kept to a reasonable minimum. One of the most useful strategies so far has been to move the most commonly used components and code to a shared component library, from where they are used as dependencies in scenes which need them. This allows us to roll out most of the changes progressively, on a per team- or page-basis.
除了测试之外,整个应用程序中重复使用的代码也保持在合理的最低限度。 到目前为止,最有用的策略之一是将最常用的组件和代码移到共享的组件库中,从那里将它们用作需要它们的场景中的依赖项。 这样,我们就可以基于每个团队或每个页面逐步推出大多数更改。
3. Accountability
3.问责制
Last but not least, a huge factor in multiple teams being able to collaborate within the same codebase comes from encouraging and having developers take personal responsibility and accountability for the product, instead of offloading the responsibility for properly testing that everything works to Q.A., testers or automation.
最后但并非最不重要的一点是,多个团队能够在同一个代码库中进行协作的一个巨大因素来自鼓励并要求开发人员对产品承担个人责任和问责制 ,而不是将对所有正常工作进行正确测试的责任转移给QA,测试人员或自动化。
This carries over to code reviews as well. Making sure each change is carefully reviewed is harder than it might seem on the surface. As team works closely together, a healthy degree of trust is developed between its members. This trust however, can sometimes translate into people being less diligent about changes made by the more experienced or otherwise trustworthy developers.
这也将继续进行代码审查。 确保仔细检查每个更改比表面上看起来要难。 随着团队紧密合作,成员之间建立了健康的信任度。 但是,这种信任有时会转化为人们对经验丰富或值得信赖的开发人员所做的更改不那么勤奋。
To encourage diligence, we emphasize that the author of the PR and the reviewer are equally responsible for ensuring everything works.
为了鼓励勤奋工作,我们强调PR的作者和审稿人对确保一切正常工作负有同等责任 。
组件库 (Component library)
To achieve the same look and feel across all the pages of our dashboards, we’ve developed a component library. What stands in our approach, is that new components are almost never developed within that library.
为了在仪表板的所有页面上实现相同的外观,我们开发了一个组件库。 我们的方法所代表的是,几乎从未在该库中开发新组件。
Every component, after being developed within the dashboard’s codebase, is taken into use in a bunch of features within that codebase first. Usually after a few weeks we begin to feel more confident that the component could be moved over, given that:
在仪表板的代码库中开发完每个组件后,首先要在该代码库中的一系列功能中使用它们。 通常在几周后,鉴于以下原因,我们开始更加自信可以移开组件:
- The API is flexible enough to support the foreseeable use-cases 该API足够灵活以支持可预见的用例
- The component has been tested in a variety of contexts 该组件已在多种情况下进行了测试
- The performance, responsiveness, and UX are all accounted for 性能,响应能力和用户体验都被考虑在内
This process follows the Rule of Three and aims to help us release only components which are truly reusable and have been taken into use in a variety of contexts before being moved to our common library.
此过程遵循“三规则” ,旨在帮助我们仅释放真正可重用的组件,并在移至我们的公共库之前已在各种环境中使用。
Some of the examples of the components we’d move over would include: footer, page header, side and top navigation elements, layout building blocks, banners, powered-up versions of buttons, typography elements etc.
我们将要移动的组件的一些示例包括:页脚,页面页眉,侧面和顶部导航元素,布局构建块,横幅,上电的按钮版本,版式元素等。
In the early days, the component library used to be located in the same codebase as the application itself. We’ve since then extracted it to a separate repository to make the development process more democratized for other teams at Unity — important when driving for its adoption.
在早期,组件库曾经与应用程序本身位于同一代码库中。 从那以后,我们将其提取到一个单独的存储库中,以使开发流程对于Unity的其他团队更加民主化-在推动其采用时很重要。
模块化组件设计 (Modular component design)
For the longest time, building reusable components meant dealing with multiple challenges, many of which often didn’t have good solutions:
在最长的时间内,构建可重用的组件意味着要应对多个挑战,其中许多挑战通常没有好的解决方案:
- How to easily import the component along with its styles, and only that 如何轻松导入组件及其样式,仅此而已
- How to override default styles without selector specificity wars 如何在没有选择器特异性冲突的情况下覆盖默认样式
- In bigger components consisting of multiple smaller ones, how to override the styling of the smaller component 在包含多个较小组件的较大组件中,如何覆盖较小组件的样式
Our dashboard, as well as our component library heavily depend on and utilize Material UI. What’s uniquely compelling in Material UI’s styling solution is the potential brought by JSS and their Unified Styling Language (well worth the read), which make it possible to develop UIs encapsulated by design like in the case of CSS Modules, and solve of the above mentioned issues in a stride.
我们的仪表板以及我们的组件库严重依赖并利用Material UI 。 在Material UI的样式解决方案中,唯一引人注目的是JSS及其统一样式语言 (非常值得一读)带来的潜力,这使得开发像CSS模块一样通过设计封装的 UI成为可能,并解决了上述问题。问题大步向前。
This differs significantly from approaches like BEM which provide encapsulation by convention which tend to be less extensible and less encapsulated.
这与BEM之类的方法大不相同, BEM 按照惯例提供了封装,而这些封装往往难以扩展且封装较少。
生活风格指南 (Living styleguide)
A component library wouldn’t be complete without a way to showcase the components it contains and being able to see the components as they change throughout the releases.
如果没有一种方法来展示其包含的组件并能够在整个发行版中更改它们的组件,就无法完成一个组件库。
We’ve had pretty good experience with Storybook which was ridiculously easy to setup and get started with, but after some time we realized a more robust and end-to-end solution was needed. Pretty close to what Styleguidist offers, but more tailored to our needs.
我们在Storybook上拥有相当不错的经验,该故事非常易于设置和入门,但是一段时间后,我们意识到需要一个更强大且端到端的解决方案。 与Styleguidist提供的产品非常接近,但更适合我们的需求。
现有设计文档 (Existing design docs)
The documentation serving as the main source of information about the latest design specification was located in Confluence, where designers kept an up-to-date specification for each component using screenshots illustrating permitted use-cases, states and variations the component could be in, listed best practices, as well as details like dimensions, used colors etc. Following that approach we’ve faced a number of challenges:
作为最新设计规范信息的主要信息源的文档位于Confluence,在此处设计人员使用屏幕快照保留了每个组件的最新规范,这些屏幕快照说明了允许使用的情况,状态以及该组件可能存在的状态和变体,列出了这些信息最佳做法,以及尺寸,使用的颜色等详细信息。按照这种方法,我们面临许多挑战:
Material design specification keeps evolving and because of that we oftentimes found ourselves either spending time on updating all the screenshots and guidelines, or let our design guidelines become outdated
材料设计规范不断发展 ,因此,我们经常发现自己要么花时间更新所有屏幕截图和准则,要么让我们的设计准则过时
Figuring out which is more correct: implementation or specification wasn’t always an easy task. Because we’ve been publishing Storybook demos of every component and for every library version, we could see what and how changed. We couldn’t do the same for the design spec.
找出哪个更正确:实现或规范并不总是一件容易的事。 因为我们一直在发布每个组件和每个库版本的Storybook演示,所以我们可以看到更改内容和更改方式。 对于设计规范,我们不能做同样的事情。
Screenshots and videos can only communicate as much. To provide components of high quality and which can be used by multiple teams it’s necessary to review whether each component works in all resolutions, is bug-free and has good UX — this was difficult without having the designer sit literally next to you to see the implementation demo being shown on the screen
屏幕截图和视频只能进行尽可能多的交流 。 为了提供高质量的组件,并且可以供多个团队使用,有必要检查每个组件是否在所有分辨率下都可以正常工作,是否没有错误以及是否具有良好的用户体验-如果没有设计师坐在您旁边的话,这很难做到。屏幕上显示了实施演示
组件文档应用 (Component documentation app)
Our documentation app aims to provide the means of efficient collaboration between designers and engineers to make it simpler and less time-consuming for both parties to document, review and develop components. To be more specific, we needed to:
我们的文档应用旨在为设计人员和工程师之间提供有效的协作方式,从而使双方进行文档,审核和开发组件的过程变得更加简单而省时。 更具体地说,我们需要:
Have a single point of reference showcasing the components, how should they look, behave, and be used — provided for every release — replacing detailed descriptions with live demos
有一个单一的参考点展示组件 ,如何 它们的外观,行为和使用方式-每个版本均提供-用实时演示替换详细说明
Make it as easy for designers and developers to collaborate on components and their docs and do so before the components are released — without the need of sharing videos, screenshots, or being physically in the same location
使设计人员和开发人员可以轻松地在组件及其文档上进行协作 ,并且在组件发布之前就可以轻松进行协作 -无需共享视频,屏幕截图或实际位于同一位置
Separate the designs into what we plan to do vs what has been done
将设计分为我们计划要做的与已经完成的事
Similarly like before, each release of the component library causes a new version of the living styleguide to be published. This time over however, there are a few differences:
与以前类似,组件库的每个发行版都会导致发布新版本的live styleguide。 但是,这次有一些区别:
Designers contribute to component documentation directly by editing documentation files through the Github UI, committing changes to the latest release.
设计人员可以通过Github UI编辑文档文件,将更改提交到最新版本, 从而直接为组件文档做出贡献 。
Component demos as WYSIWYG — the same code you see as an example of how to implement the component is used to render the demo, including any intermediate file imports, variable declarations etc. As an added bonus, components wrapped in
withStyles()
are displayed correctly (issue present in Storybook at the moment).组件演示为WYSIWYG(所见即所得) —您以示例方式看到的代码来实现该示例,以呈现该演示,包括任何中间文件导入,变量声明等。此外,正确封装在
withStyles()
中的组件可以正确显示(此问题目前在Storybook中存在)。Changes to the docs and the code are almost instantly visible without checking out the branch locally and starting the documentation app — the app is rebuilt and published on and for every commit.
对文档和代码的更改几乎可以立即看到,而无需在本地签出分支并启动文档应用程序-每次提交时都会重新构建和发布该应用程序。
开发经验 (Development experience)
One of the main goals of code reviews is making sure that each change is carefully reviewed, considered and tested before being merged and deployed.
代码审查的主要目标之一是确保在合并和部署每个变更之前都经过仔细的审查,考虑和测试。
To make this task as obstacle-free as possible we’ve developed a Preview Server capable of creating a new build of our application every time a PR is created or updated.
为了使这项任务尽可能地无障碍,我们开发了一种预览服务器 ,该服务器能够在每次创建或更新PR时创建应用程序的新版本。
Our designers, product managers and engineers can test each change before merging it in, in both staging and production environments and within minutes of making the change.
我们的设计师,产品经理和工程师可以在阶段和生产环境中以及在进行更改的几分钟内,在将每个更改合并之前对其进行测试。
结束语 (Closing words)
It’s been nearly a year since we’ve undertaken to consolidate our dashboards. We’ve spent that time learning how to grow a large but healthy software project, how to get better at collaboration and communication, and how to raise the quality bar for ourselves.
自从我们承诺整合仪表板以来已经过去了一年。 我们花了很多时间来学习如何发展一个大型而健康的软件项目,如何在协作和交流中变得更好,以及如何为自己提高质量标准。
We scaled a frontend project not only in terms of lines of code, but also in terms of number of engineers who work within its codebase — a number which quadrupled since the beginning.
我们不仅按照代码行来扩展前端项目,而且还根据在其代码库中工作的工程师数量进行规模扩展,该数量自开始以来已翻了两番。
We did a 180 degree change in dealing with time differences between our teams, moving away from a model where our teams worked in full isolation to one where close collaboration and communication are an everyday occurrence.
我们在处理团队之间的时差方面进行了180度更改,从完全隔离团队工作的模型转变为每天都进行紧密协作和沟通的模型。
While we still have a long road ahead to ensure we can scale our approach to more teams and to bigger challenges, we’ve noticed a number of improvements already:
尽管我们还有很长的路要走,以确保我们可以将方法扩展到更多团队和更大挑战,但我们已经注意到许多改进:
Roadmap and work visibility
路线图和工作可见性
Due to having one place where all the work is happening, the progress gets tracked, and all the issues are gathered in
由于只有一个地方可以进行所有工作,因此可以跟踪进度,并且可以收集所有问题
Development velocity and time-to-market
开发速度和上市时间
New features can be created in large part from already existing and well-tested components — easily findable through our documentation app
大部分新功能可以从已经存在且经过测试的组件中创建-可通过我们的文档应用轻松找到
Code quality & test coverage
代码质量和测试范围
When building new things, a solution to a similar problem usually already exists and is at a hand’s reach, along with examples how to test it
在构建新事物时,通常已经存在解决类似问题的解决方案,并且提供了如何进行测试的示例
Overall quality & UX
总体质量和用户体验
Testing features and ensuring their quality is now easier than ever, as designers, product managers and other stakeholders can test each change on their own machine, with their own accounts and data sets
现在,设计人员,产品经理和其他利益相关者可以使用自己的帐户和数据集在自己的机器上测试每项更改,从而比以往更轻松地测试功能并确保其质量
Naturally, along the way we’ve encountered a number of challenges which we need to solve, or which will need solving in the future:
当然,在此过程中,我们遇到了许多需要解决或将来需要解决的挑战:
Build & CI performance
构建和CI绩效
As the numbers of dependencies, build bundles, and tests grow, as does the time needed to do a deployment. In the future, we’ll need to develop tooling to help us only build, test and deploy the pieces which changed.
随着依赖关系,构建包和测试数量的增加,进行部署所需的时间也随之增加。 将来,我们将需要开发工具来帮助我们仅构建,测试和部署已更改的部分。
Development culture
发展文化
To build healthy software, we need to continuously work on healthy ways of communicating and exchanging ideas, and text-based communications make this task more difficult. We’re working to address this issue through a series regular leadership training sessions and embracing a more open-source ways of working, as well as organizing a few get together sessions per year for the teams to meet each other face to face.
要构建健康的软件,我们需要继续以健康的方式交流和交换想法,而基于文本的交流使这一任务更加困难。 我们正在通过一系列定期的领导力培训课程来解决这个问题,并采用更加开放源代码的工作方式,并每年组织几次聚会,以使团队能够面对面地交流。
Breakage isolation & updates
破损隔离和更新
As the number of features and pages grows, we’ll need a more robust way of isolating our application modules to prevent damage from spreading for when things go wrong. This could be achieved by versioning all the shared code (redux logic, src/components), or in extreme cases producing standalone builds of certain features.
随着功能和页面数量的增加,我们需要一种更健壮的方法来隔离我们的应用程序模块,以防止发生错误时损坏扩散。 这可以通过对所有共享代码(redux逻辑,src /组件)进行版本控制来实现,或者在极端情况下生成某些功能的独立版本。
陈述现在,现在和将来 (State then, now and in the future)
The migration has involved moving away from AngularJS to React. Here’s how the situation changed over the past year:
迁移涉及从AngularJS迁移到React。 以下是过去一年中情况的变化:
It’s a wrap! Thank you for reading! You can find me on LinkedIn here.
包好了! 感谢您的阅读! 你可以找到我在LinkedIn 在这里 。
If working on similar challenges sounds interesting to you, we’re always looking for talented engineers to join our teams all around the world.
如果应对类似的挑战对您来说很有趣,我们一直在寻找有才华的工程师加入我们遍布全球的团队。
前端开发 转unity