As the Internet continues to evolve, front-end programmers are constantly facing new challenges, and every year there are new and wonderful things happening in this changing and constantly innovating field. From the birth of wechat small program last year, to this year’s gradually hot, and the emergence of a new force of light applications, Baidu small program, the front end can extend the field has become more and more wide, of course, also means that the business is expanding. At this time, how to improve the development efficiency through technical means to cope with the growing business, is a topic worth exploring. This article will introduce the story of Taro’s birth in simple terms and record the story of the busy turn of spring and summer.

Let a person love and hate the micro channel small program

Since January 9, 2017, wechat small program (hereinafter referred to as small program) was born, accompanied by praise and controversy. From the release of the online is not viewed by most people, to today’s gradually hot, and even said that it is not too much, small program with time and practice to prove their value. At the same time, developers, small program ecology is constantly improving, many pits have been trampled flat, although there are still some problems to criticize, but it has seen the sincerity of wechat. At this time, if you have not played with the small program, it is very OUT.

Small programs for front-end programmers should be a blessing, with front-end related technology, get silky smooth Native experience, front-end can be tough in front of the product sister. Can say small program for front-end programmers opens a new door, everyone should thank WeChat, but from the development point of view, small application development experience is very questionable, not only appears some neither fish nor fowl, on grammar and some puzzling pit also often inadvertently letting a person plaint about the harmonious society, From the market out of the endless small program development framework can be seen. Here are some of the pain points of small program development.

Code organization and syntax

In a small program, a page page might have four files: page. Js, page. WXSS, page

So in the development of the time to need to go back and forth to switch files, especially in the development of template and logic, cut to cut will appear particularly troublesome, affect the development efficiency, but small program native support only so written, it is more embarrassing.

In terms of grammar, the grammar of small programs can be said to be like React and Vue, which can’t be said to be a little bit neither, but it always feels a little awkward in use. For developers, it is equal to learning a new set of grammar, which increases the learning cost. Also, small program templates can be tricky to write because there are no editor plugins, no smart hints and no Lint checks.

Naming conventions

Inconsistency of specifications can be seen everywhere in small programs

For example, the properties of components, take the simplest component as an example, in the small program official document, the properties of the component part of the screenshot, you can feel

The component property name can be session-form or bindgetPhonenumber. Of course this is not the worst, you can say that the specification for event binding is bind + event name, and the specification for other properties is delimit the word with a hyphen. I thought this was the standard for small programs until I saw the component

This is not the deal. Hey!

The same situation also occurs in the life cycle methods of pages and components. The life cycle methods of pages include onLoad, onReady, onUnload, etc., but in components, they are created, Attached, ready, etc., so the specification is not uniform. It is a bit hard to understand why the page lifecycle method is in on+Xxx format, but it is not the same when it comes to components.

The development way

Small program official provides WeChat development tools Compiled as a development tool, for the code itself does not provide a similar webpack engineering development tools to solve some problems in the development, so the small program less modern native development way, this is also a lot of small application development framework to solve the problem. For example, in small program development

  • You can’t usenpmManagement depends onIn the small program, you need to manually download the third-party code file to the local, and thenreuqireIt’s not so elegant
  • You cannot use CSS preprocessors such as Sass, because there is no concept of precompiling, you cannot use commercially available CSS preprocessors in small program development, which makes style code difficult to manage
  • Incomplete ES Next syntax support. By default, small programs only support a small portion of the ES6 specification syntax, while ES continues to evolve and some of the best new syntax features are not available
  • Manual file processing, such as picture compression, code compression and so on some file operations, must be handled manually, it is a little cumbersome

The above is from the developer’s point of view to see some small program development problems, but even if there are thousands of difficulties, we always have to face, as the new era of front-end development engineers, we can not blindly tolerate problems, to keep the mind of technology, technology as a weapon, with technical means to improve our development experience.

Brainstorm: Can I use React to write small programs

At present, when talking about front-end framework in the front-end industry, React and Vue, which still maintain the dominant position, are both excellent front-end UI frameworks. Moreover, we can often see the enthusiastic communication between fans of the two frameworks on the Internet, and some sparks of ideas are generated, showing that the community is very active.

Last year, our team bravely abandoned its historical baggage and was honored to introduce the React development method, which enabled our team to lose the kerosene lamp and start to connect to electricity. In addition, we also developed an excellent React framework Nerv, which allows us to further integrate the React development ideas.

Compared with the development method of small programs, React is obviously more modern and standardized, and React is naturally componentization, which is more suitable for our business development, and JSX has stronger expression than string template. So at this point we were thinking, can we write a little program using React?

Explore rationally

analogy

By comparing the experience applet and React, we can still see some similarities between the two

The life cycle

The applet lifecycle is similar to the React lifecycle to a large extent, and we can even find a correspondence between them

Life cycle of app and page

Small program React
onLaunch componentWillMount
onLoad componentWillMount
onReady componentDidMount
onShow Not supported, requires special handling
onHide Not supported, requires special handling
onUnload componentWillUnmount

