preface
JSX and virtual DOM have always been common questions in react interviews, but only when you understand the questions can you translate them into your real strength.
What is the JSX?
JSX is a JavaScript syntax extension that allows you to declare a variable like this in react projects.
const element = <div class='a'>hello world!</div>
// in Babel will compile to
const element = /*#__PURE__*/React.createElement("div", {
class: "a"
}, "hello world!");
Copy the code
So JSX is the syntactic sugar of react.createElement (), which is compiled by Babel into the react.createElement method.
Is that why you have to declare it explicitly in every JS file that uses JSX
import React from 'react'
Copy the code
However, React 17 introduces two new entries in the React package that are only used by compilers like Babel and TypeScript. Instead of converting JSX to React. CreateElement, the new JSX transformation automatically introduces new entry functions from the React package and calls them.
// Assume your source code is as follows
function App() {
return <h1>Hello World</h1>;
}
// Below is the result of the new JSX transformation:
// Imported by the compiler (forbid self-imported!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
Copy the code
React.createElement
React.createElement calls ReactElement internally and returns an object.
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if(config ! =null) {
// assign the config to props
/ /... omit
}
const childrenLength = arguments.length - 2;
// Handle children, which is assigned to props. Children
/ /... omit
/ / defaultProps processing
/ /... omit
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// mark this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
return element;
};
Copy the code
ReactElement will eventually return a JS object containing component data, known as a virtual DOM object, which is also a React element, with a $$typeof attribute assigned as a constant token of REACT_ELEMENT_TYPE. This object is a valid React element.
React also provides a global API to verify that an object is a valid React element
export function isValidElement(object) {
return (
typeof object === 'object'&& object ! = =null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
Copy the code
$$typeof
The $$Typeof attribute also protects against XSS attacks.
If the server allows the user to store arbitrary JSON data. You can then manually build React Element objects into the Element. Such as:
// Server could have a hole that lets user store JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* put your exploit here */'
},
},
// ...
};
let message = { text: expectedTextButGotJSON };
// Dangerous in React 0.13
<p>
{message.text}
</p>
Copy the code
Definition of the REACT_ELEMENT_TYPE value
if (typeof Symbol= = ='function' && Symbol.for) {
const symbolFor = Symbol.for;
REACT_ELEMENT_TYPE = symbolFor('react.element');
REACT_PORTAL_TYPE = symbolFor('react.portal');
REACT_FRAGMENT_TYPE = symbolFor('react.fragment'); . }Copy the code
The Symbol type is used because symbols cannot be passed in JSON. React circumvent this problem by checking element.$$Typeof and refusing to handle illegal elements.
React Component and React Element
When class component or function Component is used, the component is passed as type, the first argument to the react. createElement function
class ClassComp {
render() {
return <div>ClassComp</div>}}const FuncComp = () = > {
return <div>FuncComp</div>
}
const element1 = <ClassComp />
const element2 = <FuncComp />
// in Babel will compile to
class ClassComp {
render() {
return /*#__PURE__*/React.createElement("div".null."ClassComp"); }}const FuncComp = () = > {
return /*#__PURE__*/React.createElement("div".null."FuncComp");
};
const element1 = /*#__PURE__*/React.createElement(ClassComp, null);
const element2 = /*#__PURE__*/React.createElement(FuncComp, null);
Copy the code
Note that if the name of the custom component is not uppercase, Babel will only be converted to a normal label, also called HostComponent.
const funcComp = () = > {
return <div>FuncComp</div>
}
const element1 = <funcComp />
// in Babel will compile to
const funcComp = () = > {
return /*#__PURE__*/React.createElement("div".null."FuncComp");
};
const element1 = /*#__PURE__*/React.createElement("funcComp".null);
Copy the code
The result is that type becomes “funcComp” instead of the funcComp component, which is why the React custom component starts in uppercase.
JSX and Fiber nodes
As we can see from the above, JSX is a data structure that describes the content of the current component. It does not contain the relevant information required for schedule, Reconcile, and Render components.
For example, the following information is not included in JSX:
- Priority of the component in the update
- Components of the state
- The markup that the component is tagged with for the Renderer
- All of this is contained in the Fiber node.
So, when the components are mounted, the Reconciler generates the corresponding Fiber nodes for the components based on the contents of the components described by the JSX.
In the Reconciler update, the DATA stored in the JSX with the Fiber nodes are compared to generate the Fiber nodes corresponding to the components, and the Fiber nodes are marked based on the comparison results.