current position:Home>Why is JSX syntax so popular?

Why is JSX syntax so popular?

2022-06-24 09:47:46Bai Xiaobai

Keep creating , Accelerate growth ! This is my participation 「 Nuggets day new plan · 6 Yuegengwen challenge 」 Of the 9 God , Click to see the event details

Preface

Nowadays, although access JSX The framework of grammar (React、Vue) More and more , But there is no doubt that the deepest fate is still React.2013 year , When React With JSX When Hengkong was born , The community used to JSX There has been a lot of controversy , But now , More and more people are facing JSX Say the last sentence “ It's delicious ”! Typical “ It's delicious ” series .

JSX What is it? ?

according to React Official explanation ,JSX It's a JavaScript Grammar extension of , Similar to template syntax , Or something like XML Of ECMAScript Grammar extension , And have JavaScript All the functions of .

There are two key points in this explanation :

  • 「JavaScript Grammar extension 」
  • 「 Have JavaScript All the functions of 」

JSX The location is JavaScript Of 「 Grammar extension 」, instead of “ A version ”, This determines that the browser will not be like natural support JavaScript As support JSX . This raises a question “JSX How is the JavaScript In effect ?”

JSX How grammar works in JavaScript In effect ?

React

stay React In the frame ,JSX How is the grammar of JavaScript In effect ?React The explanation given on the official website is ,JSX Will be compiled as React.createElement(), React.createElement() Will return a message called “React Element” Of JS object .

about JSX Is compiled by Babel To complete .

Babel It's a tool chain , It's mainly used to use ECMAScript 2015+ Syntax written code into backward compatible JavaScript grammar , To be able to run in current and older browsers or other environments .

Of course Babel Also have the ability to JSX Convert to JS The ability of , Take an example : On the left is us React The syntax written in the development , And it contains a paragraph JSX Code . after Babel After the transformation , It's all about JS Code .

In fact, if you look carefully , Find out JSX More like a grammar sugar , It is described in a way similar to template syntax , Describe function objects . Actually in React Is not mandatory in JSX grammar , We can also use React.createElement function , For example, using React.createElement Function to write such a piece of code .

class Test extends React.Component {
  render() {
    return React.createElement(
      "div",
      null,
      React.createElement(
        "div",
        null,
        "Hello, ",
        this.props.test
      ),
      React.createElement("div", null, "Today is a fine day.")
    );
  }
}

ReactDOM.render(
  React.createElement(Test, {
    test: "baixiaobai"
  }),
  document.getElementById("root")
);

When using JSX after , This code will be written like this :

class Test extends React.Component {
  render() {
    return (
    	<div> <div>Hello, {this.props.test}</div> <div>Today is a fine day.</div> </div>
    );
  }
}
ReactDOM.render(
  <Test test="baixiaobai" />,
  document.getElementById('root')
);

Through comparison, we found that , On the premise that the actual function effect is consistent ,JSX The code is hierarchical 、 The nesting relationship is clear ; and React.createElement The code gives a very confusing “ Hybridity ”, Such code is not only unfriendly to read , It's hard to write .

JSX The code written by syntax is more concise , And the code structure is more clear .

JSX Syntax sugar allows us developers to write like HTML To write our JS Code . It not only reduces the learning cost, but also improves our R & D efficiency and R & D experience .

Vue

Of course. Vue The framework is no exception JSX grammar , although Vue The default recommended template is still .

Why default recommended template syntax , Quote a paragraph Vue The original words of the official website are as follows :

Any standard HTML It's all legal Vue Templates , This also brings some unique advantages :

  • For a lot of people who are used to HTML For the developers , Template is better than JSX It's more natural to read and write . There is, of course, an element of subjective preference , But if this difference leads to an increase in development efficiency , Then it has objective value .
  • be based on HTML Make the existing application migrate to Vue It's easier .
  • It also makes it easier for designers and new developers to understand and participate in projects .
  • You can even use other template preprocessors , such as Pug To write Vue The template of .

Some developers think templates mean learning extra DSL (Domain-Specific Language Domain specific language ) To develop —— We think the difference is superficial . First ,JSX It's not that there's no learning cost —— It is based on JS An extra set of grammars on top of . meanwhile , Just as familiar with JS People learn JSX It's going to be as easy as , be familiar with HTML People learn Vue The template syntax is also very easy . Last ,DSL We can let developers do more with less code , such as v-on All kinds of modifiers for , stay JSX It takes a lot more code to implement the corresponding function in .

More abstract , We can divide components into two categories : One is the partial view (presentational), One is partial logic (logical). We recommend using templates in the former , In the latter use JSX Or render functions . The proportion of these two types of components will vary according to the application type , But on the whole, we find that the components of presentation class are far more than those of logic class .