It can be seen that for app and page, except onShow and onHide methods, other methods can be found in React.

Data update mode

In React, the internal data of components is managed by state, while the internal data of components in small programs is managed by data, which is similar to some extent. Meanwhile, in React, we update the data using the setState method, passing in new data or a function that generates new data to update the view. In the applet, you have the setData method, which updates the view by passing in new data.

Both update in a data-driven view and have similar apis.

event

In applets, events are bound using the bind + event name, such as click event, and in applets, bindTap

<view bindtap="handlClick">1</view>
Copy the code

In React, it’s on + event name, like click event, onClick on the React Web

<View onClick={this.handlClick}>1</View>
Copy the code

It looks different, but the analogy is that we only need to compile on + event name to bind + event name at compile time.

So there are some similarities, and it seems possible to write small programs with React, but then we see a huge difference.

Huge difference

One of the biggest differences between React and applets is their templates. React uses JSX for component templates, while applets use string templates like Vue. There’s a huge difference between the two.

JSX

render () {
  return (
    <View className='index'>
      {this.state.list.map((item, idx) => (
        <View key={idx}>{item}</View>
      ))}
      <Button onClick={this.goto}>Go you</Button>
    </View>)}Copy the code

Applet template

 <view class="index">
   <view wx:key={idx} wx:for="{{list}}" wx:for-item="item" wx:for-index="idx">{{item}}</view>
   <view bindtap="goto">Go you</view>
 </view>
Copy the code

As we all know, JSX is essentially JS, and we can write any logical code in it, so it is much more expressive and operational than string template, and small program string template function is relatively weak, only a few more basic functions. So, how to implement using JSX to write small program templates?

The power of the compiler principle

We can carefully analyze our needs, we expect to use JSX to write small program template, but small program is obviously not support the execution of JSX code (if support, Taro should also do not exist), we can not expect wechat can give us a back door to run JSX. At this point we thought, if only we could compile JSX into small program templates.

In fact, in our daily development, this operation can be seen everywhere, Babel is our most commonly used JS code compiler, the general browser can not support some very new syntax features, but we want to use them, this time we can use Babel to our higher version of ES code, Compile to ES code that the browser can run. The same is true if we want to compile JSX into small program templates. Let’s first look at how Babel works.

As a code compiler, Babel can compile es6/7/8 code into ES5 code. Its core uses the very basic knowledge of compilation principles in calculation. It will input language code, execute it through the compiler, and output the target language code. The general process of compiling principle is to input the source program, through lexical analysis and syntax analysis, construct the syntax tree, and then through semantic analysis, understand whether the program is correct or not, and then make the required operation and optimization to the syntax tree, and finally generate the object code.

The same is true of the Babel compilation process, which consists of three phases

  • Parsing process, in which lexical, grammatical, and semantic analysis are performed to generate ESTree standard virtual syntax tree (AST)
  • The transformation process, which performs defined operations against the AST,babelConfiguration file of.babelrcDefined in thepresetpluginIt is in this step that the AST is executed and changed
  • Generate a string of object code from the AST converted in the previous step

To better understand these processes, you can use the Ast Explorer website to take a look at your code and get a feel for the Ast structure of each piece of code.

As you can see, when a copy of the source code is parsed by the compiler, it looks something like this

{
  type: "Program".start: 0.end: 78.loc: { start, end }
  sourceType: "module".body: [{type: "VariableDeclaration". }, {type: "VariableDeclaration". }, {type: "FunctionDeclaration". }, {type: "ExpressionStatement". }]... }Copy the code

The body contains the syntax tree structure of our sample code. The first VariableDeclaration corresponds to const a = 1, The third FunctionDeclaration corresponds to function sum (a, b) {}, which is the variable definition and function definition in JS, respectively. Each tree node contains many child nodes, thus forming a tree structure, more node types. See Babel types.

Of course, this is just a brief introduction to compilation principles and Babel. Compilation principles is a very esoteric course, and Babel is an excellent tool. I hope to discuss this in more detail in future articles.

Going back to our need to compile JSX into small program templates, we are very lucky that Babel’s core compiler Babylon supports parsing of JSX syntax, so we can use it directly to help us construct the AST, and the core we need to focus on is how to transform the AST. Get the new AST we need, and then recursively iterate through the new AST to generate the template of the applet.

JSX code

<View className='index'> <Button className='add_btn' onClick={this.props.add}>+</Button> <Button className='dec_btn' onClick={this.props.dec}>-</Button> <Button className='dec_btn' onClick={this.props.asyncAdd}>async</Button> <View>{this.props.counter. Num}</View> <A /> <A /> <Image SRC ={sd} /> </View>Copy the code

Compile to generate a small program template

<import src=".. /.. /components/A/A.wxml" />
<block>
  <view class="index">
    <button class="add_btn" bindtap="add">+</button>
    <button class="dec_btn" bindtap="dec">-</button>
    <button class="dec_btn" bindtap="asyncAdd">async</button>
    <view>{{counter.num}}</view>
    <template is="A" data="{{... ? A}}"></template>
    <button bindtap="goto">Go you</button>
    <image src="{{sd}}" />
  </view>
