A Virtual DOM

1.1 What is the Virtual DOM

In JavaScript, the virtual DOM is an Object and contains at least three attributes: tag, props, and children. (Different framework names may vary.)

<div id ="app"> <p class="text">hello stoney! </p> </div>Copy the code

The above HTML is converted to the virtual DOM as follows:

{
  tag: 'div',
  props: {
    id: 'app'
  },
  children: [
    {
      tag: 'p',
      props: {
        className: 'text'
      },
      children: [
        'hello stoney!'
      ]
    }
  ]
}
Copy the code

1.2 Implementation principle of virtual DOM

  • Essentially a JavaScript object, an abstraction of a real DOM tree
  • When the state changes, record the difference between the old and new trees
  • Finally, the differences are updated into the actual DOM tree;

Two diff algorithm

2.1 Traditional DIFF algorithm

The time complexity of the traditional DIff algorithm is O(n^3), which will compare nodes of two trees in pairs (O(n^2), and also need to edit (O(n)), so the complexity is O(n^3);

2.2 React Diff Mechanism

React optimizes the DIff algorithm based on the following three strategies

  • The movement of DOM nodes across hierarchies in the WEB UI is minimal and can be ignored. (tree diff)
  • Two components with the same class will generate similar tree structures, and two components with different classes will generate different tree structures. (component diff)
  • For a set of child nodes at the same level, they can be distinguished by unique ids. (element diff)

Tree diff(if a dom node has cross-level operations: only create and delete nodes)

  1. React uses updateDepth to level the Virtual DOM tree.
  2. For hierarchical comparison of trees, two trees only compare nodes at the same level. If the node does not exist, the node and its children will be completely deleted and no further comparison will be made.
  3. The comparison of the entire DOM tree can be completed by traversing it once;

Component diff: React There are three strategies for comparing different components:

  1. Two components of the same type can continue to compare the Virtual DOM tree according to the original strategy (hierarchy comparison).
  2. If two components of the same type change from component A to component B, it is possible that the Virtual DOM has not changed at all. If you know this, you can save A lot of computing time. ShouldComponentUpdate () to determine if this component needs to be diff;
  3. Different types of components, one component(to be changed) is judged as dirty component, thus replacing all nodes of the entire component; Note: If component A and component B have similar structures, but React determines that they are different types of components, the structure is not compared. Instead, component A and its children are deleted and component B and its children are created.

Element DIff Provides three node operations when nodes are at the same level: delete, insert, and move.

2.3 Reduce diFF loss

Based on the tree diff

Instead of moving nodes across hierarchically, for conditional rendering of multiple child nodes, nodes can be hidden or shown using CSS instead of moving or adding nodes.

Based on the component diff

ShouldComponentUpdate should be used to reduce unnecessary updates of components. Similar structures should be encapsulated as components as possible.

Based on the element diff

For list structures, you should avoid moving the last node to the head of the list

Three higher-order components

3.1 Definition of higher-order components

Higher-order Components is a function that takes a component as an argument and returns a new component;

function visible(WrappedComponent) {
  return class extends Component {
    render() {
      const { visible, ...props } = this.props;
      if (visible === false) return null;
      return <WrappedComponent {...props} />;
    }
  }
}
Copy the code

Higher-order components can solve the following problems:

  • Extracting repetitive code to realize component reuse;
  • Conditional rendering, controlling the rendering logic of components (render hijacking), common scenarios: permission control
  • Capture/hijack the life cycle of the processing component;

3.2 Implementation Method

3.2.1 Attribute Proxy

This method returns the component to be wrapped in Render, so we can proxy all the props passed in and decide how to render it. The higher-order component generated in this way is the parent of the component passed in. The following code implements the property broker approach;

function proxyHOC(WrappedComponent) { return class extends Component { render() { return <WrappedComponent {... this.props} />; }}}Copy the code

This method can achieve:

  • Operates on all incoming props
  • The life cycle of an operational component
  • Static methods for manipulable components
  • Available refs

3.2.2 Reverse Inheritance

The returned new component inherits the original component and calls render of the original component in Render. This can be used to access the lifecycle, props, state, render, and so on of the original component.

function inheritHOC(WrappedComponent) { return class extends WrappedComponent { render() { return super.render(); }}}Copy the code

This method can achieve:

  • Operates on all incoming props
  • The life cycle of an operational component
  • Static methods for manipulable components
  • Available refs
  • Operational state
  • Can render hijack

3.3 Use of higher-order components

compose

logger(visible(style(Input)))
Copy the code

You can manually encapsulate a function composition tool

const compose = (... fns) => fns.reduce((f, g) => (... args) => g(f(... args))); compose(logger,visible,style)(Input);Copy the code

Decorators

@logger
@visible
@style
class Input extends Component {
  // ...
}
Copy the code

3.4 Example

The following code implements rendering hijacking through reverse inheritance

export default function pageInit(SourceComponent) {
  return class HOC extends SourceComponent {
    render() {
      if (client_env === 'PC') {
         return super.renderPC()      
       }      
       return super.render();    
     }  
  };
}
Copy the code

Obtain data from the React

4.1 Obtaining data during the life cycle

ComponentDidMount () for the first rendering and componentDidUpdate() for props update;

Get data from 4.2 Hooks

4.3 Getting data in Suspense

Suspense provides a declarative way to get data in React asynchronously, wrapping components that operate asynchronously

import React, { Suspense } from "react"; import EmployeesList from "./EmployeesList"; function EmployeesPage({ resource }) { return ( <Suspense fallback={<h1>Fetching employees.... </h1>}> <EmployeesFetch resource={resource} /> </Suspense> ); } function EmployeesFetch({ resource }) { const employees = resource.employees.read(); return <EmployeesList employees={employees} />; }Copy the code

Suspense will show the content in Fallback when fetching data. After fetching data, Suspense will render the components in the retrieved data. Suspense handles asynchronous operations in a declarative and synchronous manner. No complex data retrieval logic in components, no lifecycle in components, no Hooks in components;

Optimizations in React

5.1 PureComponent, React. Memo

React causes the child to rerender if the parent component is updated, even if all props passed by the parent component to the child component are not modified. From the standpoint of the React declarative design, the props and state of the child components are unchanged, and the dom structure and side effects generated by them should not change. When a child component conforms to the declarative design concept, the render process of the component can be ignored. PureComponent is a shallow comparison of props and state for a class component, and React.memo is a shallow comparison of props for a function component. But use it with immutable values;

5.2 shouldComponentUpdate

ShouldComponentUpdate, this lifecycle checks whether render needs to be executed by checking whether props and state are changing;

If we want to add an item to an array and use state.push(item) instead of const newState=[…state, item], we need to compare props in shouldComponentUpdate. The render process of the component is executed only if the props are modified. But with the popularity of data immutability and functional components, there are fewer such scenarios;

In the second case, if you pass a large object as props to a child component during development (some properties are not needed). When a property in a large object that is not used by a child component is updated, the child component also refires Render. In this case, shouldComponentUpdate only returns true if the attribute value used by the child component changes, preventing the child component from rerendering. But this scenario has two drawbacks:

  • If there are many descendant components, it will increase a lot of work to find out the attributes used by all descendant components, and it is easy to miss and cause bugs. During the development process, it is best to only pass the required attributes, and avoid using {… Props} to pass;

  • ShouldComponentUpdate only compares data.a and data. B, so far there is no problem. Then the developer uses data. C in the C component, assuming that data.a and data. C are updated together, there is no problem. ShouldComponentUpdate (shouldComponentUpdate); shouldComponentUpdate (shouldComponentUpdate); shouldComponentUpdate (shouldComponentUpdate); shouldComponentUpdate (shouldComponentUpdate); shouldComponentUpdate (shouldComponentUpdate);

    < A data = "{data}" > {/ * B component using only the data in A and data. The B * /} < B data = "{data}" > {/ * C components using only the data in A * /} < C data = "{data}" > < / C > < / B > </A>Copy the code

5.3 Properly Remove Molecular Components

The React based workflow causes all child components to be updated whenever the parent component is updated. ShouldComponentUpdate and PureComponent, React. Memo can be used to avoid rerendering, though. However, it is necessary to reasonably disassemble components and allocate some independent data changes to sub-components, so that the refresh caused by component data changes will not cause the refresh of other components.

5.4 The correct way to write event binding

1. Bind this to the constructor (also officially recommended)

export default class Test extends Component { constructor(props){ super(props) this.handleClick = Handleclick. bind(this)} handleClick(){console.log(' click ',this)} render(){return (<div onClick={ this.handleClick }>click btn</div> ) } }Copy the code

2, Property initializer syntax (class Fields) bind callback functions (Babel translation required)

Export Default Class Test extends Component {handleClick=()=>{console.log(' clicked ',this)} render(){return (<div) onClick={ this.handleClick }>click btn</div> ) } }Copy the code

