ReactThe community has been exploring useReactThe way grammar develops small programs, among which the more famous ones areTaronanachi。 While usingReactThe main difficulty of grammar development isJSXIn grammar,JSXIn essence isJS, which is too flexible compared with the static template of the applet. The new idea in this paper is to deal with it.JSXA new way of thinking in grammar, which is a more dynamic way of thinking, is basically not limited to any of the existing solutions.JSXIt lets you handle small programs in a real React way. I hope this new idea can give any aspiration to use it.ReactPeople who develop small programs bring inspiration.

Limitations of existing ideas

Before introducing new ideas, let’s take a lookTaro (latest version 1.3)nanachiHow to deal with it on the small program sideJSXGrammatical. Simply put, it’s mainly through the compilation phase.JSXConverting to an equivalent programwxmlCome on.ReactThe code runs on the small program side.

For example, for exampleReactLogical expression:


xx && <Text>Hello</Text>

It will be converted into an equivalent widget wx:if instruction:


<Text wx:if="{{xx}}">Hello</Text>

This is the right way to do it.JSXFor example, it must recognize the logical expression and then do the correspondingwx:ifConversion processing.

What are the problems and limitations of the compilation phase? Let’s illustrate with the following examples:


class App extends React.Component {
  render () {
    const a = <Text>Hello</Text>
    const b = a

    return (
      <View>
        {b}
      </View>
    )
  }
}

First of all, we declare thatconst a = <Text>Hello</Text>And thenaAssign tobLet’s look at the latest versionTaro 1.3The conversion is as follows:

This example is not particularly complicated, but it is wrong.