</block>
Copy the code

At this point, smart you should be able to find the difficulty of the problem, to know that the small program template is only a string, and JSX is the real JS code extension, its syntax is not a string template can be compared to, in this step, we will do the operation, including but not limited to the following

  • Understand the ternary operators and logical expressions such as the ternary operatorabc ? : <View>1</View> : <View>2</View>You need to compile into<view wx:if="{{abc}}">1</view><view wx:else>2</view>
  • Understand the arraymapGrammar, for examplemapThe use ofabc.map(item => <View>item</View>)You need to compile into<view wx:for="{{abc}}" wx:for-item="item">item</view>
  • , etc.

Above is just our transformation rules of the tip of the iceberg, JSX spelled extremely flexible, we can only through the way of exhaustion, the commonly used, the React official written recommendation as transformation rules to support, and some of the more uncommon, or is not recommended to write the written laws do not support, in turn, in the form of eslint plug-in Prompt the user to modify. We currently support JSX conversion rules that cover roughly 80% of JSX writing operations.

About JSX to small program template this part, we will be in the following technical principle analysis of the series of articles, detailed for you.

I was wondering if I could do something else

After our repeated explorations and a wave of ferocious operations, we have been able to convert React code into code that small programs can run. That is to say, we have been able to officially write small program code in the React way. Happy big pu rush! But when we were excited, we calmed down and kept thinking about whether we could do something else interesting.

Analyze the requirements

We found that in ordinary work, there are usually some of our business needs, is to require small program, H5 should have, or even an RN can have the best, I guess the product manager also disdain for quick application, or must to ask us to a set of quick application also, anyway, you are not often claims to be excellent, highly reusable code. At this point, you will find that you may need to repeat the same interface and logic several times. In this case, it would be better to have a multi-end code generation tool, which can only write one piece of code and run it multiple times. Write once, run Anywhere, is believed to be the dream of all engineers.

Still the power of the compiler principle

At this time we recall the content of the previous text, to compile a code into multi-end code, is not the principle of compilation, we can input a source code, for different end set the corresponding conversion rules, and then a key conversion of the corresponding end of the code. And since we already follow React syntax, we have a natural advantage in switching to H5 (using Nerv) and RN (using React).

Design ideas completed

However, if we think about it carefully, we will find that it is not enough to simply convert the code according to the corresponding syntax rules, because different sides will have their own native components, side capability APIS, etc., and the code may not be executed directly after the conversion. For example, a normal container component in a small program uses
, while in H5 it uses

. Small programs provide rich side capabilities API, such as network request, file download, data cache, etc., while in H5 corresponding functions API is inconsistent.

Therefore, in order to make up for the differences between the different ends, we need to customize a unified component library standard and a unified API standard, and rely on their syntax and capabilities to implement the component library and API on different ends. At the same time, we need to write the corresponding runtime framework for different ends, responsible for initialization and other operations. Through these operations, we can achieve a key to generate multiple requirements. In Taro’s initial design, our component library and API standard is derived from the small program, because I remember that we have defined component library and API standards, then why not use it directly, it not only save the customized brooding, but also saves as small application development component library and API of trouble, All you have to do is get the other end close to the applet.

Some may wonder how it is possible to write one copy of code when the component libraries and API capabilities are implemented for different ends (except for small programs, from which the component libraries and API standards are derived). Since we have the operation of compiling, when writing code, we only need to introduce the standard component library @tarojs/ Components and the runtime framework @tarojs/ Taro. After compiling, the code will become the library required by the corresponding end.

Since component libraries and side capabilities depend on different side implementations to smooth out the differences, we also sometimes need to follow this pattern if we want to introduce more support for Taro. For example, in order to improve development convenience, we added Redux support for Taro. What we did was, on the small program side, we implemented the @tarojs/ Redux library as a Redux auxiliary library for the small program, and using it as a base library. It has the same API as React-Redux, which refers to @tarojs/redux when writing code. After compiling, nerv-redux (nerv’s redux helper library) is replaced on the H5 end. It’s going to be react-redux on the RN end. This implements Redux’s multi-terminal support in Taro.

The above is the overall design idea of Taro, and there are many details that have not been elaborated. You may feel that there is more to be done. In the future, we will produce a series of articles to elaborate the technical details of Taro. For example, “Taro development tool principle analysis”, “Taro Code compilation behind”, “simple JSX to small program template” and so on.

At the end

It has been almost three months since the project was approved. From the initial intense discussion and collision of various ideas, Taro’s plan gradually took shape and entered the hot development iteration, to the smooth support of the small program side and H5 side, thus he decided to go to open source. Along the way, I have gained a lot, not only the excitement of creating together with my team mates, but also countless days and nights of hard thinking. Taro is the sincere work of Bump Lab, and we will maintain it all the time. We hope Taro will become better and better, and help more people to create more value.

Project website: Taro. Aotu.io /

Project GitHub: github.com/NervJS/taro