For example, there is a template syntax .

<anchored-heading :level="1">
  <span>Hello</span> world!
</anchored-heading>

Use JSX The grammar will be written like this .

render: function (h) {
  return (
    <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading>
  )
}

Convert to createElement The conversion JS That's how it became .

createElement(
  'anchored-heading', {
    props: {
      level: 1
    }
  }, [
    createElement('span', 'Hello'),
    ' world!'
  ]
);

But whether it's template syntax or JSX grammar , Will not get the browser's natural support , These grammars will eventually be compiled into corresponding h function (createElement function , Not all versions , There are differences in different versions ) Finally become JS object , The compilation here is also the same as React The same thing Babel plug-in unit To complete .

Whether it's React Highly esteems JSX grammar , still Vue Default template syntax , Purpose To make our code more concise , And the code interface level is more clear . It not only reduces the learning cost, but also improves our R & D efficiency and R & D experience .

Read here , I believe you have fully understood “JSX yes JavaScript A grammatical extension of , It's very close to the template language , And have JavaScript All the functions of . ” The meaning behind this definition .

Whether it's React still Vue We all mentioned a function createElement, This function is to put our JSX It maps to DOM Of .

JSX How to map to DOM Of : Bottom up createElement Source code

about creatElement Source code analysis , We also share React and Vue To interpret .

There is no need to discuss the specific version of source code analysis in too much detail , Because it doesn't matter React still Vue For implementing createElement There is little difference between different versions of .

React

export function createElement(type, config, children) {
  // propName  Variables are used to store the element attributes that need to be used later 
  let propName; 
  // props  Variable is used to store the collection of key value pairs of element attributes 
  const props = {}; 
  // key、ref、self、source  Are all  React  Attribute of element , There is no need to delve into 
  let key = null;
  let ref = null; 
  let self = null; 
  let source = null; 
  // config  Object stores the attributes of the element 
  if (config != null) { 
    //  The first thing you do when you come in , Yes, in turn  ref、key、self  and  source  Attribute assignment 
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    //  Here will be  key  Value string 
    if (hasValidKey(config)) {
      key = '' + config.key; 
    }
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    //  The next step is to  config  The properties inside are moved to one by one  props  In this previously declared object 
    for (propName in config) {
      if (
        //  Filter out items that can be brought into  props  Attributes in objects 
        hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) 
      ) {
        props[propName] = config[propName]; 
      }
    }
  }
  // childrenLength  It refers to the number of child elements of the current element , Subtracted  2  yes  type  and  config  The length occupied by the two parameters 
  const childrenLength = arguments.length - 2; 
  //  If you throw away type and config, There is only one parameter left , Generally, it means that the text node appears 
  if (childrenLength === 1) { 
    //  Directly assign the value of this parameter to props.children
    props.children = children; 
    //  Handle the case of nesting multiple child elements 
  } else if (childrenLength > 1) { 
    //  Declare an array of child elements 
    const childArray = Array(childrenLength); 
    //  Push the child elements into the array 
    for (let i = 0; i < childrenLength; i++) { 
      childArray[i] = arguments[i + 2];
    }
    //  Finally, assign this array to props.children
    props.children = childArray; 
  } 

  //  Handle  defaultProps
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) { 
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  //  Finally, it returns a call ReactElement Execution method , And pass in the parameters just processed 
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

createElement Function has 3 Input parameters , this 3 The input parameter contains the one we are creating React All information about the element .

  • type: Used to identify the type of node . It can be original div 、span In this way HTML label , It can also be React Components , It can also be React fragment( Empty elements ).
  • config: An object , All properties of the component ( Does not contain some default properties ) Will be stored in the form of key value pairs config In the object .
  • children: It generally refers to all parameters after the second parameter , It records the content nested between component tags , It's called “ Child node ”“ Subelement ”.

From the source point of view ,createElement Function is the data written by the R & D personnel during development 、 attribute 、 Parameters are formatted in one layer , Turn into React Easy to understand parameters , And then deliver it to ReactElement To implement element creation .

So let's see ReactElement function

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    //  Mark this is a  React Element
    $$typeof: REACT_ELEMENT_TYPE,

    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };

  return element;
};

The source code is exceptionally simple , That is to say createElement Function conversion parameters , In one process , Packing in element Object to the developer . If you try to return this ReactElement For the output , You will find that there is no very familiar feeling , you 're right , This is what we always talk about 「 fictitious DOM」,JavaScript Object pair DOM Description of .

Finally through ReactDOM.render Method will be virtual DOM Render to the specified container .

Vue

Vue 2

Let's see Vue How to map DOM Of .

export function createElement ( context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean ): VNode | Array<VNode> {
  ...
  return _createElement(context, tag, data, children, normalizationType)
}

