To thoroughly understand the principle and implementation of react framework, learning source code is the only way. During this study period, we should absorb coding ideas and design ideas to the maximum extent. If you are in a stage where you want to improve yourself, let’s communicate and learn together
What is JSX
- JSX is a syntactic sugar that passes during compilationbabeljsTranslated into
React.createElement
grammar React.createElement
It returns a React element- The React element is actually a regular JS object, known as the virtual DOM, that describes what is displayed on the screen
ReactDOM
To ensure that the actual DOM data in the browser is consistent with the React elementReactDOM.render
Convert the virtual DOM into the real DOM and insert it into the container
JSX compilation process
/ / JSX:
<h1 className="title" style={{color:'red'}}>hello</h1>
// React17 previous compilation result:
React.createElement("h1", {
className: "title".style: { color: 'red'}},"hello")
// after React17:
// If JSX is used, the 'react/ jsx-Runtime 'package is automatically introduced
// Remove React17 before import React from 'React' : develop-time ESLint error, runtime React undefined
// React.createElement ==> _jsx
import { jsx as _jsx } from 'react/jsx-runtime';
let element = _jsx("h1", {
className: "title".style: { color: 'red'}},"hello")
// The result returned
{ type:'h1'.props: {className: "title".style: { color: 'red'}},children:"hello" }
Copy the code
Three. Implementation ideas
1. Realize the React. CreateElement method ()
1) Create our own createElement method using the React createElement method. The input parameters are: element type (div/span), config (key/ref), and children. There may be more than one children, so to determine the children, when there are more than one child elements, should be put in the array.
2) Wrap primitive types as objects to facilitate subsequent DOM-diff comparisons. Create the tool function wrapToVdom with the value of elemment. If the parameter is string/number, it is wrapped as an object and returned.
3) Wrap the element originally returned from createElement
2. Implement ReactDOM. Render ()
Create render with vDOM and container. The function is mounted internally
2) Create the mount function mount (vdom, container).
Create createDOM (vdom); Convert the virtual DOM to the real DOM. First, determine the case that vDOM does not exist, and then do different processing according to different types of virtual DOM:
-
REACT_TEXT: Creates the text node createTextNode
-
Common elements: createElement createElement. Handle internal properties. Properties can be updated later. So you need to update the property method updateProps. Determine properties
-
React element: Mount directly
-
Array: Iterate through the array and mount each item
Implement JSX
1. Create projects
create-react-app react-basic
cd react-basic
yarn add cross-env
Copy the code
2. src/index.js
import React from "./react";
import ReactDOM from "./react-dom";
let element1 = (
<div className="title" style={{ color: "red}} ">
<span>hello</span>world
</div>
);
console.log(JSON.stringify(element1, null.2));
// reactdom. render takes care of rendering, turning the react element into a real DOM render/insert it into the DOM container
// Bable does not compile JSX into the virtual DOM, but JSX into the React createElement method call
// The react. createElement is executed during browser execution, and the virtual DOM is generated
ReactDOM.render(element1, document.getElementById("root"));
Copy the code
3. src/constants.js
export const REACT_TEXT = Symbol('REACT_TEXT');
export const REACT_ELEMENT = Symbol('react.element');
Copy the code
4. src/utils.js
import { REACT_TEXT } from "./constants";
export function wrapToVdom(element) {
return typeof element === "string" || typeof element === "number"
? { type: REACT_TEXT, props: { content: element } }
: element; }
Copy the code
5. src/react.js
import { wrapToVdom } from "./utils";
import { REACT_ELEMENT } from "./constants";
/** * Create a virtual DOM == React element *@param {*} Type The type of the element span div P *@param {*} Config Config object className style *@param {*} Children, single - object/multiple - array */
function createElement(type, config, children) {
let ref; // This element can be referenced by ref
let key; // The child element's key is uniquely identified
if (config) {
delete config.__source; // Delete temporarily useless attributes source: bable Compiled attribute
delete config.__self;
ref = config.ref; // Retrieve the ref key
delete config.ref; // Delete the ref key from config
key = config.key;
delete config.key;
}
letprops = { ... config };if (arguments.length > 3) {
// If there are more than three entries, there are multiple child elements
props.children = Array.prototype.slice.call(arguments.2).map(wrapToVdom);
} else {
// React element object or string/number/null/und
props.children = wrapToVdom(children);
}
return {
$$typeof: REACT_ELEMENT,
type,
ref,
key,
props,
};
}
const React = { createElement, };
export default React;
Copy the code
6. src/react-dom.js
import { REACT_TEXT } from "./constants";
/** * Insert the virtual DOM into the real DOM@param {*} Vdom Virtual DOM *@param {*} The container vessel * /
function render(vdom, container) {
mount(vdom, container);
}
export function mount(vdom, container) {
let newDOM = createDOM(vdom);
container.appendChild(newDOM);
}
/** Convert the virtual DOM to the real DOM */
export function createDOM(vdom) {
if(! vdom)return null; // null and und are also legal DOM
let { type, props } = vdom;
let dom; / / true DOM
if (type === REACT_TEXT) {
If the element is text, create a text node
dom = document.createTextNode(props.content);
} else {
dom = document.createElement(type);
}
// Handle attributes
if (props) {
updateProps(dom, {}, props);
if (typeof props.children == "object" && props.children.type) {
mount(props.children, dom);
} else if (Array.isArray(props.children)) {
reconcileChildren(props.children, dom);
}
}
vdom.dom = dom;
return dom;
}
/** * Update the new attribute to the real DOM *@param {*} Dom Real DOM *@param {*} OldProps Old property object *@param {*} NewProps New property object */
function updateProps(dom, oldProps={}, newProps={}) {
for (let key in newProps) {
if (key === 'children') {
continue; // Child nodes are handled separately
} else if (key === 'style') {
let styleObj = newProps[key];
for (let attr instyleObj) { dom.style[attr] = styleObj[attr]; }}else{ dom[key] = newProps[key]; }}for(let key in oldProps){
if(! newProps.hasOwnProperty(key)){ dom[key] =null; }}}function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i++) {
letchildVdom = childrenVdom[i]; mount(childVdom, parentDOM); }}const ReactDOM = {
render,
};
export default ReactDOM;
Copy the code