To understand why the above code fails, we first need to understand the compilation phase. Essentially in the compilation stage, the code is actually a string, and the compilation stage processing scheme needs to analyze the necessary information from the string (through theASTAnd then do the corresponding equivalent transformation.

And for the example above, what equivalence should be done? We need to analyze it at the compilation stage.byesJSXFragment:b = a = <Text>Hello</Text>And then<View>{b}</View>Medium{b}Equivalent substitution<Text>Hello</Text>。 However, at the compilation stage, you want to determinebThe value of B is very difficult. Some people say that it can be traced back to determine the value of B. It’s not impossible, but consider that becauseb = aWell, first of all, make sure.aThe value of thisaHow to determine the value of? Need tobIdentification in accessible scoping chainsaHowever,aMaybe it comes from the assignment of other variables, cycling and reciprocating. If there are not simple assignments, such as function calls, ternary judgments and other runtime information, traceability will fail.aIn itself, it is a variable hanging on the global object, and traceability is even more impossible to talk about.

So it’s not easy to determine at the compilation stage.bThe value of.

Let’s take a closer look at the error message in the picture above.a is not defined

Why?aNot defined? That’s another issue, we know.<Text>Hello</Text>, which is equivalent toReact.createElement(Text, null, 'Hello')AndReact.createElementThe return value of the method is a normal valueJSObjects, such as

// ReactElement object
{
  tag: Text,
  props: null,
  children: 'Hello'
  ...
}

So the code above is inJSWhen the environment really works, it is roughly equivalent to the following:


class App extends React.Component {
  render () {
    const a = {
      tag: Text,
      props: null,
      children: 'Hello'
      ...
    }
    const b = a

    return {
      tag: View,
      props: null,
      children: b
      ...
    }
  }
}

However, we just said that the compilation phase needs toJSXEquivalent treatment requires thatJSXConvert towxmlSo<Text>Hello</Text>thisJSXThe fragments were specially processed.aNo longer an ordinary personjsObject, here we seeaVariables are even missing, which exposes a serious problem: code semantics are broken, that is, due to compile-time schema pairsJSXSpecial processing, code semantics that really run on small programs are not what you expect. This is a headache.

New ideas

Because compile-time solutions, like the above limitations, often let you have “I’m still writing” when using them.React” This feeling.

Here we introduce a new way of thinking, which is true during the running of the applet.ReactIt’s almost the same, and it doesn’t change any code semantics.JSXExpression will only be processed asReact.createElementMethod call, which is normal in actual operationjsObject, and ultimately render the widget view in other ways. Next, we will elaborate on the specific content of this idea.

Step 1: Give each individualJSXFragments are uniquely identifieduuidSuppose we have the following code:


const a = <Text uuid="000001">Hello</Text>

const y = <View uuid="000002">
  <Image/>
  <Text/>
</View>

We giveaFragmentsyClip addeduuidattribute

Step 2:ReactCode passingbabelConverted to code recognizable by a widget, such asJSXEquivalent for fragmentsReact.createElementReplacement, etc.


const a = React.createElement(Text, {
 uuid: "000001"
}, "Hello");

Step 3: Extract each individualJSXSnippets, with small programstemplatePackaging, Generationwxmlfile

<template name="000001">
  <Text>Hello</Text>
</template>

<template name="000002">
  <View uuid="000002">
    <Image/>
    <Text/>
  </View>
</template>


<! - Placement template - >
<template is="{{uiDes.name}}" data="{{...uiDes}}"/>

Pay attention to every one heretemplateOfnameLogo andJSXUnique identification of fragmentsuuidIt’s the same. Finally, a placeholder template needs to be generated at the end:<template is="{{uiDes.name}}" data="{{...uiDes}}"/>

Step 4: AmendmentReactDOM.renderRecursion (React 16.xLater, not in a recursive way.) Processes, recursive execution stages, aggregationJSXFragmentuuidAttribute, Generate and ReturnuiDesData structure.

Step 5: Generate Step 4uiDesPass it to the widget environment.uiDesSet to placeholder template<template is="{{uiDes.name}}" data="{{...uiDes}}"/>Render the final view.

We are above.AppComponent examples illustrate the whole process. First of alljsCode will be escaped to:


class App extends React.Component {
  render () {
    const a = React.createElement(Text, {uuid: "000001"}, "Hello");
    const b = a
    
    return (
     React.createElement(View, {uuid: "000002"} , b);
    )
   }
}

Simultaneous generationwxmlDocument:

<template name="000001">
  <Text>Hello</Text>
</template>

<template name="000002">
  <View>
    <template is="{{child0001.name}}" data="{{...child0001}}"/>
  </View>
</template>

<! - Placement template - >
<template is="{{uiDes.name}}" data="{{...uiDes}}"/>

After using our customizationrenderimplementReactDOM.render(<App/>, parent)。 stayrenderIn addition to the routine creation of component instances and the execution lifecycle, additional collection of components in the execution process will be performed during the recursion process of _________.uuidIdentification, final generationuiDesobject


const uiDes = {
  name: "000002",
  
  child0001: {
      name: 000001,
      ...
  }
  
  ...
}

The applet gets thisuiDesSet to placeholder template<template is="{{uiDes.name}}" data="{{...uiDes}}"/>。 Finally render the applet view.

Throughout the process, all you haveJSThe code is running inReact processIn Chinese, the semantics are identical.JSXThe fragments will not be processed in any particular way, but simplyReact.createElementCall, and because of theReact processJust purejsOperations, execution is very fast, usually only a few Ms. Ultimately one will be output.uiDesData to the applet, the applet passes through thisuiDesRender the view.

Now let’s look at the previous assignment.const b = a, there won’t be any problems, becauseaIt’s just an ordinary object. In addition, there are restrictions on common compile time schemes, such as arbitrary function return.JSXFragment, dynamic generationJSXFragmentsforRecyclingJSXThe fragments and so on can be completely released, becauseJSXFragment onlyjsObject, you can do anything, eventuallyReactDOM.renderWill collect all fragments of execution resultsuuidIdentification, GenerationuiDesAnd the applet will follow thisuiDesThe data structure renders the final view.

It can be seen that this new idea is quite different from previous compile-time schemes.JSXThe processing of fragments is dynamic, and you can appear anywhere, anywhere, in any function.JSXFragment, the final execution result determines which fragment to render, only the fragment that executes the resultuuidWill be writtenuiDes。 This is essentially different from the static identification of schemes in compilation.

epilogue

“Talk is cheap. Show me your code! “Is that just a way of thinking? Or has it been fully realized?

There is a complete implementation, and the Alita project is working on it.JSXThis is the idea used in grammar, which is why Alita can transform the whole React Native project without restricting its writing. In addition, Alita has done a lot of optimization on this idea. If you are interested in the specific implementation of this idea, you can go to read the Alita source code, which is totally open-source https://github.com/areslabs/alita.

Of course, you can also construct your own React applet development program based on this idea.

The above is the whole content of this article. I hope it will be helpful to everyone’s study, and I hope you will support developpaer more.