preface

In recent years, the whole front-end field develops rapidly, and efficient front-end frameworks emerge endlessly. Each team chooses different technical solutions. Because of the characteristics of the Internet and China’s own characteristics, each product has the same demand for multi-terminal delivery. Cross-end scenarios like small programs are also different from existing development methods. In order to meet the needs of business, technicians will maintain multiple sets of codes in daily development due to different platforms, which is low in efficiency and high in cost. In my opinion, the big front end is the trend, the cross end is the first step of the trend, and it is very important for technicians to be able to solve the pain point of maintaining multiple sets of code without affecting the functional experience. Lean once, write once, run anywhere.

background

Industry status quo – a hundred schools of thought contend



After nearly two years of development, small program has gone deep into the daily life of users, the number of small program applications has exceeded one million, covering more than 200 subdivided industries, and the daily active users have reached 200 million. At the same time, like Alipay, Baidu, toutiao, hand Q and so on have started their own small program ecology, a hundred schools of thought contend with the occasion and born. Qingju Bike, as a convenient travel tool, is also more user-friendly for users in terms of the use of lower cost and more friendly experience. Ready-to-go small programs have become the trend of platform choice.

Business requirements – efficient and stable multiple entrances

High efficiency, stability and multiple entries are now the requirements of the business. Qingju Bike is a small program with relatively high daily life (currently ranked in the TOP 10 of Aladdin small programs), which requires us to have high standards for the performance (loading, rendering, response time), stability and safety of small programs.

At the same time, the business also needs to get more entrance on each platform, which directly leads to our choice of framework, business development requirements more rigorous.

Framework expectations

From the perspective of users, in order to reduce the cost of users (download, install or update APP), we choose a platform in the market that is more suitable for the characteristics of bikes as the entrance. At this time, there will be a lot of problems for research and development. When we choose the framework, we will have high expectations or requirements for the following points:

  1. Each end of the development effect to be highly consistent, can not be developed after a large amount of time compatible multi end
  2. There are different implementation methods for different ends, which can be easily expanded to meet the differentiated needs of different ends of the product
  3. Engineering is better because front-end process development is less efficient, need to use software engineering technology and methods for development and management
  4. Framework design, access without changing too much code, with a good degree of abstraction
  5. Performance requirements are high, package size, loading, rendering, and response are all strictly considered

practice

Cross-end technical scheme design

Cross-platform framework — Chameleon



Chameleon is more than just a cross-end solution that allows developers to develop multi-end native applications efficiently and at a low cost. Based on the excellent front-end packaging tool Webpack, it absorbs the most useful engineering design accumulated over the years in the industry and provides the front-end basic development scaffold command tool to help end developers efficiently complete business development from the whole process of development, joint adjustment, testing, on-line and so on. From the architecture design drawing of Chameleon framework:

  1. Under each platform, Chameleon added a relatively complete DSL, including routing, custom lifecycle, components, data event binding and management, style rendering, etc., based on its own running pool.
  2. Chameleon-tools does compilation (based on Webpack), provides syntax checking, translation, dependency assembly, etc.
  3. Chameleon-ui and Chameleon-API are used to smooth out the inconsistencies and API differences of various platforms.
  4. Provide polymorphic protocol to solve component and API differentiation;

In general, Chameleon’s design mode is more suitable for us to do cross-end project development, which improves our efficiency and avoids maintaining multiple sets of duplicate codes…

Technical structure of Qingju Bicycle business

Qingju Bike’s simple business flow chart:



The business of Qingju Bike is relatively complex, including login, authentication, electronic fence, propaganda and education, status change and more than 30 pages (not including various H5 implementations). Users actively open/scan the code in wechat and enter the mini program. After completing login, they scan the code and unlock. Ride after complete payment end of the entire process (only core processes mentioned here), because of business needs, we need to maintain WeChat, alipay, Scott (fast application, baidu access), and many other small procedures, for developing the fastest is copy a set of code and then corresponding modifications, when to develop new features is pain.

Based on the consideration of the differences between business and multi-terminal, the final technical scheme design drawing of CML Qingji bicycle applet is as follows



From the current module of Qingju Bike, in order to truly realize cross-end development, differences between platforms need to be solved:

  1. The SDK provided by different platforms for small programs is different, for example, the getAuthCode of wechat and Alipay. The API method name is different, and the return is also different. How to solve similar problems?
  2. How to solve the problems caused by different channels and access schemes?
  3. Page inside – component call – parent-child component communication, store data management how to handle?
  4. How to solve the underlying hardware implementation scheme? For example, the difference between the BLE of wechat and Alipay; Baidu small program IOS does not support BLE, how to solve similar problems?
  5. For example, the stability of some webView ends is considered to limit the uplink of H5-data. How to solve the problem?
  6. Other issues, utils component differentiation…

Multi-terminal interface consistency

