In the past, when we say that a certain function is multi-functional, it usually means that it can be implemented between different types of devices, such as PC and mobile devices. Or to be more specific, I mean “cross-platform,” which can be as big as it is across operating systems (Windows, macOS, Linux, iOS, Android, etc.) or as small as it is across different implementation libraries of a particular technology.

But today we are going to look at scenarios for various environments across the MVVM architectural pattern.



Chameleon is an open source cross-end solution that aims to unify the MVVM cross-end environment so that any terminal designed using the MVVM architecture can be developed and run using it.

In such an MVVM environment, which involves Weex, React-Native, WebView/ browser, and Flutter, as well as the specific business products they implement, For example, wechat applets, fast applications, Alipay applets, Baidu intelligent applets, toutiao applets and other kinds of applets.

You may find that there are many kinds of “mini programs” mentioned here. Although there were many negative voices when the concept of wechat mini programs even appeared in the early version, with its continuous development, it has now become an indispensable application form in public life.

Ma Huateng revealed that as of November 2018, there were 1.5 million wechat mini program developers, the number of mini program applications exceeded 1 million, covering more than 200 subsectors, and the daily active users reached 200 million. This success and the huge traffic entry point that touches almost every aspect of life, everyone wants to get in, so you can see other companies coming up with similar mini-program solutions.

On the other hand, in addition to small programs, in 2018, xiaomi, Huawei, OPPO and other 10 Android phone manufacturers also formed a fast application alliance, and has released a series of fast applications.

Chameleon aims to cross these ends, and as more and more different implementations are implemented, cross-end scenarios are getting more complex. We interviewed Zhang Nan, the founder of Chameleon, and asked him to share with readers the growth of Chameleon in this process.

The address of the project: https://github.com/didi/chameleon

This article is the first time that the implementation principle of Chameleon has been made public!

Excess dry goods, including:

  • Terminal development: The future of development

  • Implementation principle of Chameleon cross-end

  • Comparison of current cross-end schemes (various small programs, fast applications, etc.)

  • Taro

  • Difficulties and thoughts encountered in the process of evolution

Why did you develop Chameleon?

A good place to start is with industry background.

According to the Statistics report on Internet Development in China released by China Internet Network Information Center (CNNIC), as of June 2018, the number of Internet users in China had reached 802 million, with wechat 1 billion, Alipay 400 million and Baidu 330 million living on a monthly basis. On the other hand, in Q3 2018, Android phones in China accounted for more than 80% of the overall proportion of smart phones, living about 600 million a month.

BAT and Android have become the real user entrance to the Internet in China. As long as the entry level apps with high traffic want to be platforms, they want to become an ecological platform and Internet traffic entrance. With the access of a large number of third-party apps, the company APP can be associated with more interests of enterprises from the business layer and have stronger vitality. From the technical level, “local capability interface layer” can be used to collect a large amount of user data. From consumer Internet to industrial Internet, a large number of basic user data clues from all walks of life are needed to drive and make decisions.

In this context, combined with the history of the development of computer technology, we know that every new technology will go through a “separate” stage, small program technology is no exception, so we see the emergence of various other small program platform.

As the founder of wechat small program, although other small programs have deliberately imitated the technical implementation principle and interface design, as a front-line developer to release small programs on different platforms, they often need repeated development and testing, and the workload of 1 unit has become N units. And that’s not even taking into account other portals like Kuaiapps.

In this case, Didi’s r&d engineers are one of the most obvious “victims”. Didi Chuxing has related portals in wechat Wallet, Alipay and Android Express app, and the proportion of user traffic is not low.

The r&d students pursue both the flexibility of H5 in the end, and the performance close to the original. In the face of entrance expansion, main end, independent end, wechat small program, Alipay small program, Baidu small program, Android manufacturers alliance fast application, single function in each platform have to be repeated, development and maintenance costs have increased exponentially.

There was an urgent need for a solution that could build multiple entries by maintaining one set of code, so we set about creating a project called Chameleon (CML, Camillon) that really focused on making one set of code run multiple times.

Chameleon uses MVVM architecture at its core. Why can it span multiple ends?

MVVM is also known as Model View ViewModel. It is essentially an evolved version of MVC (Model View Controller), which abstracts the state and behavior of the View and separates the View UI from the business logic.

