Mp.weixin.qq.com/s?__biz=MzU…

The history of mini program development — In the early morning of January 9, 2017, the much-anticipated micro program of wechat was officially launched.

Before this, jingdong into a front small team, after a month of closed development, a version of the speed of iteration in one week, finally released in the first time her “jingdong shopping” small program, although the function and interface now seem humble, but it was completely accords with WeChat small program “within reach, finished with the” concept.

Of course, with the continuous iteration of the whole project, the design, interaction and functional complexity of the “JINGdong Shopping” small program has been fully comparable to the APP side. The engineering practice of this has been shared by Professor Liu Huimin at GMTC Global Big Front-end Technology Conference (Beijing) 2019[1]. Those who are interested can download PPT: Jingdong Shopping mini program Engineering Road [2].

At that time, there were some shortcomings in the development of wechat small programs, such as chaotic dependency management, backward engineering process, imperfect ES Next support, inconsistent naming norms, etc. All of these problems are now known to have various official and unofficial solutions, but at the exploratory stage of applets development, they were pain points.

One of my personal favorites is that a language becomes a “compilation target” language when its capabilities are inadequate and the user’s operating environment does not support other options.

Throughout the history of the front-end, whether it is the popularity of CSS preprocessors, the popularity of various templates, or the birth of CoffeeScript and TypeScript[3], all confirm this statement, and wechat applet is no exception. Therefore, a variety of small program development frameworks such as a hundred flowers bloom, emerge in endlessly.

The main difference between these small program development frameworks is DSL, which can be seen from the logo color. In addition to Chameleon[4] is a custom DSL, the other green logos follow the Vue syntax (such as MPVUE). The blue logo follows the React syntax (e.g. Taro).

After WeChat small procedures, the major manufacturers have released their own small program platform, such as: alipay, baidu, headlines, QQ, etc., coupled with fast application, netease, 360, jingdong, small application circuit is more and more crowded, developers need to fit more and more small application platform, as a result, application development framework have also carried out various size adaptation.

So, if you look back at this point in time, you will find that throughout 2018 and even early 2019, the main differences and focus of applets development frameworks are: DSL and multi-endpoint adaptation.

Taro[5] was born due to the increasing business needs. At that time, our team needed to be responsible for: JINGdong Shopping, TOPLIFE, etc. At the same time, the above businesses have more or less multiple requirements, such as wechat mini program, H5 and React Native(mainstream APPS of JD are basically built with React Native rendering engine), and it can be predicted that, In the future, it is likely to need to adapt to more small program platforms, and it is not practical to develop a set of code for each end, which will lead to: increased research and development costs, difficult code maintenance.

At that time, our team developed a React framework: Nervjs[6], the entire team’s technical stack was switched to React, and there was no small program framework that followed the React syntax in the market at that time. We developed Taro, and hoped to write small programs using React syntax. Write Once Run Anywhere to achieve cross-end.

The Taro framework has been open since June 7, 2018, and has been iterating consistently in three areas:

Support for React Hooks, CSS Modules, Mobx, etc. Such as the Taro Forum [7], the Taro Material Market [8] and other platforms, as well as the community construction plan released later [9]. After more than one year’s efforts, Taro has been widely recognized by the community. As of December 18, 2019, Taro has 22,254 Stars and 250 ficolin-3 which provide 150+ development cases voluntarily submitted by communities: taro-user-cases[10], including many cases.

But despite all this, Taro still has some problems that can’t be solved, or rather, aren’t easy to solve. For example, React DSL is strongly bound, JSX adaptation is a lot of work, and community contribution is complex. In the final analysis, a large part of these problems are related to Taro’s architecture.

Therefore, our team has been waiting for the right opportunity to upgrade the entire architecture and repair some of the technical debt caused by the rapid iteration of the project.

Most of all, a simple project maintenance iteration was no longer enough to satisfy our team’s restless heart, and we were eager to take this opportunity to make a technological breakthrough.

Exploring cross-framework development of Applets – Before we talk about the Taro architecture, let’s review the architecture of applets.

