Pluto is a typesetting rendering engine on iOS that can easily describe interface elements through JSON/JS files, with high development efficiency, and guarantees smoothness and memory.
Qzone Feed has a complex business and many styles. Each time a new Feed style is added, the development workload is large and needs to follow the version. The coupling between styles is severe, and changing one style may affect another style.
Under such a background, the Feed Team urgently needs a more mature rendering engine, which needs to solve at least the following problems:
Based on these requirements, we have re-examined the more mature typesetting rendering engines on the market, such as ComponentKit, new development frameworks like React Native, and even referring to Android's typesetting system. All of them have difficulty solving the above problems simultaneously, so we finally decided to create our own engine, Pluto, which focuses on performance and development efficiency. Currently, Pluto has been applied to most of the space Feed styles on QQ and Qzone. I will explain the details of the comparison between Pluto and these existing typesetting rendering engines later. First, let's see how Pluto works.
First, we need a typesetting description:
Next, we will focus on describing click events, templates, reuse, and extensions. These will address the issues of hot updates, development efficiency, performance optimization, and feature expansion, respectively.
Pluto can also bind event response JS scripts through JSON descriptions, such as the following JSON file:
When the image component is clicked, the corresponding JS file can be found and the onClick method is called.
In the initial example, a UI layout was expressed using a single JSON file. If there are many similar interface elements existing simultaneously, there will be many JSON files with duplicate data. At this point, the template feature is needed. For example, each Cell in a list shares the same template, but the data filled in is different. In the example below, replace "imageName" with "${image}" instead of a specific image. Then provide a dictionary mapping, where each data item maps to different data, generating different views.
Reuse is a factor that was heavily considered in the initial design of Pluto and is the biggest feature that sets it apart from other engines. It can be said that this feature, can be applied to scenarios with high-performance requirements, such as lists. Reuse refers to the fact that in list controls like UITableView, different list items reuse the same Cell, and the view data inside the Cell can be reused. The core purpose is to reduce the number of times views are created and modified. It is worth emphasizing that the optimization of view reuse in Pluto is a built-in feature, and callers do not need to perform additional operations.
The implementation principle is shown in the following figure. Each view generated by Pluto contains a reuse pool. When using the reuse feature, the view will not be destroyed but recycled. During recycling, its subviews will be placed in the reuse pool (the subviews here are a view tree, including the subviews of the subviews) and hidden. When reusing, you only need to restore the display and adjust the width, height, data, etc., which is much lower in cost than creating a view.
Pluto has built-in rendering controls, such as TextItem, ImageItem, and ButtonItem. If these components do not meet the requirements, you can also complete the encapsulation through extension components. The general process for implementing custom controls is as follows:
Here, let's summarize some features of Pluto.
Fast: Pluto's typesetting performance is not much different from the performance of directly written typesetting code. Currently, compared to other lists on mobile QQ, its smoothness is leading. (Of course, its performance is much better than Auto Layout)
Asynchronous: Pluto's entire typesetting and text rendering process can be executed in the work thread and is thread-safe. It will not affect user operations.
Descriptive Typesetting: Pluto receives typesetting information as a dictionary, expressing typesetting information descriptively without writing logic code. The benefits of making it descriptive are:
● Easy to maintain and less prone to errors (because it does not contain code).
● Easy to cache.
● Can be written in JSON format, typesetting through local reading or background delivery.
● Immutable data: In the entire typesetting engine, typesetting data is immutable, which means it is easy to maintain, test, cache, reuse, and achieve thread safety, etc.
Caching and Reuse: Since the typesetting data is immutable, internal caching is performed to speed up the entire display process. The rendering part will try to reuse existing controls as much as possible to speed up rendering. At the same time, the typesetting data updates of the same view will also be compared internally for differentiation, and the typesetting process will reuse the old typesetting information as much as possible.
Template System: Pluto has a complete template function. The same style only needs to be written once to form a template, and then different data can be filled into the template according to the situation to generate different views. Templates can also be reused through combinations.
Rich Text: Pluto supports the mixed layout of basic controls, such as Text tags and image tags, and also supports custom tags and custom controls.
Before the comparative analysis, let's first review the main use case of Pluto: a Feed list that can load more Feeds infinitely, requiring good performance in memory, CPU, and smoothness.
For this scenario, we will compare the existing mainstream UI development libraries, including Xcode's built-in Storyboard/Xib, Facebook-led open-source components ReactNative and ComponentKit, and Pluto in this article.
● Storyboard is a visual UI editing tool with high development efficiency. In terms of performance, it uses native controls, so the performance is somewhat inferior. It also does not support asynchronous typesetting, which affects smoothness. The generated files use XML descriptions, which can theoretically be delivered dynamically, but the XML format is not public, and compatibility is not guaranteed between versions, making it difficult to achieve dynamic delivery.
● React Native uses JS+HTML for development, with high development efficiency. It also has high dynamism and cross-platform features. However, its performance is somewhat lacking, with some issues in speed and memory usage, making it difficult to use in high-performance scenarios like Feed streams.
● ComponentKit is very similar to Pluto, with the biggest difference being that Component does not support JSON/XML static expression styles or event dynamic binding functions. In terms of dynamism and maintainability, it is much weaker. We have considered adding JSON expression style functionality to ComponentKit. However, ComponentKit directly uses native views without an intermediate virtual view layer, so there are performance issues as well. The cost of modification is too high.
● Compared to React Native, Pluto has fewer components, but using JSON can bring the development efficiency close to React Native in terms of typesetting description. Its performance is quite good compared to other components; asynchronous support ensures the smoothness of the main thread; its dynamism is similar to React Native, but it cannot add new controls, and the controls are all locally embedded. It relies more on local code logic and cannot modify existing local code logic, but it can replace some parts. Of course, if the logic can only be embedded, there will be no risk of review.