React (step 2 ✌️)
Implement the mount of custom components
If you’ve used React, you’ll notice that we use a lot of custom components in React. We don’t currently support rendering custom tags in The Pauper version of React
// main.js
class MyComponent {
constructor() {
}
} const customComponent = <MyComponent /> // Be sure to use the customComponent or webPack will declare only unused variables when packaging console.log(customComponent); Copy the code
Not surprisingly, we can see that our MyComponent type is created with createElement and passes a function object, which is definitely not supported by the native API.
So how do we do that? “Think about it. When we did React, did all custom classes inherit from react.component?”
So let’s go with the idea and write a base Component class that is recognized by createElement, and then all our custom Components inherit from it.
Our next task is to “encapsulate a Component class that can be rendered to a native page.”
- Props: As a rule of thumb, each Component can accept an argument to props
- Children: Can accept child components as children
- AppendChild: Adds children to the root node
- Root: root node (real DOM)
- There is a Render method to create it“Virtual tree“The render method essentially calls the React.createElement method.
plugin-transform-react-jsx
Helped us do the work)
“So!! We’ll also need to change our createElement method to return a real DOM. This doesn’t match the React design principle. We’ll need to make createElement return a virtual DOM“
Methods: CreateElement and Document. createTextNode are called from the native Web API, stored in root variables. CreateElement returns only the instance of the enclosing class. To get the real DOM, we need to call root.
Create a new my-react.js file and put the logic of react.componentin a separate file
my-react
│
│
└ ─ ─ ─ dist│ │ main. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ the SRC│ │ main. Js│ │ my - react. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ webpack. Config. JsCopy the code
my-react.js
// my-react.js
export let React = {
createElement: (tagName, attributes, ... children) = > {
let ele;
if (typeof tagName === 'string') { // How to create a native tag ele = new ElementWrapper(tagName, attributes); } else { ele = new tagName(attributes); } // Since our children are createElement logic, it is also a virtual DOM. Change child to vChild children.forEach(vchild= > { if (vchild === null) { return; } if (typeof vchild === "string") { vchild = new TextWrapper(vchild); } ele.appendChild(vchild); }) return ele; }, }; class ElementWrapper { constructor(type, attributes) { this.root = document.createElement(type); Object.keys(attributes || {}).forEach(key= > { if (key.match(/^on/)) { let eventType = key.replace(/^on/.' ').toLocaleLowerCase(); this.root.addEventListener(eventType, attributes[key]); return } this.root.setAttribute(key, attributes[key]); }); } appendChild(vchild) { // appendChild essentially operates on the real DOM, so take the real DOM of the instance returned by createElment this.root.appendChild(vchild.root); } } class TextWrapper { constructor(content) { this.root = document.createTextNode(content); } } class Component { constructor(props) { this.props = props; this.children = []; } // Since we need to get the real DOM when rendering, we need a property to get root get root() { // Get the rendered virtual DOM and then the real DOM return this.render().root; } } React.Component = Component; Copy the code
Test our React in main.js
// main.js
import { React } from "./my-react";
class MyComponent extends React.Component {
constructor() {}
render() { return ( <div id="id" style="background: red"> <span onClick={()= > { console.log("add event success!" ); }} > zaoren1 </span> <span>zaoren2</span> </div> ); } } const customComponent = <MyComponent />; document.body.appendChild(customComponent.root); Copy the code
Now that we’re ready to load our custom components, we’ll create a new react-dom.js file to write the render method in order to use it more closely to our React
my-react
│
│
└ ─ ─ ─ dist│ │ main. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ the SRC│ │ main. Js│ │ my - react. Js│ │ react - dom. Js└ ─ ─ ─ package. Json| └ ─ ─ ─ webpack. Config. JsCopy the code
react-dom.js
// react-dom.js
export const render = function(vElement, parentDOM) {
parentDOM.appendChild(vElement.root);
}
Copy the code
main.js
// main.js
+ import { render } from "./react-dom";
.+ render(<MyComponent />, document.body);
- document.body.appendChild(customComponent.root);
Copy the code
Implement setState to update the component state
// my-react.js
class Component {
constructor(props) {
this.props = props;
this.children = [];
this._root = null; } setState(state) { this.state = state; let oldRoot = this._root; if (oldRoot && oldRoot.parentNode) { oldRoot.parentNode.replaceChild(this.root, oldRoot); } } // Since we need to get the real DOM when rendering, we need a property to get root get root() { // Get the rendered virtual DOM and then the real DOM // Save a copy of root in _root every time you get root. return this._root = this.render().root; } } Copy the code
// main.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'zaoren1' } } render() { const { name } = this.state; return ( <div id="id" style="background: red"> <span onClick={()= > { this.setState({ name: 'setState success! ' }) }} > {name} </span> <span>zaoren2</span> </div> ); } } Copy the code
Then click on our zaoren1 and the content becomes setState Success!
Also, we can see that setState in React simply calls replaceChild to replace a child node under a parent node without re-rendering the entire DOM.
Project source code
This article is formatted using MDNICE