It is a mode for data-driven reflection of views, which may now be moving away from its original purpose, becoming more of a “communication protocol” between view data, making terminal development simpler. This is a trend that will be adopted by all future frameworks.



Facebook opened source React in 2013. React was itself a Web UI engine that grew into the React Native project to write Native mobile applications. This is what brought the MVVM pattern to the cross-side direction.

Vue. Js was released around 2014 and has occupied a large number of user groups against the current. Alibaba also released Weex project based on it in 2016, making it possible to write Native apps with Vue.

Google released Flutter 1.0.0 for the future across Android and iOS at the end of 2018.

The principle of

We know that terminal development is inseparable from three elements — interface performance (structure, appearance) layer, logical processing layer and system interface layer (network, storage and media, etc.).



Developers write code in the initialization phase (life cycle) calls “interface layer” model of the interface drawing interface, when users touch interface, the interface performance layer “send events to the user” logical processing layer “, the latter after condition judgment to deal with and feedback to the user interface, the process may need to call the system interface layer, The feedback process calls the interface of the “interface presentation layer”.

Under the conventional terminal development architecture mode, no matter it is Web, Android or iOS terminal project development, it strongly depends on the environmental interface of each terminal, especially on the interface related model design.

Drawing interface in iOS system is based on UIKit framework in Objective-C language environment. In Android system, the user drawing interface is based on Java language environment, and LayoutInflater processes XML structure hierarchy tree. The Web side uses DOM model and CSS to describe the drawing interface.

The key in MVVM is that it completely isolates the interface from the logical layer through the ViewModel layer, which is responsible for associating the interface performance with the response events (UPDATE /notify) of the logical processing layer. The communication between the upper and lower layers of this “isolation layer” is normative and pure enough.

Model logic processing is pure business response logic, any language can be implemented, you can use Android Java, iOS Objective-C, you want to use “the world’s first language PHP” can also be implemented.

The popular choice of JavaScript is largely due to its obvious advantages in this area, such as low learning cost, natural cross-end properties, virtual machine (V8, JavaScriptCore) and component construction in all directions, and active ecology.

The system interface layer is even simpler, requiring only unified base interface + extensible interface capabilities.

Various MVVM schemes

Take a look at what the various MVVM solutions look like.

React Native, Weex, and fast application MVVM



The code written by the developer runs in the virtual machine (V8, JavaScriptCore), which contains the extended system base interface. Runtime, will describe the data interface (mainly described the CSS + DSL) through the communication layer is passed to the Android, iOS, the rendering engine, users touch interface, through the communication layer to the inside of the virtual machine business processing code, business processing code can call network, storage and media interface, such as the feedback to the interface again.

Flutter of MVVM

The biggest difference between Flutter and RN is that it replaces “JavascriptCore/V8+JS” with “engine implemented in C++ + Framework implemented in Dart+ static type Dart+ compiled into machine code”.

The solution of Flutter is shown below:


The Service is the local capability interface layer, and the Widget tree is the view layer model.


The use of Flutter and RN are similar In design. The Flutter documentation states that “In Flutter, almost everything is a widget.” Calls to widgets change from RN’s JSX to widget calls to Flutter, The appearance description of the UI ranges from RN’s CSS (text styles, Layout models, box models) to custom Flutter Widgets (textStyle, Layout Widgets, widgets).


In essence, Flutter is also an MVVM architecture. The logical layer notifies the view layer of updates through setState. To some extent, this is why Flutter can be transformed into a Web framework. Business code is not deeply dependent on the view-model specific to either side.

MVVM for all kinds of small programs

React Native uses WebView as a rendering engine, while React Native uses a separate rendering engine (so many CSS layouts are not supported).


How is it implemented on Chameleon?