createElement Function is to _createElement An encapsulation of functions , It allows more flexibility in the parameters passed in , After processing these parameters , Call to really create VNode Function of _createElement:

export function _createElement ( context: Component, tag?: string | Class<Component> | Function | Object, data?: VNodeData, children?: any, normalizationType?: number ): VNode | Array<VNode> {
  ...
  return vnode;
}

_createElement There are methods 5 Parameters :

  • context Express VNode Context of .
  • tag It means label , It can be a string , It can also be a Component.
  • data Express VNode The data of .
  • children At present VNode Child nodes of , It's any type of , It then needs to be standardized VNode Array .
  • normalizationType Indicates the type of child node specification , Different types of specifications have different methods , It is mainly for reference render Whether the function is compiled or handwritten by the user .

_createElement The implementation content is a little more , There is no detailed analysis here , Anyway, one will be created in the end VNode , Every VNode Yes children,children Each element is also a VNode, And that's what makes a VNode Tree, It's a good description of our DOM Tree.

When VNode Once you've created it , Just come down and put VNode Render into a real DOM And render it . The process is through vm._update Accomplished .Vue Of _update Is a private method of the instance , When it is called 2 individual , One is the first rendering , One is when the data is updated , We'll only see the first rendering here ; When calling _update when , The core is the call vm.patch Method .

patch: This method is actually used on different platforms , such as web and weex The definition is different

Introduce a piece of code to see the specific implementation .

var app = new Vue({
  el: '#app',
  render: function (createElement) {
    return createElement('div', {
      attrs: {
        id: 'app'
      },
    }, this.message)
  },
  data: {
    message: 'Hello Vue!'
  }
});

stay vm._update That's what's called in the method of patch Methodical :

if (!prevVnode) {
  //  First render 
  vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
} else {
  //  to update 
  vm.$el = vm.__patch__(prevVnode, vnode);
}

First render :

  • $el The corresponding is id by app Of DOM Elements .
  • vnode The corresponding is render Function by createElement Function created fictitious DOM.
  • hydrating In the case of non server rendering, it is false.

After confirming the parameters for the first rendering , Let's see patch Implementation process of . A smelly and long source code .

function patch (oldVnode, vnode, hydrating, removeOnly) {
      if (isUndef(vnode)) {
        if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }
        return
      }

      var isInitialPatch = false;
      var insertedVnodeQueue = [];

      if (isUndef(oldVnode)) {
        // empty mount (likely as component), create new root element
        isInitialPatch = true;
        createElm(vnode, insertedVnodeQueue);
      } else {
        var isRealElement = isDef(oldVnode.nodeType);
        if (!isRealElement && sameVnode(oldVnode, vnode)) {
          // patch existing root node
          patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
        } else {
          if (isRealElement) {
            // mounting to a real element
            // check if this is server-rendered content and if we can perform
            // a successful hydration.
            if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
              oldVnode.removeAttribute(SSR_ATTR);
              hydrating = true;
            }
            if (isTrue(hydrating)) {
              if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
                invokeInsertHook(vnode, insertedVnodeQueue, true);
                return oldVnode
              } else {
                warn(
                  'The client-side rendered virtual DOM tree is not matching ' +
                  'server-rendered content. This is likely caused by incorrect ' +
                  'HTML markup, for example nesting block-level elements inside ' +
                  '<p>, or missing <tbody>. Bailing hydration and performing ' +
                  'full client-side render.'
                );
              }
            }
            // either not server-rendered, or hydration failed.
            // create an empty node and replace it
            oldVnode = emptyNodeAt(oldVnode);
          }

          // replacing existing element
          var oldElm = oldVnode.elm;
          var parentElm = nodeOps.parentNode(oldElm);

          // create new node
          createElm(
            vnode,
            insertedVnodeQueue,
            // extremely rare edge case: do not insert if old element is in a
            // leaving transition. Only happens when combining transition +
            // keep-alive + HOCs. (#4590)
            oldElm._leaveCb ? null : parentElm,
            nodeOps.nextSibling(oldElm)
          );

          // update parent placeholder node element, recursively
          if (isDef(vnode.parent)) {
            var ancestor = vnode.parent;
            var patchable = isPatchable(vnode);
            while (ancestor) {
              for (var i = 0; i < cbs.destroy.length; ++i) {
                cbs.destroy[i](ancestor);
              }
              ancestor.elm = vnode.elm;
              if (patchable) {
                for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {
                  cbs.create[i$1](emptyNode, ancestor);
                }
                // #6513
                // invoke insert hooks that may have been merged by create hooks.
                // e.g. for directives that uses the "inserted" hook.
                var insert = ancestor.data.hook.insert;
                if (insert.merged) {
                  // start at index 1 to avoid re-invoking component mounted hook
                  for (var i$2 = 1; i$2 < insert.fns.length; i$2++) {
                    insert.fns[i$2]();
                  }
                }
              } else {
                registerRef(ancestor);
              }
              ancestor = ancestor.parent;
            }
          }

          // destroy old node
          if (isDef(parentElm)) {
            removeVnodes([oldVnode], 0, 0);
          } else if (isDef(oldVnode.tag)) {
            invokeDestroyHook(oldVnode);
          }
        }
      }

      invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
      return vnode.elm
    }