Wechat applets are mainly divided into logical layer and view layer, and the native part below them. The logic layer is mainly responsible for JS running, and the view layer is mainly responsible for page rendering. They mainly communicate with each other through Events and Data, and call the native API through JSBridge. This is also the architecture of most small programs led by wechat small program.

Since the native part of the image is like a black box to the front-end developer, the native part of the entire architecture diagram can be omitted. At the same time, we also simplify the logic layer and the view layer, and finally get a minimalist version of the applet architecture diagram:

In other words, you just need to call the corresponding App()/Page() methods in the logic layer, and process data in the methods, provide life cycle/event functions, etc., and provide the corresponding templates and styles in the view layer for rendering. This is what most applets development frameworks focus on and deal with.

The Taro architecture is currently divided into compile time and runtime.

Among them, Taro code is mainly converted into small program code through Babel[11], such as JS, WXML, WXSS, JSON.

Runtime is mainly for some parts: life cycle, events, data processing and docking.

Taro is compiled using babel-Parser [12] to parse the Taro code into an abstract syntax tree. Then, babel-types[13] was used to modify and transform abstract syntax tree. Finally, babel-generate[14] was used to generate corresponding object codes.

For details, please refer to babel-Handbook [15]

The most complex part of image compilation is JSX compilation.

We all know that JSX is a JavaScript syntactic extension that can be written in a variety of ways. Here we have adapted the possible JSX writing methods one by one in an exhaustive way. This part is a lot of work. In fact, Taro has made a large number of commitments to better support the various JSX writing methods.

However, it is not possible to cover all cases, so we recommend that you write React code according to the official specification. We also provide a rich ESlint plugin to help you write the specification.

There’s always a meme in our team that says: If you use Taro to develop bugs, your React code is well-written.

The core Render method of React is missing from the compiled code. Also added to the code are BaseComponent and createComponent, which are the core of the Taro runtime.

Import Taro, {Component} from ‘@tarojs/ Taro ‘import {View, Text } from ‘@tarojs/components’ import ‘./index.scss’

export default class Index extends Component {

NavigationBarTitleText: config = {navigationBarTitleText: ‘home’}

componentDidMount () { }

Render () {return (<View className= ‘index’ onClick={this.onclick}> Hello world!) }}

Import {BaseComponent, createComponent} from ‘@tarojs/ taro-appellate ‘