First, any high-level Language code block at the application layer is divided into several layers: Language, framework, and Library:

  • Language – In layman’s terms, the basic logical commands needed to implement a program: logical judgments (if), loops (for), function calls (foo(), etc.

  • Framewrok — In plain English, the specification required to complete an App interaction task, such as life cycle (onLoad, onShow), modularity and data management, etc.

  • Library – Methods encapsulate collections. For example, in the Web front end, Vue is better called a framework, while jQuery is better called a library. Android System under the activity Manager + Window Manager View System collection called framework, and SQLite, liBC is more suitable for libraries.

This corresponds to Chameleon:

Specific to the implementation principle, the panoramic architecture diagram is as follows:


You can understand that Chameleon has done the following to achieve its goal of “unifying MVVM across cross-end environments” :

  • Standard Language(CML DSL), Framework and Library (built-in components and apis) protocol layers are defined.

  • Translate the DSL into an end-to-end DSL at offline compile time, compiling only code that is basic and stable enough at the Language level.

  • At each end of the implementation of the Framework was unified, in each end as far as possible to use the original Framework, convenient use of its ecology, so that many components can be directly used.

  • Library (built-in components and apis) is implemented at each end of the runtime.

  • Provides polymorphic protocol for users to facilitate the expansion of the above aspects of the content, access to the special properties of the bottom end, and improve maintainability.



The implementation idea is very simple, all designed for MVVM standardization, do not do unnecessary design, so the macro perspective, just like Node.js (Libuv) running on Windows and macOS system, provide a cross-platform abstraction layer.

From an MVVM perspective:

  • View (Presentation layer)

    • There are frameworks available, including Vue for browsers, apet Engine for Webview, React Native/Weex Engine for Android and iOS, and even Dart Framework for Flutter.

    • Chameleon built-in component library: Polymorphic protocol defines unified components such as View, input, text, block and cell. It is the original base class of the interface group layer and derives complex interface functions.

  • ViewModel (association layer) Chameleon syntax translation

    • Component calls

    • cycle

    • conditional

    • Event callback association

    • Father and son

  • Model (Logical Response Layer)

    • JavaScript code

    • CML Runtime framework

    • Chameleon API: Polymorphic protocol defines unified interface, cmL. request, CML. store, etc

Chameleon’s cross-terminal solution brings great convenience to the development of developers. What is the specific performance?

Bottom line: Development based on Chameleon will become more and more efficient.

The emergence of each end makes the workload of 1 become N times due to the existence of multiple ends. With Chameleon, the workload will be changed back to 1.2. This additional 0.2 effort is to deal with the differentiation of functions on each end, such as the following scenario:

  • When a service line migrates to Chameleon, it finds that there is no “Passport login component”, and the secret login can be exempted in various small programs. The login box pops up on the Web and Native end. Different services have different user interaction patterns, so Chameleon does not provide any component. Developers need to extend a single login component < Passport /> based on polymorphic protocols, which returns a login callback token anyway, without the external component caring about what happens inside.

  • Users need the sharing function. There is no “share component”, so they can share in the upper right corner of wechat Web and directly share in the mini program. Different services have different interaction patterns, and users need to expand a separate login component <share/> based on the polymorphic protocol.

With the accumulation of business, such examples with large differences in each end can be changed into separate maintenance of one business component after another, and there is no need to repeat development later. Moreover, the product experience is promoted to be consistent, and the three-layer structure of components “built-in components of CML framework ->CML extension components -> polymorphic components extended by business developers themselves” has reached 100% unity.

As components accumulate less business development work, engineers can focus on more meaningful things, which is why Chameleon exists.

Based on the uniform cross-end abstraction, users in the continuous maintenance process of Chameleon project, after Chameleon releases a new end, your business code can be released into the new end without any changes.

For example, the CML-Yanxuan project supports three terminals during development, and the small program terminals of Baidu and Alipay are added later. The original code can run five terminals directly, and what you see at one end means what you see at multiple terminals.

Can only run 3 ends in development



The original code seamlessly supports five ends


In addition, it is particularly emphasized that large company teams with strong technical skills want to develop code in their own hands and have better control over the output. In fact, the built-in components and built-in apis of Chameleon can be replaced, so all components are developed by the business side. When you don’t want to use Chameleon, you can directly export the native components and leave Chameleon, as shown below:

At present, Taro is the most eye-catching among the multi-party unified plans. Could you please make a specific comparison between Chameleon and Taro?

We think the biggest difference between Chameleon and other solutions is that other frameworks are enhanced with small programs, which means to write small programs with Vue or React. The official access examples of these frameworks are also running wechat small programs.

They are more similar to Chameleon’s predecessor MPV (Mini Program View), which considers how to enhance applets development. When wechat mini program was released in 2017, Didi, as a whitelisted user, first tried to access it and began to face the problem of repeated development.

At this time, we specially set up a small project team to complete a project named MPV. The goal of the first phase was to “achieve a set of code to run Web and wechat applet without affecting the user’s performance and relying on the principle of the framer”.

It looks very nice. Using such a scheme to achieve the Web side and the small program side, it does achieve more than 90% code reuse. Overall, the development efficiency and testing efficiency have been improved to some extent, but it is not really unified across multiple sides.

In general, the differences between Chameleon and Taro can be classified as follows:


Each item in the table needs to be considered when doing a cross-end solution. We said that except Chameleon, other schemes are only enhancing small programs, or imitating the API and component interface design of wechat small programs.

Taro is to convert JSX into a small program template to simulate the interfaces and components of wechat small programs on other ends, so that other ends are more like wechat small programs. When business development is inconsistent, environment variables need to judge the differences and call them separately, which will cause the difference logic and product logic to be mixed together.

In addition, it will follow the applets update, the business side will have a double dependency; Other end and small programs can not keep consistent, users to a variety of differentiation compatible, not conducive to maintenance.

The Chameleon? Chameleon took these issues into account and developed the pseudo-span MiniProgram View into a true multi-span solution during the evolution of the early pseudo-span MiniProgram View.

The previous table shows that Chameleon considers both uniformity and variability, and variability does not affect maintainability; When the differences are really too great, don’t use one set of code to implement multiple ends on the same page, but to unify common components.

That’s just to compare Chameleon’s overlap with Taro’s, but remember that Chameleon isn’t just a front-end framework, it’s:

  • There is also a unified Chameleon Native SDK. Chameleon not only hopes to unify all kinds of small programs, but also to cover its own APP. It will continue to expand APIS and components through Native SDK, expecting to have the same local capabilities as small programs. Ideally, a single set of code would run seamlessly in a variety of small programs, in your own APP.

  • Open source back-end management system is yet to be developed.

  • There is also an open source XEdtior non-development editor that can edit cross-site pages and publish directly.

In addition, the future will bring the following capabilities:

  • Backend unified interface (message push, sharing and payment, etc.)

  • Based on the unified MVVM standard, there is also a native APP based on Flutter

The current variety of applets and Native cross-end frameworks is similar to the era when multiple browsers were popular, such as Safari, Chrome, Firefox, IE 6/7/8/9 and Android. By analogy, Chameleon’s interface components are designed more like jQuery.

Some are XHRHttprequest, others are ActiveXObject, jQuery considers what users need, needs a network request interface, support get, POST, etc. So jQuery writes an interface named $. Ajax that is neither ActiveXObject nor XHRHttprequest. It provides a wrapper network interface.

Chameleon also has the same idea, all the interface design is really compatible with all ends, no difference, and only the current end of the interface call code: IE only retain ActiveXObject, Chrome only retain XHRHttprequest.

The interface design of Chameleon is better than that of jQuery in that it uses standard polymorphic protocols for maintainability, preserves only the current side of the code for performance, and exposes the polymorphic protocols so that users can extend their desired apis (like $.xxx).

Of course times have changed, listening views are no longer $(‘# XXX ‘).click(fn), but MVVM data-driven views, thus providing VM layers such as Chameleon bidirectional binding.

As we talked about Chameleon’s predecessor, MPV, let’s share the entire evolution of Chameleon.

Birth: Choose to translate or simulate a small program environment?

As mentioned above, in 2017, we completed a project named MPV. The goal of the first phase was to achieve a set of code to run Web and wechat applets without affecting users’ performance and relying on the principle of the framework.

Was lack of small application data is one of the biggest problems (let alone the cover today so many of the industry solution), was the only one you can refer to the open source project is WEPT and WEPT is a small program WeChat real-time development environment, its goal is to provide small program to develop efficient, stable and friendly, unlimited operating environment. It is designed to mimic the execution of applets on the Web side.

So we considered two implementation strategies when developing MPV:

1. Mock mini-program environments like Stack for the Web; Just like the micro channel developer tools also simulated the small program execution environment, WAServie, WAWebview to provide two sets of environment source code to do the bottom layer, in the page to open three independent operating environment to run and iframe communication simulation micro channel small program between the unicom relationship between the three Webview.

2. One by one code translation supports small programs, but the disadvantage is that there may be edge cases to deal with and many potential bugs.

Ultimately, after reviewing the source code and wechat developer tools, we clearly abandoned the first implementation strategy and chose to translate code one by one to support small programs. The main reason is that the Web side is too large to integrate all functions of wechat.

After three months of intensive development, we finally achieved the first version of MPV:



After implementing a few demos, the migration plan is executed:



The final implementation effect of MPV in Webapp is as follows:



The end result was pretty good, with over 90% code reuse and a significant improvement in overall development and testing efficiency.

However, in the follow-up practice, a large number of problems were found, and the bigger the project, the more prominent the problems were, summarized as follows:

  • Maintainability issues, no separation of common code and end difference code. There is not only business logic in the project, but also mixed with the product functional differentiation logic on the Web side and the applet side. For example, as mentioned before, the sharing function can not be realized on the Web side (guide sharing), but the small program can be realized, which means that all kinds of environment judge all kinds of differentiation logic, pull the whole body, but also back and forth test.

  • The direction of MPV is wrong. MPV uses applets syntax standards (such as the life cycle of applets and API interfaces), which makes it difficult for users to understand clearly.

  • There is no direct use of existing ecological components on each side, i.e. there is no standard specification to access existing open source components on one side. For example, the Web side pick.js component lacks a fast access specification, so users either have to redevelop it, or use environmental judgment in templates and JS code for introduction. As a result, the invocation methods and input and output of different ends of the same function are inconsistent.

  • Business projects rely on the MPV framework. The framework relies on wechat applet interface (template, lifecycle and interface) to extend the unified interface. For example, when the small program of wechat updated wx.request, the business project side could not use it immediately and needed to wait for the framework update.

  • The folder structure is chaotic, mixed with multiple end code files, and the identification cost is high.

  • Efficient data management modes such as VUEX and Redux are not supported

  • Size units are not uniform, px and RPX are not consistent

  • There are too many small differences around:

    • Agreement, such as Web client can use the / / : www.didiglobal.com/passenger/create, small programs can only use https://:www.didiglobal.com/passenger/create

    • Open a new page links, such as open the main page, Web side is / / : www.didiglobal.com/passenger/create, is small application/page/create

  • Inconsistent parameter transfer occurs between pages

  • Debugging costs are high, and both ends need to test after modifying the code

  • The interface effects at both ends are inconsistent, and the unity construction of basic built-in components is insufficient

  • The engineering construction is backward, such as not supporting liveroload, data mock, resource location, proxy, and multi-terminal unified preview

  • Incomplete interface design, lifecycle, component layering, native API design, etc

  • The template DSL syntax is not canonical

Growth stage: from pseudo unification to unification

With the accumulation of MPV practice, we have certain confidence and assurance, and the subsequent planning is more clear. In April 2018, we further expanded the scale of the cross-terminal project to create a true cross-n-terminal solution, aiming to provide a standard MVVM architecture development mode to unify various terminals. That’s where Chameleon came in.

What Chameleon really wanted was a set of code that ran multiple applications, and it was summed up to solve several major problems:

  • There is a lot of detail to be done in order to fully complete the unification of all the details of end development, especially interface unification

  • Differentiation customization space should be considered under the premise of completing the previous one

  • Continuous maintainability



The target ideal business form looks like this:

The top part of the figure is traditional development, while the bottom part of the Chameleon model abstracts the UI rendering layer and the local interface capability layer. Part of the business code is simple pages produced by XEditor (the predecessor of h5Editor) editing tool, and the other part is developed by Chameleon engineers. Not only solve cross-end problems, but also improve the efficiency, quality, performance and stability of engineering development process, allowing engineers to focus on meaningful business, growth faster.

First Native rendering engine choice — applets architecture, RN/Weex architecture

From MPV to Chameleon, the most obvious change to the outside world was the upgrade from 2-end (Web, applets) to multi-end (Web, applets, Android, iOS), and the initial confusion over whether the rendering engine on the first end would use applets or RN/Weex.

RN/Weex has a lot of data to look up, but not for small programs. After the painstaking search, according to the description of a friend who knows the inside story to share, just have a certain understanding.



Here are a few highlights:

  • The small program presentation layer uses Webview, which has a built-in JS framework for communicating with Native, and the real business code is executed in a separate JS VIRTUAL machine container instance

  • JS vm container usage. The iOS OS uses JavaScriptCore, and the Android OS uses QQ browser X5 kernel

  • The data drivers used by the various TAG Components of the applet are Web Components

  • Obviously, some of the more performance critical components are plugged into the Webview using native controls (video, keyboard, etc.).

    • How does Native get the location of a Native control? The answer is notified to the native layer by a set of small frameworks embedded in the Webview

    • How does a native control ensure normal scrolling in an internal scrollable element? The answer is CSS setting – Webkit-over-Scroll :touch, the implementation of iOS is the Native UIScrollView, Native can find the UIScrollView in the view hierarchy through some black technology, and then insert and process the Native control; You can’t do this with Android direct drawing. Right now (as of April) it simply overlays the Webview’s outermost layer, scrollView, with a set of JS frameworks built into the Webview to control the location of native controls

The final analysis is as follows:



Although small program plan looks very simple, but actually many details point needs a large number of grinding, from confirm solutions to real can be run online release, just take the terminal on the research and development of human as 20 p * 6 months, WeChat small program team goals and we across the end goal is different, they put so much cost is worth it, We don’t have to spend so much money to cross ends.

So we chose to abandon the applets rendering scheme and use RN/Weex, which is already open source.

The first version of the final use of Weex, including the team to see the Weex source code implementation.

In the overall design, only Weex rendering function is used, and the outer packaging interface ensures higher expansibility in the future.

Chameleon Native SDK

For Native SDK, we mainly do work from the three aspects of Native ability expansion, performance and stability.



  • Native Ability expansion: Whether Webview or React Native, Weex, or even Flutter only provides rendering capabilities (and some of the most basic Native interfaces), More local environment capabilities required to complete business functions (such as sharing to wechat) need to be extended by Android and iOS Native containers. Local capabilities are divided into two types, including components that involve UI interface (UI components such as login and payment) and APIS that involve pure capabilities (network, storage, etc.).

  • Performance: The interface display and interaction time depend on two key factors: resource loading time (the code not packaged into the installation package) and execution time

  • Stable: mainly focus on grayscale release (controllable risk) and online stop loss. The main work is to release according to the user’s grayscale and can quickly degrade to H5

The following is the optimized data of the first screen loading time in the performance direction. The original H5 using SSR (Server Side Render) has been regarded as the fastest Web first screen technical solution (not considering BIGPIPE, which takes time to optimize the back-end multi-module), and it remains below 1.5 seconds. Reduced to about 0.5 seconds after optimization.

   

In performance optimization we have a TODO plan for execution speed. The main reason why Flutter executes faster than Weex and RN is that Flutter is compiled and the machine code is already recognized by the CPU before the client machine runs. The latter is interpreted, to the client before the operation is a string, while compiling and executing, although the JIT optimization is done, the gap is still large. There is also an intermediate code that smoothes out the differences in machine code between CPU architectures; Of course, the premise is that the development language is changed to static typing, which is not expanded here.

The development of Web terminal, Alipay mini program, fast application, wechat mini program and Native terminal has been changed from five times to about 1.2 times. The most important thing is that with the accumulation of differentiated polymorphic components and cross-terminal components at each end of the business level, the workload of the subsequent 1.2 will also become 0.8, and the optimization of 0.4 mainly comes from two aspects:

  • 0.2 is the accumulation of common cross-end components with higher reuse

  • 0.2 is differentiated polymorphic components of various business levels. For example, the implementation and interaction of the login function are inconsistent between the Web end, Native end and small program end. In this case, the business form is different, and the < Passport > component designed is also different, which can only be encapsulated by different business lines.

What’s next on the roadmap.

Our ultimate goal is to provide standard MVVM architecture development patterns that unify all terminals.



The next roadmap is as follows:



Welcome students with the same vision to join us and contribute their own code to the warehouse.

The address of the project: https://github.com/didi/chameleon

QQ group:



The public no. :



Introduction to interview Guests

Zhang Nan, founder of Chameleon, technical team leader, former Senior engineer of Baidu, lifelong learner.