3, bind

Export default class Test extends Component {handleClick(){console.log(' click ',this)} render(){return (<div onClick={  this.handleClick.bind(this) }>click btn</div> ) } }Copy the code

4. Arrow function

Export default class Test extends Component {handleClick(){console.log(' click ',this)} render(){return (<div onClick={  ()=> this.handleClick() }>click btn</div> ) } }Copy the code

The benefit of methods 2, 3, and 4 is that you don’t need to write extra constructor functions when state is not needed. The problem with this method, however, is that each time the component is rendered, it creates a different callback function, which affects performance. If passed as props to the sub-component, the sub-component will do additional re-rendering because each time a new method instance is passed as a new property; The first and second methods are recommended to bind this;

5.5 SetState optimization

When updating setState in batches, executing setState multiple times only triggers the render process once. In the case of non-batch update (setTimeout/promise callback), the render process will be triggered once each time setState is executed. In this case, the number of setState triggers should be reduced. A common business scenario is to ensure the last call of setState when handling interface callback.

5.6 Rational use of functional components

Functional component is also called stateless component, no state, method, life cycle, only responsible for pure display; Performance optimization is achieved primarily by reducing lifecycle functions inherited from Components; When a component is only used for pure presentation and does not need its own internal state, only functional components can be considered when the component is presented mainly through props passed by the parent component.