Since the componentization of each end is implemented in different ways (wechat WebComponents, Baidu simulation componentization, Alipay react framework), the consistency of multi-end interface is a troublesome matter. In terms of the design and actual experience of CML itself, both the unit conversion of style and the unified encapsulation of components have been well unified.

Click on the GIF image to preview the effect of the three ends of cyorange Bike applet based on CML:

Differentiation (customization)

We split several common modules according to business scenarios, including user correlation, order issuing, hardware/Bluetooth communication, order management, marketing, etc. Each module has its own store, action and exposed commonApi to realize the whole product function with the logic of each page. For differentiation, we enumerate an example of login that is compatible with wechat and Alipay login interfaces through polymorphism. We in the project SRC/Componets to establish an API space for polymorphic management API, for login we in accordance with the SPECIFICATION of CML to establish a login.interface file

The implementation is as follows:

<script cml-type="interface"> // Define the input parametertype RESObject = {
	code: string,
	errMsg: string
  }
  type done = (res: RESObject) => Void

  interface MethodsInterface {
    login(timeout: String, opt: done): Void
  }
</script>
<script cml-type="wx">
  class Login implements LoginInterface {
    login (timeout, opt) {
      wx.login(timeout, code => done({code: ' '12', errMsg: 'successful'})); } } export default new Login();  '12', errMsg: 'success'})); }}export default new Login();
</script>Copy the code

Once the interface is defined, it can be called in a uniform way. Here’s a question that’s easy to forget: The input and output must be defined in the interface, and the implementation specifications of each end should be standardized. This is to avoid the input (increase or decrease) in the method modification of one end without considering the implementation of the other end. If the full test will waste a lot of manpower and cannot guarantee the coverage of the test, for the sake of maintainability, It is advisable to stick with interfaces for polymorphic components from the beginning, sometimes it is too late to fix them at the program level…

import login from '.. /.. /components/Api/login/login.interface';
 
export const login = function ({commit}) {
  return new Promise((resolve, reject) => {
    login.login()
      .then(({code}) => {
        commit(types.SET_USER_CODE, {code});
        resolve({code});
      })
      .catch(e => {
        reject();
      });
  });
};Copy the code

On the other hand, if PM puts forward some requirements at one end, such as adding some special functions for Alipay users when they log in, we can carry out transformation without affecting other processes. At the same time, it is physically isolated and can release a separate NPM package with high maintainability. For example, for the API of Bluetooth communication, wechat requires the terminal to convert ArrayBuffer to HexString, while Alipay does not. Here, we also use the polymorphic interface to pull out, wechat performs the conversion, and Alipay directly returns the original data. The whole BLE process is very complicated. We have made a lot of optimization for the success rate of communication. Hack directly in the code will affect the whole process, even cause the instability of the whole Bluetooth action and affect the unlock rate. This layer of polymorphic encapsulation does not affect the original logic, and the cost is relatively low.

engineering

Engineering is the use of software engineering technology and methods to manage the development, on-line and maintenance of projects. Because front-end process development is relatively inefficient, the efficiency of research and development can be improved through modularization, componentalization, local development and on-line deployment automation.

Common Commands

  1. CML dev ‘compiles all’, CML WX dev ‘compiles wechat’, starts development mode, listens for file changes and dynamic packaging
  2. CML build ‘compiles all’, CML WX dev ‘compiles wechat’ to build the production environment
The mock data

In the process of front-end development, data mock is an important function, which plays a very important role in logic verification, r&d efficiency and online and offline environment switching. CML also provides data mock capabilities here.

import cml from "chameleon-api";
cml.get({
  url: '/api/getUserInfo'}) .then(res => { // ... CML. SetStorage ('user'. res) }, err => { cml.showToast({ message: JSON.stringify(err), duration: 2000 }) });Copy the code

Only the API path needs to be written in the parameter URL of the calling method. How the local dev development mode mocks this API request and how the build line mode requests the online address requires the apiPrefix to be configured in the configuration file.

CML supports local mocks by creating js files of mock data in the /mock/ API/folder. Local mocks can be implemented directly when dev mode is enabled (with apiPrefix left blank for local IP + port).

CML configuration

1. The construction process of Chameleon is configured. A chameleon.config.js file is provided in the root directory of the project, in which the global object CML API can be used to operate the configuration object. The extensibility of configuration at the compile level is also exposed

2. Page routing configuration. Chameleon project has a built-in unified routing management mode for all ends. In order to distinguish small programs, URL represents H5, path represents small programs, and mock service interface is provided. Insert the Pages field in app.json to implement routing on the applet side.

The framework design

Component invocation – Parent-child component communication

For message flow cards on the home page, we will have different forms of login, authentication, order, ride cards and so on. We encapsulate a component to process, and the static effect is as follows:



This component is relatively generic, and we can roughly divide it into two types: notification type and action type. As shown in Figure 2, the message is purely displayed, and the other messages are messages with buttons. In the design of components, we distinguish them according to different types passed when invoking components.

Notification type: TipsCard

Action type: ActionCard

Here is how the action type is implemented:

 <template>
  <view class="action-card">
    <view class="{{'content '+(remindActive && 'bigSmall')}}">
      // ...
    </view>
  </view>
</template>

<script>
  class ActionCard {
    mounted () {
      EventBus.on(REMIND_CARD, ({index}) => {
        if(index === this.index) this.remind(); }); } methods = { callback (e) { EventBus.emit(ACTION_CARD, {index: this.index}); }}}export default new ActionCard()
</script>Copy the code

For custom event handling, we bind a componentAction with c-bind: Action, pass the EventBus event to execute the call, and dynamically pass the Type in Component is to specify different message flow types

<template>
  <view>
     <component is="{{item.type+'-card'}}"/ > / /... </view> </template> <script> class Home {beforeMount() { this.tipsList = [] EventBus.on(ACTION_CARD, (data) => { this.componentAction(data) }); } methods = { componentAction(data) { ... // Execute instance}}}export default new Home();
</script>
Copy the code

The final result is shown below

 

Data management – Store

CML framework as the framework of data-driven view, the application is data-driven, data logic becomes very complex, it is necessary to use a good and efficient data management framework, we may need mapState, mapAction in each page to do the component state acquisition, event distribution and so on

import createStore from 'chameleon-store';
import user from './user';
import location from './location'; / /..................... Other state managers are omitted here const store = createStore({modules: {user, location, bicycle, // ………… Other state managers are omitted here}});export default store;Copy the code

This is where the entry actions for the user state are listed

import mutations from './mutations';
import * as actions from './actions';
import * as getters from './getters'; const user = { state: { }, mutations: { ... mutations }, actions: { ... actions }, getters: { ... getters } };export default user;Copy the code

CML provides the chameleon-Store module package, and the usage and writing method is the same as vuex

CML provides computing attributes — computed, listening attributes — watch, data management like VUex, and DSL syntax that are small in terms of learning costs and conform to current development practices.

<script>
  class Home {
    data = {
      remindActive: false
    }
    watch = {
      lockState: function(cur, old) {
	 returnthis.remindActive } } computed = { ... store.mapState(['location'.'user'])}}export default new Home();
</script>Copy the code

Based on qingju’s relatively high daily activity, high efficiency, stability and multi-entry are now the requirements of the business, which requires us to have high standards for the performance (loading, rendering, response time), stability and safety of small programs. performance

Performance improvement

In terms of performance, CML does array diff, because the main running performance bottleneck of small programs is the transmission performance of WebView and JS virtual machine, CML tries to diFF out the modified part of the transmission, the performance will be relatively better, paste the source code



Source code implementation is relatively simple, but the actual use is relatively large, bike small program has a lot of list pages, when the list data for re-assignment into the diff function to filter out the actual change of each item, and then the view-render, performance will improve a lot




Packet size

In terms of package size, CML doesn’t change much from the previous version. The 1.2MB compiled package size is basically unchanged, thanks to the compression of the code and the fact that the Runtime code itself is not large.



conclusion

“No sweet without sweat” is the overall feeling of Orange bike applet connecting to Chameleon cross-end frame.

Suffering: The “suffering” here is actually because we need to do more work to pave the way for the future. Originally, we only need to consider one side of the CASE, but now we need to abstract out a series of interface parameters, common components, and need to master more platform technology solutions.

Gan: When we launched the Orange Bike applet through Chameleon, we no longer had to maintain multiple sets of code in response to new business requirements, and we completely eliminated the risk that maintaining multiple sets of code might lead to inconsistency on one end. Not only RD students benefit, QA students can save half of the acceptance time because it is a set of code logic. We think this is an example of technology-driven business development. Technology is not just a tool, but should be realized from the business perspective. Only then can it have real value.

CML does DSL compilation and transformation to fundamentally realize the unification of all ends. Introducing component polymorphism, method polymorphism and abstract configuration of each end can more flexibly erase differences and facilitate configuration and expansion. Through in-depth understanding of The Chameleon framework and project practice, we can abstract some common layer components and interfaces based on CML, of course, refers to cross-platform components and interfaces, because only in this way can the development efficiency be enhanced and more possibilities be brought to the business.



Are we going to consider in choosing a framework to consider not only its own performance, stability, security, packet size, etc., also need to have a look at the reusability and development, such as whether can expand out based on the framework itself is suitable for the general business even company level components, API, whether convenient as the change of the business and can do reactive extensions, changes in a timely manner.

Here is the end of the green Orange bicycle Chameleon cross-end practice introduction, if there is a bad expression of hope to include more and put forward suggestions we timely improvement, thank you.