In the first rendering , Because of the oldVnode( id by app Of DOM Elements ) It's actually a DOM container, Then through emptyNodeAt Method to oldVnode convert to VNode object , Then call createElm Method , Create real... Through virtual nodes DOM And insert it into its parent node .

Through the bottom React and Vue Of createElement Source code , Analysis of the JSX How to map to reality DOM Of , The overall direction of the realization idea is the same . So we are all learning from each other , Learn from each other .

Why? React Choose from the beginning JSX?

stay 2013 year ,React With JSX Syntax appears , It was controversial when it first appeared , Why? React Will choose JSX? Not any other grammar . such as :

Templates

The typical template syntax is AngularJS, If you used AngularJS, You will find that a lot of concepts will be introduced into the template , For example, the new template syntax 、 New template directive .

<div ng-controller="Ctrl1">
      Hello <input ng-model='name'> <hr/>
      <span ng-bind="name"></span> <br/>
      <span ng:bind="name"></span> <br/>
      <span ng_bind="name"></span> <br/>
      <span data-ng-bind="name"></span> <br/>
      <span x-ng-bind="name"></span> <br/>
</div>

angular.module('test', [])
  .controller('Ctrl1', function Ctrl1($scope) {
    $scope.name = '1';
});

React The original intention of the design is **「 Separation of concerns 」,React The basic unit of concern itself is the component , High cohesion within the component , Low coupling between components . The template syntax cannot . also JSX It will not introduce too many new concepts .** It can also be seen that React The code is simpler , More readable , Closer to HTML.

const App = (props) => {
  return (
    <div> xxx </div>
  )
}

Template string

JSX The syntax of is a bit like a template string , If in the early years , Have used PHP + JQuery Students of the technology stack may have written code with such a syntax .

var box = jsx` <${Box}> ${ true ? jsx`<${Box.Comment}> Text Content </${Box.Comment}>` : jsx` <${Box.Comment}> Text Content </${Box.Comment}> ` } </${Box}> `;

I don't know what you think , Anyway, when I was writing such code, it was very painful , And the code results become more complex , Not conducive to later maintenance .

JXON

<catalog>
  <product description="Cardigan Sweater"> 1111 </product>
  <script type="text/javascript"><![CDATA[function matchwo(a,b) { if (a < b && a < 0) { return 1; } else { return 0; } }]]> </script>
</catalog>

But finally give up JXON The reason for this scheme is , Braces cannot be the starting and ending positions of elements in the tree , Provide good syntax hints .

template

<template>
  <div>1</div>
<template>
<script>
  ....
<script>

Then why can't it be with Vue The use of Template syntax ? JSX Nature is JavaScript, To achieve conditional rendering, you can use if else, You can also use ternary expressions , You can also use any legal JavaScript grammar . in other words ,JSX Can support more dynamic requirements . and template Because of grammatical restrictions , It can't be like JSX That can support more dynamic requirements . This is a JSX Compared with template An advantage of . JSX Compared with template There's another advantage , Yes, you can return multiple components in one file .

But just Vue Come on , Default choice template Grammar also has a reason ,template Because the syntax is fixed , There are many optimizations that can be done at the compilation level , For example, static tags can really update on demand ; and JSX Due to the strong dynamics , Optimization can only be done in limited scenarios , Although the performance is not as good as template good , But in some scenes with high dynamic requirements ,JSX It's standard , This is also used by many component libraries JSX The main reason is .

summary

By comparing various schemes , Find out JSX He has his own advantages ,JSX The code written by syntax is more concise , And the code structure is more clear .JSX Syntax sugar allows us developers to write like HTML To write our JS Code . It not only reduces the learning cost, but also improves our R & D efficiency and R & D experience .

also JSX There is not much grammar in itself , Nor do we expect to introduce more standards . actually , stay 16 In the year ,JSX Published 2.0 The construction plan and a small number of new features , But soon Facebook Give up . The whole plan was stopped within two months after it was announced . One reason is JSX Design intention of , I don't want to introduce too many standards , Not expected JSX Join the browser or ECMAScript standard .

Reference resources

copyright notice
author[Bai Xiaobai],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/175/202206240859173403.html

Random recommended