5.7  ComponentWillUnmount

The React lifecycle function, which is triggered when a component is uninstalled, clears event listeners and timeout events.

5.8 a lazy loading

In single-page applications, lazy loading is usually used to jump from one route to another. It can also be used for complex components that are displayed only after the user has acted, such as popover modules that are displayed after a button is clicked. Lazy loading is via webpack’s dynamic import and react. lazy methods;

import { lazy, Suspense, Component} from "react" class ErrorBoundary extends Component {constructor(props) {super(props) this.state = { hasError: false } } static getDerivedStateFromError(error) { return { hasError: } render() {if (this.state.haserror) {return <h1> </h1>} return this.props. Children}} const Comp = lazy(() => { return new Promise((resolve, Reject) => {setTimeout(() => {if (math.random () > 0.5) {reject(new Error(" reject network Error "))} else { Resolve (import("./Component")) // Component is lazy}}, 2000) }) }) export default function App() { return ( <div className="App"> <div style={{ marginBottom: 20}}> When implementing lazy load optimization, not only the loading state should be considered, but also the loading failure should be fault-tolerant. </div> <ErrorBoundary> <Suspense fallback="Loading..." > <Comp /> </Suspense> </ErrorBoundary> </div> ) }Copy the code

Six interview problems

6.1 Functions of entry files in the project

src/index.js

The SRC file is the core of the project, which is our main area of work. The index.js file is the only interface to the file associated with the index. HTML file. As follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
Copy the code

Reactdom.render () renders the content to the root” root”. The “root” in document.getelementById (‘root’) is the “root” in index.html, which refers to the content of the page. Here, too, you can write something (structure, style, logic) that is the root component of the entire project.

6.2 React and React – DOM

React was not available before V0.14, all functionality was included in React. React was split into React and React – DOM starting with V0.14. The reason why it’s broken down is because of React-native; React package contains only the core parts common to Web and Mobile. That is, the React package is abstract logic and only contains the main logic of React, such as component implementation, update scheduling, etc. DOM operations are placed in the React-DOM, and Mobile operations are included in the React-native. As the name implies, react-DOM is a platform implementation for web DOM, mainly used for web rendering. ReactDOM only does browser-specific or DOM-related operations, such as reactdom.render () and reactdom.finddomNode (). For server-side rendering, reactdom.renderToString () can be used. React does everything else;

Import React from ‘React ‘; For Web applications, import REactDOM from ‘react-dom’; Import {Text, View} from ‘react-native’;

React.js: provides core functions of react. js, such as the virtual DOM, component-react. createElement(type, props, children); Render (Element, container[, callback]) -Element: The content to render -container: Content repository to render -callback: rendered callback function

6.3 React-router and React-router-dom

  1. React-router: Implements core routing functions

  2. React – the router – dom: based on the react – the router, joined in the browser running environment of some functions, such as: Link component, will render an a label, a tag line Link component source code; The BrowserRouter and HashRouter components, the former using pushState and popState events to build routes, and the latter using window.Location. hash and hashchange events to build routes

  3. The react-router-dom depends on the react-router. Therefore, when using NPM to install dependencies, only the libraries in the corresponding environment need to be installed. There is no need to explicitly install the react-router. For browser-based development, only react-router-dom is required

6.4 Role of yarn.lock file in the Project

In a word, you need to lock the version number of the package when installing yarn and upload it to Git to ensure that everyone has the same dependency when installing YARN. And it avoids incompatibilities caused by accidental changes or updates by developers;

Seven Children

The content between component tag pairs is passed as a special property props. Children the component content can be customizable in the common form of a component, as illustrated in the popover component example below

  • children
  • The transfer function
  • Transitive subcomponent

7.1 the children sample

Transfer label

import React, { PureComponent} from 'react'; import Alert from './alert'; Class App extends PureComponent {render() {return <div > <Alert title = "" > <p> </ p> </Alert> </ div>}} export default App;Copy the code

Alert. Js as follows:

import React, { PureComponent } from 'react';
import "./alert.css";
class Alert extends PureComponent {
  render() {
    let { title, children } = this.props;
    return <div id = "alert" >
      <header className = "alert-header" >{title} </header>
        {children}
      </div >
  }
}
export default Alert;
Copy the code

Effect:

Passing the React component

import React, { PureComponent } from 'react'; import Alert from './alert'; Class Btn extends PureComponent {render() {return <button> close </button>}} Class App extends PureComponent {render() { Return <div> <Alert title=" "> <Btn /> </Alert> </div >}} export default App;Copy the code

alert.js 

import React, { PureComponent } from 'react';
import "./alert.css";
class Alert extends PureComponent {
  state = {
    show: true
  }
  hide = () => {
    this.setState({
      show: false
    })
  }
  render() {
    let { show } = this.state;
    let { title, children } = this.props;
    return <div id = "alert" style = {{ display: show ? 'block' : 'none' }}>
      <header className = "alert-header">{title}< /header>
        {children}
      </div >
  }
}
export default Alert;
Copy the code

The effect

7.2 transfer component

import React, { PureComponent } from 'react'; import Alert from './alert'; Class Btn extends PureComponent {render() {return <button> close </button>}} Class App extends PureComponent {render() {  return <div> <Alert title='hello Stoney' component={<Btn />} /> </div> } } export default App;Copy the code

alert.js

import React, { PureComponent } from 'react';
import "./alert.css";

class Alert extends PureComponent {
  state={show: true}
  hide = () => {
    this.setState({show: false})
  }
  render() {
    let {show} = this.state;
    let { title } = this.props;

    return <div id="alert" style={{display: show ? 'block' : 'none'}}>
      <header className="alert-header">{title}</header>
      {this.props.component}
    </div>
  }
}

export default Alert;
Copy the code

7.3 Passing SubComponents

import React, { PureComponent } from 'react'; import Alert from './alert'; Class Btn extends PureComponent {render() {return <button onClick={() => {this.hide ()}}> Close </button>}} class App extends PureComponent { render() { return <div> <Alert title='hello Stoney' Child={Btn} /> </div> } } export default  App;Copy the code

alert.js

import React, { PureComponent } from 'react';
import "./alert.css";

class Alert extends PureComponent {
  state={show: true}
  hide = () => {
    this.setState({show: false})
  }
  render() {
    let {show} = this.state;
    let { title, Child } = this.props;

    return <div id="alert" style={{display: show ? 'block' : 'none'}}>
      <header className="alert-header">{title}</header>
      <Child hide={this.hide} />
    </div>
  }
}

export default Alert;
Copy the code

7.4 Transfer Function

Render is the name of the defined function, which can also be defined as cb

import React, { PureComponent } from 'react'; import Alert from './alert'; Class Btn extends PureComponent {render() {return <button onClick={() => {this.hide ()}}> Close </button>}} class App extends PureComponent { render() { return <div> <Alert title='hello Stoney' render={(props) => { return <Btn {... props} /> }} /> </div> } } export default App;Copy the code

alert.js

import React, { PureComponent } from 'react';
import "./alert.css";

class Alert extends PureComponent {
  state={show: true}
  hide = () => {
    this.setState({show: false})
  }
  render() {
    let {show} = this.state;
    let { title, render } = this.props;

    return <div id="alert" style={{display: show ? 'block' : 'none'}}>
      <header className="alert-header">{title}</header>
      {render({hide: this.hide})}
    </div>
  }
}

export default Alert;
Copy the code

Vue vs. React

DOM manipulation is expensive when rendering an interface, and the best we can do is to minimize DOM manipulation. VUE and React are implemented using virtual DOM.

Similarities:

  1. Both support server rendering, nuxt.js (vue server rendering framework) and next.js(React server rendering framework);
  2. Both are implemented using the virtual DOM; Both are componentized development, and the parent component data is transmitted through props parameters.
  3. Only the skeleton of the framework, other functions such as routing, state management, etc., are separated components of the framework;
  4. Both are javaScript UI frameworks, data-driven views, focused on creating front-end rich applications;
  5. React react Native, Vue Weex;
  6. React has Redux, vue has vuex;

In React, all components use JSX for rendering. JSX is a syntactic sugar of javaScript written using XML syntax. It can use the full programming language javaScript to build view pages. The tool’s support for JSX is relatively advanced compared to other vUE templates currently available. By contrast, the React feature lacks the richness of the Vue Template system, which allows Template to be styled and less involved in business logic during Template writing. A Template is always declared and any HTML in the Template is valid. The HTML code needs to be separated from the component file.

In terms of performance, when we consider re-rendering capabilities. React’s mechanism triggers a re-rendering of the entire component tree when the state of a component changes, and because React has a lot of checking memory, it can provide many useful warnings and error messages, but additional properties may be required to avoid unnecessary re-rendering of child components. While Vue’s re-rendering capability is out-of-the-box, Vue provides optimized re-rendering, where the system tracks dependencies during rendering and works accordingly; State is a key concept in react applications. There are also companion frameworks designed to manage a large state object, such as Redux. In addition, the state object is immutable in the React application, meaning that it cannot be changed directly (although = assignments can change state without rerendering the page). In React you need to use the setState() method to update the state; In vUE, the state object is not required. Data is managed in the vUE object by the data property. In VUE, methods such as setState() are not required to change its state.

Difference:

  1. React is strictly for the MVC View layer, whereas Vue is MVVM mode;
  2. Unlike Virtual DOM, VUE tracks the dependencies of each component and does not need to re-render the entire component tree.
  3. React requires shouldComponentUpdate to render the entire component tree whenever the state of the application is changed.
  4. React uses JSX + inline style, which means that the HTML and CSS are all in JAVASCRIPT.
  5. Vue recommended practice is webpack + VUe-Loader single file component format, that is, HTML, CSS, JS written in the same file;
  6. Data binding: VUE implements two-way data binding, react data flows are one-way;
  7. State objects in React are immutable, requiring setState() to update the state;
  8. In vUE, the state object is not required; the data is managed by the data attribute in the VUE object.
  9. Different event mechanisms; Vue native events use standard Web events, component custom event mechanism is the basis of parent-child communication; React native events are wrapped in an event proxy, where all events bubble up to the Document object to listen to, and then compose events to deliver. The React component has no events and uses props for parent-child communication.