preface
After reading the React source code and Posting about it on CSDN, I’m going to take a look at the react17 source code again recently, because any book or article is different every time I read it, and so is the source code. Take notes for each study at both Denver and Github. (The plan has been updated in about half a month)
Making: github.com/China-forre…
- Read-only of JSX & React elements
/* The display is compiled to createElement, which calls the virtual DOM Render method, which converts the virtual DOM into a real DOM and inserts it into the container. */ /* The react element is immutable. We create new react elements each time we render them. React only updates the necessary parts.Copy the code
let element = {type: 'h1'}
ObjectThe freeze (element)ObjectFreeze () is throughObjectDefineProperty () set writable:falseThe implementation;// Write your own object.freeze ()
Object.defineProperty(Object.'freeze', {
value: function(obj) {
let i;
for(i in obj) {
if(obj.hasOwnproperty(i)) {
Object.defineProperty(obj, i, {
writable: false // Cannot be modified}}})// Not extensible
Object.seal(obj);
}
})
*/
Copy the code
- Implement rendering of native components
Set the environment variable (we’re writing our react, so we need to disable the REact17 JSX converter so we can see createElement, for the following reason) : “Set DISABLE_NEW_JSX_TRANSTORM=true&&react-scripts start” set development mode for packaging (before compression) “start”: “Set NODE_ENV=development&&react-scripts start” We can observe that React17 started a new JSX converter, so we can compile JSX without importing import react from ‘react’
Import {JSX as _jsx} from ‘react/jsx-runtime.js’ : a separate package require(‘jsx-transform’)(‘h1’); In our new version (17), we do not need to set environment variables, because we specify:
Create virtual DOM method react. createElement, implement render method.
JSON. Stringify () of the common usage: www.cnblogs.com/echolun/p/9… Replacer is a replacement
JSON.stringify(obj, replacer, 2);
function replacer(key, value) {
if(key === 'age') {return value + 1;
}else {
returnvalue; }}Copy the code
{
"type": "h1"."key": null."ref": null."props": {
"id": "title"."className": "title"."style": {
"color": "red"
},
"children": [{"type": "span"."key": null."ref": null."props": {
"children": "It worked"
},
"_owner": null."_store": {}},"hello"]},"_owner": null."_store": {}}Copy the code
The realization of the createElement method
function createElement(type, config, children) {
if (config) {
delete config._source;
delete config._self;
}
letprops = { ... config };if (arguments.length > 3) {
children = Array.prototype.slice.call(arguments.2);
}
props.children = children;
return {
type,
props
}
}
const React = { createElement };
export default React;
Copy the code
Implement the react.render () method for mounting
/* Update virtual DOM properties to real DOM; AppendChlid mount the child of the virtual DOM to the container */
function render(vdom, container) {
const dom = createDOM(vdom);
container.appendChild(dom);
}
/* Turn the virtual DOM into the real DOM */
function createDOM(vdom) {
if (typeof vdom === 'string' || typeof vdom === 'number') {
return document.createTextNode(vdom);
}
// The React element is a virtual DOM object.
let { type, props } = vdom;
let dom;
if(typeof type === 'function') {
console.log('vdom', vdom);
console.log('type', type);
return mountFunctionComponent(vdom);
}else {
dom = document.createElement(type);
}
// Update the properties of the newly created real DOM with the properties of the virtual DOM
updateProps(dom, props);
// Handle children here alone
// If there is only one son, and the son is a virtual DOM element
if (typeof props.children === 'string' || typeof props.children === 'number') {
dom.textContent = props.children;
} else if (typeof props.children === 'object' && props.children.type) {
// Insert your son into the real DOM
render(props.children, dom);
} else if (Array.isArray(props.children)) {
reconcileChildren(props.children, dom);
}
else {
document.textContent = props.children ? props.children.toString() : ' ';
}
// Place the real DOM as a DOM attribute in the virtual DOM in preparation for future updates
return dom;
}
// Convert a virtual DOM of type custom function component into a real DOM and return it;
function mountFunctionComponent(vdom) {// The virtual DOM of a custom function component
let {type: FunctionComponent, props} = vdom;
let renderVdom = FunctionComponent(props);
return createDOM(renderVdom);
}
function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i++) {
let childVdom = childrenVdom[i];
render(childVdom, parentDOM)
}
}
function updateProps(dom, newProps) {
for (let key in newProps) {
if (key === 'children') continue;
if (key === 'style') {
let styleObj = newProps.style;
for (let attr instyleObj) { dom.style[attr] = styleObj[attr]; }}else{ dom[key] = newProps[key]; }}}const ReactDOM = { render };
export default ReactDOM;
Copy the code
- Implement rendering of function components
let { type, porps} = vdom;
let dom;
if(typeof vdom === 'function') {
return mountFunctionComponent(vdom)
}else {
dom = document.createElement(type);
}
// Convert a virtual DOM of type custom function component into a real DOM and return it;
function mountFunctionComponent(vdom) {// The virtual DOM of a custom function component
let {type: FunctionComponent, props} = vdom;
let renderVdom = FunctionComponent(props);
return createDOM(renderVdom);
}
Copy the code
- Implement rendering and updating of class components
Github.com/China-forre…
- Batch status updates
We all know about setState in the React class component. It is also known that setState is asynchronous in synthesized events and hook functions, and synchronous in native events. So why is that? How is it done?
In React, events can be updated asynchronously and in batches; After calling setState, the state is not updated immediately. Instead, the state is cached and then batch updated after the event function is finished. Once updated and re-rendered, the component is not refreshed repeatedly, which is also a small point of performance optimization.Copy the code
handlerClick = () = > {
this.setState((lastState) = > ({ number: lastState.number + 1 }), () = > {
console.log("callback1".this.state.number);
});
console.log("this.state.number".this.state.number);
this.setState((lastState) = > ({ number: lastState.number + 1},() = > {
console.log("callback1".this.state.number);
}));
console.log('this.state.number'.this.state.number);
Promise.resolve().then(() = > {
console.log('this.state,number'.this.state.number);
this.setState((lastState) = > ({ number: lastState.number + 1},() = > {
console.log("callback1".this.state.number);
}));
console.log('this.state,number'.this.state.number);
this.setState((lastState) = > ({ number: lastState.number + 1},() = > {
console.log("callback1".this.state.number);
}));
console.log('this.state,number'.this.state.number);
});
}
// this.state.number: 0 0 2 3 4
Callback: 2, 2, 3, 4
Copy the code
-
Compose events and batch updates
-
The life cycle
Components need to be updated regardless of property changes or state changes.
Now functional components don’t need to import React from ‘React’ at the top
Before React17, Babel is translated by react.createElement ("div"); After React17: Because: The new Runtime transformer requires ('react/ jsx-Runtime ')("div") to import the module itself, so that no external variables are required.Copy the code