In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the relevant knowledge of "the core principle and usage of React". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
1. Basic preparation for the project
1.1 create a project
Create a project using the npx create-react-app my_react command
1.2 Project structure
After deleting some unused files, the directory looks like this
Index.js at this time
Import React from 'react'; import ReactDOM from' react-dom'; ReactDOM.render ("sunny", document.getElementById ('root'))
two。 Create react.js and react-dom.js files
We can change the files that need to be introduced into react and react-dom into our own files.
Import React from'. / react'; import ReactDOM from'. / react-dom'; ReactDOM.render ("sunny", document.getElementById ('root'))
3. Complete react-dom
We are in the index.js file
ReactDOM.render ("sunny", document.getElementById ('root'))
Using ReactDOM in this way shows that he has the method of render.
So we can implement react-dom like this.
/ / react-dom.js let ReactDOM = {render} function render (element,container) {container [XSS _ clean] = `$ {element} `} export default ReactDOM
Let's take a look at the running results.
You are to be congratulated! The Great Wall has taken the first step
Okay, now let's put a tag on each element so that we can identify the relationship with other elements by the tag, or we can find the element directly through the tag.
Just like the picture below, can you directly see that the parent node of 0.0 and 0.1 is 0?
/ / react-dom.js let ReactDOM = {render, rootIndex:0} function render (element,container) {container [XSS _ clean] = `$ {element} `} export default ReactDOM
As the code shows, we add a tag data-reactid to each element
Run and find that the mark is really successful, ha.
4. Refactoring render method
Our previous render method
Function render (element,container) {container [XSS _ clean] = `$ {element} `}
The element passed in by default is a string, but the reality is that it may be a text node, a DOM node, or a custom component. So we implement a createUnit method that passes in element to determine what type of node element is. It then returns an object that is judged to be of a certain type and the corresponding methods and properties are added. For example, if our element is of type string, then we return an object of type string, and the object itself has an element property and a getMarkUp method, this getMarkUp method, which converts element into a real dom. In fact, you can simply think that the createUnit method is to add a getMarkUp method to the element object.
/ / react-dom.js import $from "jquery" let ReactDOM = {render, rootIndex:0} function render (element,container) {let unit = createUnit (element) let markUp = unit.getMarkUp (); / / to return the HTML tag $(container) .html (markUp)} export default ReactDOM
As shown in the code, pass element into the createUnit method, and the resulting unit is an object
{_ currentElement:element, getMarkUp () {...}}
Then execute the getMarkUp method of unit to get the real dom, and then you can mount it to container!
Note that if the element passed into render is the string "sunny", that is
Import React from'. / react'; import ReactDOM from'. / react-dom'; ReactDOM.render ("sunny", document.getElementById ('root'))
That is to say, the element passed into createUnit is the string "sunny", then the returned unit is
{_ currentElement: "sunny", getMarkUp () {}}
Then how to write this createUnit?
5. Implement the createUnit method
We create a new file called unit.js
Insert a picture description here
/ / Unit.js class Unit {} class TextUnit extends Unit {} function createUnit (element) {if (typeof element = 'string' | | typeof element = "number") {return new TextUnit (element)}} export {createUnit}
As shown in the code, createUnit new a TextUnit object when createUnit determines that element is a string, and then returns it, which is the unit object we talked about above.
Why should TextUnit inherit from Unit?
This is because element in addition to strings, may also be native tags, columns such as div,span, etc., may also be our custom components, so we first wrote a unit class, which implements the properties common to these element. Then specific classes, such as TextUnit, directly inherit Unit, and then implement their own properties.
6. Implement Unitnew
The object that Unit gets should be like this
{_ currentElement:element, getMarkUp () {...}}
That is, this is a property that all categories have, so we can implement Unit in this way
Class Unit {constructor (element) {this._currentElement = element} getMarkUp () {throw Error ("this method should be overridden and cannot be used directly")}}
Why should getMarkUp throw Error ("this method should be overridden and cannot be used directly")?
Students who have learned java or other languages should understand it in a second, because getMarkUp wants to be a method rewritten by quilt subclasses, because the results returned by each subclass when executing this method are different.
7. Implement TextUnit
At this point, all we have to do is rewrite the getMarkUp method, but don't forget to add a reactid to each element. As for why, it has already been mentioned above, and there is also a big picture.
Class TextUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid return `${this._currentElement}`}}
All right, let's take a look at what a complete Unit.js looks like.
/ / Unit.js class Unit {constructor (element) {this._currentElement = element} getMarkUp () {throw Error ("this method should be overridden Cannot be used directly ")} class TextUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid return `${this._currentElement}`} function createUnit (element) {if (typeof element = 'string' | | typeof element = =" number ") {return new TextUnit (element)}} export {createUnit}
Under the introduction of unit test by index.js
/ / index.js
Import React from'. / react'
Import ReactDOM from'. / react-dom'
ReactDOM.render (
"sunny"
Document.getElementById ('root')
);
/ / react-dom.js import {createUnit} from'. / unit' import $from "jquery" let ReactDOM = {render, rootIndex:0} function render (element,container) {let unit = createUnit (element) let markUp = unit.getMarkUp (ReactDOM.rootIndex); / / to return the HTML tag $(container) .html (markUp)} export default ReactDOM
Insert a picture description here
An unexpected success! Ha ha ha
8. Understanding the React.creacteElement method
When I first learned react, I always had a lot of questions with me. For example, when you look at the following code, you will think: why do we just introduce React, but we don't obviously see that we use it elsewhere? then I wonder, since it's not used, will it be affected after deletion? The answer is, of course, no.
Import React from 'react'; import ReactDOM from' react-dom'; let element = (hello world) console.log ({type: element.type, props:element.props}) ReactDOM.render (element,document.getElementById ('root'))
When we study with this question, we will find that React.createElement () is actually adjusted when rendering element, so the answer to the above question is found here.
As shown in the following code, this is the transformation from jsx syntax to React.createElement
The code above hello world / / is simple, but we all know that react is the so-called virtual dom, which is certainly not what we see. When we translate the above code through babel, we look at React.createElement ("H2", {id: "title", className: "bg", style: {color: 'red'}}, "hello", React.createElement ("span", null, "world"))
Document has a createElement () method, and React also has a createElement () method. Let's introduce the createElement () method of React.
Var reactElement = ReactElement.createElement (. / / tag name string / ReactClass,. / / [attribute values of elements to objects],. / / [child nodes of elements])
1. Parameters:
1) the first parameter: either a html tag name string or a ReactClass (required)
2) second parameter: attribute values of the element to the object (optional). These attributes can be called through this.props.*
3) start with the third parameter: the child node of the element (optional).
2. Return value:
A ReactElement element of a given type
We can change our index.js.
/ / index.js import React from'. / react'; import ReactDOM from'. / react-dom'; var li1 = React.createElement ('li', {onClick: () = > {alert ("click")}},' First'); var li2 = React.createElement ('li', {},' Second'); var li3 = React.createElement ('li', {},' Third'); var ul = React.createElement ('ul', {className:' list'}, li1, li2, li3) Console.log (ul); ReactDOM.render (ul,document.getElementById ('root'))
You can just take a look at the final printing result of ul.
So, all we know is that the ReactElement.createElement method will produce a ReactElement element of a given type, then this object is passed into the render method, and then the createUnit and getMarkUp operations mentioned above are performed.
9. Implement the React.createElement method
After the above explanation, we probably already know the function of the React.createElement method, so let's take a look at how it is implemented.
We created a new file, element.js.
/ / element.js class Element {constructor (type,props) {this.type = type this.props = props}} function createElement (type,props= {},... children) {props.children = children | | []; return new Element (type,props)} export {Element, createElement}
We defined an Element class, then created an object of this class in the createElement method, and return went out
Yes, this object is the ReactElement element of the given type mentioned above, which is shown in the following figure.
This is how we should React.createElement () call this method, so we will mount the method to react.
We haven't implemented react.js yet.
In fact, it is very simple to return a React object, which has a createElement method
/ / react.js import {createElement} from ". / element" const React = {createElement} export default React
10. Implement NativeUnit
After createElement returns a ReactElement element of a given type, the above implementation passes the change element into the render method, so it goes through the createUnit method and the createUnit method to determine what type of element it belongs to, as shown in the following code
/ / Unit.js import {Element} from ". / element" / / the new code class Unit {constructor (element) {this._currentElement = element} getMarkUp () {throw Error ("this method should be overridden Cannot be used directly ")} class TextUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid return `${this._currentElement}`} function createUnit (element) {if (typeof element = 'string' | | typeof element = =" number ") {return new TextUnit (element)} / / New Code if (element instanceof Element & & typeof element.type =" String ") {return new NativeUnit (element)}} export {createUnit}
Okay, now let's implement the NativeUnit class, which is mainly to implement the getMarkUp method of NativeUnit.
Class NativeUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid let {type,props} = this._currentElement;}}
To be clear, the getMarkUp method of NativeUnit is to set the
Such an element object is converted to a real dom
Therefore, we can improve the getMarkUp method in this way.
Class NativeUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid let {type,props} = this._currentElement; let tagStart = ``- ${m.toLowerCase ()} `)}: ${value}`;}) .join (' ') tagStart + = (`style= "${styles}" `)} else if (propName =' className') {/ / if it is a class name} else if (propName = 'children') {/ / if it is a child element} else {/ / other custom attributes For example, reactid}} return tagStart+' >'+ childString + tagEnd}}
This is actually the place
{style: {backgroundColor: "red"}}
Take out the object property of style in the object
Then the backgroundColor is changed into background-color by regularization.
And then splice it into tagStart.
Next, we will implement className, and it is too easy to find this.
Class NativeUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid let {type, props} = this._currentElement; let tagStart = `{componentInstance.componentDidMount & & componentInstance.componentDidMount ()}) return renderUnit.getMarkUp (this._reactid)}}
Then the mounted event is triggered after the dom of the component is mounted on the page
/ / react-dom.js import {createUnit} from'. / unit' import $from "jquery" let ReactDOM = {render, rootIndex:0} function render (element,container) {let unit = createUnit (element) let markUp = unit.getMarkUp (ReactDOM.rootIndex); / / to return the HTML tag $(container) .html (markUp) $(document) .trigger ("mounted")} export default ReactDOM
From this dependence, it is realized that the componentDidMount life cycle function, ha.
Test it, did you succeed or not?
Ah, as always successful, you may be curious to ask me why every test is successful, that is because I debugged it to be successful even if it is not successful.
To implement the setState function below, let's modify the getMarkUp method of CompositeUnit.
Class CompositeUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid let {type:Component,props} = this._currentElement / / actually, in the example type = Counter let componentInstance = this._componentInstance = new Component (props) / / Save the instance object to the current unit componentInstance._currentUnit = this / / hang the unit to the instance componentInstance componentInstance.componentWillMount & & componentInstance.componentWillMount () let renderElement = componentInstance.render (); let renderUnit = this._renderUnit = createUnit (renderElement) / / Mount the rendered content object to the current unit $_ (document) .on ("mounted", () = > {componentInstance.componentDidMount & & componentInstance.componentDidMount ()}) return renderUnit.getMarkUp (this._reactid)}
We added for this instance of CompositeUnit
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
_ componentInstance: an instance is used to represent the current component (the Counter component we wrote)
_ renderUnit: the unit._currentElement corresponding to the react element returned by the render method of the current component
In addition, we also passed
ComponentInstance._currentUnit = this / / attach unit to instance componentInstance
The current unit is mounted to the component instance componentInstance.
The instance of the visible component saves the current unit, and the current unit also saves the component instance
14. Implement setState
Let's take a look at the following example, number+1 every other second
/ / index.js import React from'. / react'; import ReactDOM from'. / react-dom'; import $from 'jquery' class Counter extends React.Component {constructor (props) {super (props) this.state = {number:0};} componentWillMount () {console.log ("Hello Sunshine, I'm componentWillMount"); $_ (document) .on ("mounted", () = > {console.log })} componentDidMount () {setInterval (() = > {this.setState ({number:this.state.number+1})}, 1000)} render () {return this.state.number}} let element = React.createElement (Counter, {name: "timer"}) ReactDOM.render (element,document.getElementById ('root'))
As mentioned earlier, the setState method inherits from the Component component. So we add a setState method to the Component component
/ / component.js class Component {constructor (props) {this.props = props} setState (partialState) {/ / the first parameter is the new element, and the second parameter is the new state this._currentUnit.update (null,partialState)}} export {Component}
We found that the update method of the corresponding unit of the current instance was called in the setState method, which passed in part of the value of state.
Seeing here, we know that we need to go back to the CompositeUnit class and add a update method.
Class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / New property object let nextProps = this._currentElement.props} getMarkUp (reactid) {.}}
We first changed the value of _ currentElement. Why is there a situation with or without nextElement?
(mainly because, if _ currentElement is a string or number, then it needs to pass nextElement to replace the old _ currentElement. If it is not a string or a number, it does not need to be passed. And CompositeUnit must be a component, so you don't have to pass nextElement.
Next, we get the latest state and update the component's state with the following code
Let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState)
Getting the latest props is not the same as getting state. Props is bound to _ currentElement, so the latest props is obtained through
Let nextProps = this._currentElement.props
Next, we need to get the old and new rendering elements first, and then compare them. How do we get them?
Class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / the new attribute object let nextProps = this._currentElement.props / / to compare and update / / get the last rendered unit let preRenderedUnitInstance = this._renderUnit first / / through the last rendered unit, get the last rendered element let preRenderElement = preRenderedUnitInstance._currentElement / / get the latest rendering element let nextRenderElement = this._componentInstance.render ()} getMarkUp (reactid) {
We first get the last rendered unit, and then get the last rendered element preRenderElement through the last rendered unit
Then use this._componentInstance.render () to get the next rendered element nextRenderElement.
Then you can compare the two elements.
We will first judge whether or not to make an in-depth comparison.
It's very simple if you don't make a depth comparison.
Get the new rendering unit directly, then get the dom to be rendered through getMarkUp, and then replace the dom element in the current component
Class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / the new attribute object let nextProps = this._currentElement.props / / to compare and update / / get the last rendered unit let preRenderedUnitInstance = this._renderUnit first / / through the last rendered unit, get the last rendered element let preRenderElement = preRenderedUnitInstance._currentElement / / get the latest rendering element let nextRenderElement = this._componentInstance.render () / / if the new and old element types are the same, you can compare the depth. If not, kill the old element directly. Create a new if (shouldDeepCompare (preRenderElement,nextRenderElement)) {} else {this._renderUnit = createUnit (nextRenderElement) let nextMarkUp = this._renderUnit.getMarkUp (this._reactid) $(`[data-reactid= "${this._reactid}"]`) .replacewith (nextMarkUp)}} getMarkUp (reactid) {}}
Let's simply write the shouldDeepCompare method and directly return false to test whether the non-depth comparison can be executed correctly.
Function shouldDeepCompare () {return false} class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / the new attribute object let nextProps = this._currentElement.props / / to compare and update / / get the last rendered unit let preRenderedUnitInstance = this._renderUnit first / / through the last rendered unit, get the last rendered element let preRenderElement = preRenderedUnitInstance._currentElement / / get the latest rendering element let nextRenderElement = this._componentInstance.render () / / if the new and old element types are the same, you can compare the depth. If not, kill the old element directly. Create a new if (shouldDeepCompare (preRenderElement,nextRenderElement)) {} else {this._renderUnit = createUnit (nextRenderElement) let nextMarkUp = this._renderUnit.getMarkUp (this._reactid) $(`[data-reactid= "${this._reactid}"]`) .replacewith (nextMarkUp)}} getMarkUp (reactid) {}}
Insert a picture description here
It turned out to be a success.
What if an in-depth comparison can be made?
Class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / the new attribute object let nextProps = this._currentElement.props / / to compare and update / / get the last rendered unit let preRenderedUnitInstance = this._renderUnit first / / through the last rendered unit, get the last rendered element let preRenderElement = preRenderedUnitInstance._currentElement / / get the latest rendering element let nextRenderElement = this._componentInstance.render () / / if the new and old element types are the same, you can compare the depth. If not, kill the old element directly. Create a new if (shouldDeepCompare (preRenderElement,nextRenderElement)) {/ / if depth comparison can be made Send the updated nextRenderElement to preRenderedUnitInstance.update (nextRenderElement)} else {this._renderUnit = createUnit (nextRenderElement) let nextMarkUp = this._renderUnit.getMarkUp (this._reactid) $(`[data-reactid= "${this._reactid}"]`) .replaceWith (nextMarkUp)} getMarkUp (reactid) {}}
If it can be deep, execute it.
PreRenderedUnitInstance.update (nextRenderElement)
What does this mean?
If we are currently performing a rendering Counter, what is preRenderedUnitInstance?
That's right! It is obtained by the Counter component executing the render method and then executing the createUnit
Insert a picture description here
The unit of this string
And then called the update method of this unit
Note that the unit here is the unit of the string, that is, TextUnit
So we need to implement the update method of TextUnit
Class TextUnit extends Unit {getMarkUp (reactid) {this._reactid = reactid return `${this._currentElement}`} update (nextElement) {debugger if (this._currentElement! = = nextElement) {this._currentElement = nextElement $(`[data-reactid= "${this._reactid}"]`) .html (nextElement)}
The update method of TextUnit is very simple. First, determine whether the rendering content has changed, and if so, replace the content of the dot string.
And replace the current unit's _ currentElement with the latest nextElement
Let's simply change shouldDeepCompare to return true and test the depth comparison.
Function shouldDeepCompare () {return true}
As successful as ever
15. Implement the shouldComponentUpdate method
We know that there is a shouldComponentUpdate that determines whether or not to re-render the component's
ShouldComponentUpdate (nextProps, nextState) {return nextState.someData! = = this.state.someData}
Obviously, it asks us to pass in two parameters, nextProps and nextState after the component is updated
And again, in the process of implementing update, we have already got nextState and nextProps.
Class CompositeUnit extends Unit {update (nextElement,partialState) {. / / get the new state and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState); / / the new property object let nextProps = this._currentElement.props / / will be compared and updated. } getMarkUp (reactid) {
So, we can execute the shouldComponentUpdate method in update to determine whether or not to re-render the component
Class CompositeUnit extends Unit {update (nextElement,partialState) {/ / update currentElement to the new element this._currentElement = nextElement | | this._currentElement; / / get the new status and update the component's state let nextState = this._componentInstance.state = Object.assign (this._componentInstance.state,partialState) / / the new property object let nextProps = this._currentElement.props if (this._componentInstance.shouldComponentUpdate & &! this._componentInstance.shouldComponentUpdate (nextProps,nextState)) {return;} / / to compare and update / / get the last rendered unit let preRenderedUnitInstance = this._renderUnit first / / through the last rendered unit, get the last rendered element let preRenderElement = preRenderedUnitInstance._currentElement / / get the latest rendering element let nextRenderElement = this._componentInstance.render () / / if the new and old element types are the same, you can compare the depth. If not, kill the old element directly. Create a new if (shouldDeepCompare (preRenderElement,nextRenderElement)) {/ / if depth comparison can be made Leave the update work to the unit corresponding to the Element element rendered last time to handle preRenderedUnitInstance.update (nextRenderElement)} else {this._renderUnit = createUnit (nextRenderElement) let nextMarkUp = this._renderUnit.getMarkUp (this._reactid) $(`[data-reactid= "${this._reactid}"]`) .replaceWith (nextMarkUp)} } getMarkUp (reactid) {}
16. Implement the componentDidUpdate lifecycle function
So Easy .
Just trigger the event after the update
Class CompositeUnit extends Unit {update (nextElement,partialState) {if (this._componentInstance.shouldComponentUpdate & &! this._componentInstance.shouldComponentUpdate (nextProps,nextState)) {return } if (shouldDeepCompare (preRenderElement,nextRenderElement)) {/ / if depth comparison can be made Leave the update to the unit corresponding to the Element element rendered last time to handle preRenderedUnitInstance.update (nextRenderElement) this._componentInstance.componentDidUpdate & & this._componentInstance.componentDidUpdate ()} else {this._renderUnit = createUnit (nextRenderElement) let nextMarkUp = this._renderUnit.getMarkUp (this._reactid) $(`[data-reactid= ") ${this._reactid} "] `) .replaceWith (nextMarkUp)}} getMarkUp (reactid) {}
17. Implement shouDeepCompare
It is extremely simple to judge whether a deep comparison is needed. You only need to judge whether oldElement and newElement are both strings or numbers, and this type of deep comparison is necessary.
Then determine whether oldElement and newElement are both Element types. If not, return false. Yes, then determine whether type is the same (that is, determine whether it is the same component, if yes, return true).
All other cases are return false
Function shouldDeepCompare (oldElement NewElement) {if (oldElement! = null & & newElement! = null) {let oldType = typeof oldElement let newType = typeof newElement if ((oldType = 'string' | | oldType = "number") & & (newType = "string" | | newType = "number")) {return true} if (oldElement instanceof Element & newElement instanceof Element) {return oldElement.type = newElement.type }} this is the end of return false's "Core principles and usage of React". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.