class Index extends BaseComponent {

// …

_createDate(){ //process state and props } }

export default createComponent(Index)

React BaseComponent (setState, forceUpdate, etc.); render (BaseComponent, forceUpdate, etc.); Taro’s current architecture only follows the React syntax when it is developed, and has nothing to do with React when it is actually run after the code is compiled.

CreateComponent’s main purpose is to call Component() to build the page; Docking events, life cycles, etc. Diff Data and call the setData method to update the Data.

To sum up, therefore, the characteristics of the entire Taro current structure are:

Recompile, run light: this can be seen by comparing the lines of code on both sides. Compiled code has nothing to do with React: Taro simply follows the React syntax while developing. Compile directly with Babel: This also leads to Taro’s current weakness in engineering and plugins. Architecture for other solutions applet development frameworks have blossomed and we have been inspired by the community.

Let’s take a look at how mpVue [16], a representative applets development framework that follows vUE syntax, is implemented.

Mpvue is essentially a fork of vuejs/[email protected] code, preserving Vue Runtime capabilities while adding support for the applets platform.

Specific performance in the source is: under the platforms of the Vue source folder increased mp directory, inside has realized the complier (compile time) and the runtime (runtime) support.

Mpvue is also implemented at compile time and run time.

Mpvue compile time MpVUE compile time does something similar to Taro: compile Vue SFC code into small program code files (JS, WXML, WXSS, JSON).

The biggest difference is that Taro compiles JSX to a small program template, while MPVue compiles Vue to a small program template. However, due to the similarity between the Vue template and the applets template, MPVue has much less work to do in this area than Taro.

The mpVUE runtime is strongly related to the Vue runtime. First, let’s look at the Vue runtime.

A.vue single file consists of three parts: template, script, and style.

In the orange path part, template will be analyzed through ast in vue- Loader during compilation and finally generate a render function. Executing the render function will generate a virtual DOM tree, which is an abstraction of the real DOM tree. The nodes in the tree are called vnodes.

After Vue gets the virtual DOM tree, it can make patch diff comparison with the old virtual DOM tree. After the patch phase, vue uses real DOM manipulation methods (insertBefore, appendChild, etc.) to manipulate DOM nodes and update views.

At the same time, the part of the green path, when instantiating Vue, will do responsive processing to the data data, when monitoring the change of data, will call the render function to generate the latest virtual DOM tree, Then patch was compared with the old virtual DOM tree to find the vNode with the lowest modification cost for modification.

When mpvue runs, it will first empty the relevant methods of DOM operation in the patch phase, that is, do nothing. Second, while creating the Vue instance, Page() is surreptitiously called to generate the Page instance of the applet. The patch phase of the runtime then calls the $updateDataToMp() method directly, which takes the data that is maintained hanging on the Page instance and updates it to the view layer via the setData method.

The overall mpVue schematic is as follows:

Therefore, unlike Taro, mpvue is part compile-time, part run-time. This can also be roughly reflected in the comparison of the amount of code.

Mpvue’s WXML template, like Taro’s, is compiled from code; Unlike Taro runtime and React runtime, mpVue essentially runs Vue in applets and implements most of the features of [email protected] (only a few features are not implemented due to the limitations of applets templates, such as filter, slot, v-html). And the whole framework based on Webpack to achieve a more perfect engineering.

The difference in the realization principle and effect of other small program frameworks also brings us some thinking:

Compile time OR runtime: The main reason Taro chose recompile time was to consider performance. After all, under the same conditions, the more work done at compile time, the less work done at runtime, the better performance. In addition, recompiling also ensures that Taro’s code is readable after compilation. But in the long run, as computer hardware becomes more and more redundant in performance, we think it’s worth sacrificing a little more tolerable performance in exchange for greater flexibility and better adaptation of the overall framework. Statically compiled OR dynamically built templates: Although Taro’s and MPvue’s templates are statically compiled, there are many examples of dynamic builds in the community, such as Remax[17]. DSL constraints: Can we implement a small application development framework that is free of DSL constraints? Taro Next – This time, we think about the nature of the front end from the perspective of the browser: no matter what framework is used, React or Vue, the final code will be executed using the BOM/DOM API of the browser, such as: CreateElement, appendChild, removeChild, etc.

Therefore, we created the taro-Runtime [18] package and implemented an efficient, compact VERSION of the DOM/BOM API in this package (the UML diagrams below only reflect the structure and relationships of a few major classes) :

Then, we inject into the logic layer of the applet through the ProvidePlugin[19] of Webpack.

In this way, you have an efficient, streamlined VERSION of the DOM/BOM API when running your applet.

After the React implementation is injected into the DOM/BOM, Nerv/Preact can theoretically run directly. However, React is a bit special because the React DOM contains a lot of code for browser-compatible classes, which makes the package too large. We don’t need this code, so we need to do some customization and optimization.

React 16+ uses the following architecture:

The react-Core, the core of React, is at the top of the picture, with the React-Reconciler in the middle, which maintains the VirtualDOM tree, and internally implements the Diff/Fiber algorithm that determines when and what to update.

The Renderer is responsible for platform-specific rendering, providing hosting components, handling events, and so on. For example, react-DOM is a renderer that renders DOM nodes and handles DOM events.

As a result, we implemented the taro-React [20] package, which connects the React-Reconciler to the Taro-Runtime BOM/DOM API:

The specific realization of the picture is mainly divided into two steps:

Implement the hostConfig configuration for the React-Reconciler by calling the corresponding Taro BOM/DOM API in the Methods of hostConfig. Implement the Render function (similar to reactdom.render) method, which can be thought of as a container to create the Taro DOM Tree. The React code actually runs properly when the applet runs and generates the Taro DOM Tree. How do you update the large Taro DOM Tree to the page?

First of all, we template all the components of the applet one by one, so as to get the corresponding template of the applet component. The following picture is what the view component of the applet looks like after template processing:

We then dynamically “recursively” render the entire tree based on the component’s template.

The specific process is to first traverse the child elements of the Taro DOM Tree root node, and then select the corresponding template to render the child elements according to the type of each child element. Then we will traverse the child elements of the current element in each template, so as to recursively traverse the entire node Tree.

Taro Next’s React implementation flowchart is as follows:

React and Vue are different from each other in development, but after implementing the BOM/DOM API, the differences between them are very small.

The biggest difference between Vue and React is the CreateVuePage method at runtime, which does some runtime processing, such as life cycle alignment.

Other parts of the image, such as building the BOM/DOM, modifying the DOM Tree, and rendering principles, are the same as React.

When referring to Flutter, we have to mention The Flutter Web[21], which is the core drawing layer that implements Flutter on top of the standard browser API and essentially calls the BOM/DOM API at the end. So, in theory, it can be adapted, but we don’t put too much effort into it, and it will be implemented and maintained by the community like a quick app.

Taro Next will talk to you about implementing Taro Next in more details, such as events, updates, and life cycles.

Taro Next event is the first event to be implemented in the following ways:

In the template process of the small program component, all the event methods are specified to call ev functions, such as bindtap, bindchange, bindSubmit and so on. Implement the eventHandler function at runtime, bind it to the eh method, Collect all of the applet events by using the document.getelementById () method to get the TaroNode corresponding to the triggering event and by createEvent() to create a conforming TaroEvent call As you can see, the Taro Next event essentially implements its own event mechanism based on the Taro DOM. One of the benefits of this is that the widget supports bubbling and capturing events regardless of whether they are supported or not.

Update both React and Vue will eventually call Taro DOM methods such as appendChild and insertChild.

[0].cn.[4].value: {root.cn.[0].cn. “1”} and updated to the view layer with the setData method.

As can be seen from the picture, the granularity of the update here is DOM level, and only the DOM that is changed will be updated. Compared with the previous data level update, it will be more accurate and have better performance.

The life cycle is probably one of the least volatile parts of the system compared to the other parts of the system. As before, the lifecycle implementation is a one-to-one correspondence of the lifecycle methods between App instances/Page instances maintained at runtime.

const config: PageInstance = { onLoad (this: MpInstance, options) { //… }, onUnload () { //… }, onShow () { safeExecute(‘onShow’) }, onHide () { safeExecute(‘onHide’) }, onPullDownRefresh () { safeExecute(‘onPullDownRefresh’) } //… }

Unlike previous architectures, Taro Next is nearly fully operational.

The new architecture basically solves the previous legacy problems:

No DSL restrictions: Whether your team is a React or Vue technology stack, you can build dynamically using the Taro development template: Unlike previous templates generated by compilation, Taro Next’s templates are fixed and then dynamically “recursively” render the entire Taro DOM tree based on the component’s template. New features are seamlessly supported: Since Taro Next essentially runs React/Vue on small applications, new features are seamlessly supported. Community contributions are simpler: the error stack will be the same as React/Vue, and the team only needs to maintain the core taro-Runtime. Based on Webpack: Taro Next implements multi-terminal engineering based on Webpack and provides plug-in functions. Performance tuning As mentioned earlier, all things being equal, more work done at compile time means less work done at run time and better performance. Taro Next put a lot of effort into performance optimization after its new architecture became nearly fully operational.

And before that. Take a look at Taro Next’s process and compare it to the native applet process.

As can be seen from the image, Taro Next has more performance risks caused by the red part compared to the native applet, such as the increase in package Size caused by the introduction of React/Vue, runtime wear, Taro DOM Tree construction and update, DOM data initialization and update.

We can only do the green part, namely: Taro DOM Tree build and update, DOM data initialization and update.

The following table is an example of TodoMVC’s package Size comparison in the case of native, Taro Old, Taro Next, etc. It can be seen that after introducing React/Vue, the package Size increases by about 30K in the case of Gzip.

Unlike previous templates that are compiled, Taro Next’s templates are fixed and then dynamically “recursively” render the entire Taro DOM tree based on the component’s template. That said, Taro Next’s WXML size is limited.

As the project grows and pages grow, the native project WXML will grow in size, while Taro Next will not. That said, Taro Next’s packages may get smaller when the number of pages passes a tipping point. Therefore, the package Size problem is not a concern.

During the Taro DOM Tree construction and update phase, we implemented a set of DOM/BOM apis that were only efficient and streamlined, and only necessary.

Github has a repository jsDOM [22], which basically implements a set of Web standard DOM/BOM on Node.js. The repository’s code is about 2.1 MB before compression. Taro Next’s core DOM/BOM API has less than 1,000 lines of code.

Therefore, we maximized performance during the Taro DOM Tree build and update phases.

Taro Next’s updates are DOM level updates, which are more efficient than Data level updates because Data granularity updates are actually redundant. Not all Data changes result in DOM updates.

Secondly, Taro compresses the Path of the Taro DOM Tree during the update, which also greatly improves performance.

The end result of the image is that in some business scenarios write, add, select data, Taro Next performs better than native.

Of course, experimental data will always be flawed, and the final performance will depend on the verification of various complex business scenarios. If you are interested in Taro Next’s performance, you can run the Taro-Benchmark [23] package to compare the results.

We have also been continuously improving The performance of Taro Next in all aspects. Please pay attention to Taro Next’s latest Commit.

Taro Next will soon be released in version 3.0, supporting cross-application development with React/Vue, which will be expanded to other applications and their ecosystem in subsequent iterations.

The Taro team will focus on supporting React/Vue. Flutter and Angular will be left to the community to adapt and maintain, just like Kuaiapp. Qiyu8[24] and Issacpeng[25] from Huawei are helping us adapt.

At the same time, we have also built the Taro Mobile One-stop R&D platform, which integrates the previously accumulated multi-stage development workflow and engineering solutions, and has built-in data monitoring, component market and visual construction. Currently, it is in the internal testing stage.

Business incubation technology and technical service business: this is the most important and deepest point of the Taro project from inception to iteration. Top-down OR bottom-up: From a developer’s perspective, React/Vue code is written differently; From a bottom-up perspective, however, the differences are not that great, as they all call the BOM/DOM API. If we go a little further down the rendering layer, won’t the difference between platforms be that great? For example, Flutter. Learn Once Write AnyWhere & Write Once Run AnyWhere: Many developers prefer Learn Once Write AnyWhere proposed by React. Our Taro slogan is “Write Once Run AnyWhere”, which often leads to our frequent criticism. Here are some of my own thoughts: Learn Once Write AnyWhere is intrinsically more developer-friendly. Developers only need to Learn the React stack to develop Web/ mobile applications, for example, but it is less project-friendly, with each project having to maintain a copy of the code. While Write Once Run AnyWhere is not so friendly to developers (the more adaptors, the higher the cost of adaptation will inevitably increase, and the higher the requirements for developers will be), but according to our practice, it will be more friendly to the project, “one set of code, multiple adaptors”. Of course, the granularity of adaptation here is not necessarily project-level, in fact, in our specific practice, there are quite a few: business level or even page level. As the saying goes, “A single thread does not make a thread, and a single tree does not make a forest”, Taro is no longer a project of a single team, but a project of the entire Taro development community.

Finally, I would like to take this opportunity to thank all the community members who have helped Taro’s growth, especially the contributors to Taro. Thank you very much!

We also thank Garfield550[27], Liang Yin [28] and ShaoQian Liu[29] who were invited to be the core maintainers of TaroUI[26]. They will support the subsequent iterations and maintenance of TaroUI.

Of course, there are Zacksleo [30], Jay Fong[31], Loveonelong [32], Lolipop99 [33], Bozai Cake [34], Original sin [35], Lentoo [36], Childe of White-collar Xia [37] , YuanQuan[38], Tourze [39], lingxiaoZhu[40], etc.

In addition, I would like to thank our r&d teams, including Tencent Cloud, Digital Guangdong, Tencent CDC, Netease Yanxuan, Huawei Open Source team, Zhaopin Consumer Finance, and many others, who have quietly provided valuable suggestions for the development of Taro.

Long wind and waves will sometimes, straight sail to the sea.

Welcome to bump Lab blog: AOtu.io [41]

Or pay attention to the bump Laboratory public account (AOTULabs), push the article from time to time: