preface
Let's not talk about the syntax principle, but first forcibly simulate the syntax according to the API effect to realize a simple version of React
The following code has nothing to do with the React source code, just a simple simulation idea
render
The first step is to create an element return with the class and bind the click event. The code is as follows. You can normally see that a button appears
class AddButton { // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // Output instance render() { this.dom = this.createDOM(`<button>0</button>`); this.dom.addEventListener("click", () => console.log("click"), false); return this.dom; } } // Insert page document.body.appendChild(new AddButton().render());
state && setState
Implement class state and modify state method
class AddButton { // Built in status constructor() { this.state = { num: 0 }; } // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // Update view with unique modification status setState(state) { this.state = state; this.dom = this.render(); } // Modify data handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } // Output instance render() { this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`); this.dom.addEventListener("click", () => this.handleAdd(), false); return this.dom; } } // Insert page document.body.appendChild(new AddButton().render());
After rendering, you see this The DOM output has been found to have changed, but the view is not rendered because it is a one-time insertion at the end. Let's move on to the render view
Re render
We now built the operation of inserting data into the class and added a new method to insert new elements and remove old elements
class AddButton { // Built in status constructor() { this.state = { num: 0 }; } // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // It's just a way to replace elements changeDom() { const oDom = this.dom; this.dom = this.render(); document.body.insertBefore(this.dom, oDom); document.body.removeChild(oDom); } // Update view with unique modification status setState(state) { this.state = state; this.changeDom(); } // Modify data handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } // Output instance render() { this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`); this.dom.addEventListener("click", () => this.handleAdd(), false); return this.dom; } } // Insert page document.body.appendChild(new AddButton().render());
Now, although the effect is realized, you still have to manually insert the elements into the view at the beginning
Extract public classes
We first extract some common methods into a separate class, and complete the parameters passed by props attribute
class React { // Built in status constructor(props = {}) { // example this.wrapper = null // state this.state = {} // attribute this.props = {} } // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // It's just a way to replace elements changeDom() { const oDom = this.dom; this.dom = this._render(); this.wrapper.insertBefore(this.dom, oDom); this.wrapper.removeChild(oDom); } // Update view with unique modification status setState(state) { this.state = state; this.changeDom(); } // Output instance _render(wrapper) { if (wrapper) this.wrapper = wrapper; this.dom = this.createDOM(this.render()); this.dom.addEventListener("click", () => console.log('Add custom event'), false); return this.dom; } }
Then, the Component only needs to directly inherit the Component and process its own logic
class AddButton extends React { constructor() { super(); this.state = { num: 0 }; } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { return `<button id="btn">${this.state.num}</button>`; } }
Several things have been done above:
- Extract common logic to React class
- Custom component AddButton
Another problem is whether the click event is temporarily coupled into the Component, which will be implemented in the next chapter
Add custom event
Because events are customized by components, our idea is to bind events with generic classes after component instances are defined
class React { // Built in status constructor(props = {}) { // example this.wrapper = null // state this.state = {} // attribute this.props = {} // event this.event = {} } // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // It's just a way to replace elements changeDom() { const oDom = this.dom; this.dom = this._render(); this.wrapper.insertBefore(this.dom, oDom); this.wrapper.removeChild(oDom); } // Update view with unique modification status setState(state) { this.state = state; this.changeDom(); } // Initialization event initEvent() { const events = Object.keys(this.event) events.forEach(key => { this.dom.addEventListener(key, this.event[key].bind(this), false); }) } // Output instance _render(wrapper) { if (wrapper) this.wrapper = wrapper; this.dom = this.createDOM(this.render()); // An instance needs to be created before it can be initialized this.dom && this.initEvent() return this.dom; } }
At the same time, the component code also needs to be modified accordingly
class AddButton extends React { constructor() { super(); this.state = { num: 0 }; this.event = { click: this.handleAdd } } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { return `<button id="btn">${this.state.num}</button>`; } }
ReactDom.render
As we all know, React will provide such a method to insert components into a specified element. Let's simulate it directly
const ReactDom = { // Inserts a component instance into the specified element render(component, wrapper) { wrapper.appendChild(component._render(wrapper)); } };
Final run code
We have implemented several functions above:
- Responsible for creating the React class of elements, including one-way data flow, custom status, events, replacement elements, etc
- ReactDom class responsible for mounting
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <div id="root"></div> <script> class React { // Built in status constructor(props = {}) { // example this.wrapper = null // state this.state = {} // attribute this.props = {} // event this.event = {} } // Create Dom createDOM(domString) { const div = document.createElement("div"); div.innerHTML = domString; return div; } // It's just a way to replace elements changeDom() { const oDom = this.dom; this.dom = this._render(); this.wrapper.insertBefore(this.dom, oDom); this.wrapper.removeChild(oDom); } // Update view with unique modification status setState(state) { this.state = state; this.changeDom(); } // Initialization event initEvent() { const events = Object.keys(this.event) events.forEach(key => { this.dom.addEventListener(key, this.event[key].bind(this), false); }) } // Output instance _render(wrapper) { if (wrapper) this.wrapper = wrapper; this.dom = this.createDOM(this.render()); // An instance needs to be created before it can be initialized this.dom && this.initEvent() return this.dom; } } const ReactDom = { // Inserts a component instance into the specified element render(component, wrapper) { wrapper.appendChild(component._render(wrapper)); } }; class AddButton extends React { constructor() { super(); this.state = { num: 0 }; this.event = { click: this.handleAdd } } handleAdd() { const num = this.state.num + 1; this.setState({ num: num }); } render() { return `<button id="btn">${this.state.num}</button>`; } } ReactDom.render(new AddButton(), document.getElementById("root")); </script> </body> </html>
So far, leaving aside the practical ideas, we have simply simulated the basic syntax implementation of React