preface

My previous study or work experience was related to React technology stack. Now I need to use Vue2 technology stack to maintain projects and develop requirements after I come to the new company. It took me about a week to brush the official document of Vue2. Now, in order to enhance my proficiency in using Vue2 and prevent me from quickly remembering how to use it when React is not often used but I have to switch back to it at a certain moment, Thus, this article explains the corresponding relationship between Vue2 and React in basic use.

Again, this article won’t go into too much detail about the principles of the two frameworks (I haven’t read the source πŸ˜‚), but rather, it will compare and contrast the implementation we need to use in our normal development. In short, my React implementation uses Vue2.

Design concept

A large and complex project has a clear division of responsibilities of the code structure is very important, it has very important meaning for project maintenance, so the React and Vue praise highly modular way to organize our project, like a complete computer, apart from various modules can be independent design, development, mutual coupling, Finally, according to everyone’s unified protocol to design a good interface, the final assembly into a powerful, complete computer.

However, in terms of overall writing, the design concept of the two frames is not quite the same:

JSX

React has only one syntactic sugar, JSX, which incorporates structure and execution logic and presentation into JacaScript, which is why React is more flexible. This all in JS approach has some disadvantages, it will make HTML and JS strong coupling, leading to the confusion of the code in the component, not conducive to maintenance. On the other hand, the development experience makes up for a lot in maintainability by being able to accurately jump to definitions during type hints, automatic checks, and debugging.

Template template

Vue embraces the more traditional and classic idea of separating HTML, CSS, and JS. This means that developers write code separately from structure, execution logic, and presentation, which is a huge improvement in project maintainability. In Vue, however, using the Template template and using the v-if, V-show, V-for syntactic sugar provided to write code was very unfriendly in terms of type hints, defining jumps, and so on, which was another minus for project maintenance.

An example 🌰

Now we have a simple scene that renders a “small” component based on a state that can be toggled with a button click.

React:

import React, { useState } from "react";

function App() {
  const [show, setShow] = useState(true);

  const renderContent = () = > {
    return (
      <div>
        Contents are all here...
        <span>Could be more complex</span>
      </div>
    );
  };

  return (
    <div className="App">
      <h1>Do you want to show the content below?</h1>
      <button onClick={()= >setShow(! show)}>Click</button>
      {show && renderContent()}
    </div>
  );
}

export default App;
Copy the code

We use JSX syntax {show &&… } to determine whether the render logic is executed, and separate the render logic (renderContent) from the renderContent (renderContent). This separation is common in my previous development experience. First, it is for structure reuse, for example, the render structure is also used in other places in the current file. But the “widget” isn’t big enough for me to create a separate JSX file to write as a standalone component; The second is to keep the code in return simple.

However, as the requirements continue to be iterated, the current App component becomes bloated, with a lot of rendering structures like renderContent scattered throughout the component. If you are a person working on the project now, you will find that you will be very tired of matching the render structure in the code to the page structure you have already presented!! I used to maintain a JSX file with thousands of lines of code written in a page, and a lot of rendering logic and execution logic were interspersed throughout the page. I was directly emO at that time.

To use the template template in Vue, we can write:

<template> <div id="app"> <h1>Do you want to show the content below? </h1> <button @click="handleBtnClick">Click</button> <div v-show="show"> Contents are all here... <span>Could be more complex</span> </div> </div> </template> <script> export default { name: "App", data() { return { show: true, }; }, methods: { handleBtnClick() { this.show = ! this.show; ,}}}; </script>Copy the code

As you can see, when writing components using templates, your rendering structure is all in the template, and the page is consistent with the code structure, which is very friendly to the initial developer. In addition, if you want to reuse the rendering logic with v-show in this component, you will be forced to encapsulate it into another component. React doesn’t do this because it’s too liberating, and most of the time most people don’t want to bother.

The downside of using template is that everything written after instructions is written as a string, and the useful function of defining jumps is strangled. It seems to work with TypeScript, but Vue2 and Ts are hard to work with.

Component structure

React uses.jsx files to define components. The general style is a separate file that is strongly coupled with HTML and JS, and uses {} to parse expressions as follows:

// OneComponent.jsx
import React, { useState } from "react";
// import './style.css';

function OneComponent() {
  const [content, setContent] = useState("I am one of useless component.");

  return <div>{content}</div>;
}

export default OneComponent;
Copy the code

Using components:

// App.jsx
import OneComponent from "./OneComponent";

function App() {
  return (
    <div>
      One component below:
      <OneComponent />
    </div>
  );
}
Copy the code

Vue uses a. Vue file to define components. In this file, you write HTML, CSS, JS, and {{}} to parse expressions or values.

// onecomponent.vue // structure (HTML) <template> <div>{{content}}</div> </template> // execute logic (js) <script> export default { name: "OneComponent", data() { return { content: "I am one of useless component.", }; }}; </script> // display (CSS) <style scoped></style>Copy the code

Using components:

// App.vue
<template>
  <div>
    One component below:
    <one-component />
  </div>
</template>

<script>
import OneComponent from "./OneComponent";

export default {
  name: "App",
  components: {
    OneComponent,
  },
};
</script>
Copy the code

Data management

React and Vue are one-way data flows. Data from the parent component flows down to the child component, but not vice versa. The data source of a component generally consists of two parts, one is passed in through props and the other is its own data.

react

React allows static or dynamic prop to be passed down. Static prop usually passes strings directly.

props

The function component gets props as follows:

function Student(props) {
  return <h1>My name is {props.name}</h1>;
}

const element = <Student name="vortesnail" />;
Copy the code

Dynamic prop can also be written here:

<Student name={name} age={1} isMarried={false} isHandsom />
Copy the code

state

React 16.8 Pre-class Component used state to manage data state within components. After 16.8 hooks enabled functional components to manage state as well.

UseState returns a state and a function to update the state. If the new state needs to use the previous state, you can pass a function to setState. The function takes the last state as its first argument, processes it and returns an updated value.

import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  return (
    <>
      Count: {count}
      <button onClick={()= > setCount(initialCount)}>Reset</button>
      <button onClick={()= > setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={()= > setCount((prevCount) => prevCount + 1)}>+</button>
    </>
  );
}
Copy the code

vue

Static and dynamic prop passing is also supported in Vue, but in the case of dynamic passing, the instruction V-bind, abbreviated:, is used.

props

Static prop is generally passed as a string. Obtain props as follows:

<template>
  <h1>My name is {{ name }}, I'm {{ age }} years old.</h1>
</template>

<script>
export default {
  name: "Student",
  props: ["name", "age"],
};
</script>
Copy the code

Passing dynamic prop is different, and requires v-bind:

<student :name="name" :age="1" />
;
Copy the code

You can make type constraints on prop in Vue, such as:

props: { name: String, age: { type: Number, default: 18 } }
Copy the code

But in React you need the prop-types library (with TypeScript you just check at compile time).

data

Data state within components in Vue is managed by Data. When a component is defined, DATA must be declared as a function that returns an initial data object.

<script> export default { name: "OneComponent", data() { return { name: "vortesnail", age: 12, }; }}; </script>Copy the code

The state can be changed directly from the Vue instance:

methods: { changeName() { this.name = 'vortesnail2'; }}Copy the code

Class and style

React differs from Vue in class and style.

react

React uses the className keyword instead of the class attribute in the real DOM.

className

In React, classnames are usually passed string constants or string variables. Arrays or objects are not supported.

import React, { useState } from "react";

function App() {
  const [show, setShow] = useState(true);
  const [active, setActive] = useState(true);

  return (
    <div
      className={`appThe ${show ? "show" :"" ${}active ? "active" :""} `} ></div>
  );
}

export default App;
Copy the code

React uses the JS template string syntax directly. When there are too many styles, use the classnames package to gracefully transfer various states.

classNames("foo"."bar"); // => 'foo bar'
classNames("foo", { bar: true }); // => 'foo bar'
classNames({ "foo-bar": true }); // => 'foo-bar'
classNames({ "foo-bar": false }); / / = > '
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true.bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames("foo", { bar: true.duck: false }, "baz", { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null.false."bar".undefined.0.1, { baz: null }, ""); // => 'bar 1'
Copy the code

style

React style receives an object:

const someStyles = {
  color: lightyellow,
  background: lightblue,
  fontSize: "12px"};function App() {
  return <div style={someStyles}>Colorful world!</div>;
}
Copy the code

vue

Unlike React, Vue provides class and style enhancements, allowing you to pass strings, arrays, and objects. In addition, v-bind:class will be merged with class, v-bind:style will be merged with style.

class

  1. Binding string:
<div class="app">hello world! </div>Copy the code
  1. Binding object:
<template> <div class="app" :class="{ show: isShow, active: isActive }"> hello world! </div> </template> <script> export default { name: "App", data() { return { isShow: true, isActive: false, }; }}; </script> <style> .app { background-color: #fff; } .show { display: block; } .active { color: blue; } </style>Copy the code

The actual DOM structure is rendered as follows:

<div class="app show">hello world!</div>
Copy the code
  1. Bind arrays:
<template> <div class="app" :class="[showClass, activeClass]">hello world! </div> </template> <script> export default { name: "App", data() { return { showClass: "show", activeClass: "active", }; }}; </script> <style> .app { background-color: #fff; } .show { display: block; } .active { color: blue; } </style>Copy the code

The actual DOM structure is rendered as follows:

<div class="app show active">hello world!</div>
Copy the code
  1. classCan be passed directly to the outermost element inside a component as a property:
Vue.component('my-component', { template: ' <p class="origin">hello world! </p> ' })Copy the code

Add an additional class when using:

<my-component class="extra"></my-component>
Copy the code

The actual DOM structure is rendered as follows:

<p class="origin extra">hello world!</p>
Copy the code

style

  1. The object:
<template> <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"> hello world! </div> </template> <script> export default { name: "App", data() { return { activeColor: "green", fontSize: 16, }; }}; </script>Copy the code

The actual DOM structure is rendered as follows:

<div style="color: green; font-size: 16px;">hello world!</div>
Copy the code
  1. The array:
<template> <div :style="[baseStyles, overridingStyles]">hello world! </div> </template> <script> export default { name: "App", data() { return { baseStyles: { fontSize: "20px", color: "green", }, overridingStyles: { fontSize: "16px", }, }; }}; </script>Copy the code

The actual DOM structure is rendered as follows:

<div style="font-size: 16px; color: green; height: 80px;">hello world!</div>
Copy the code

Conditions apply colours to a drawing

Conditional rendering is deciding whether to render something based on a condition.

The react to realize

React uses the ampersand operator &&. If… Else to achieve conditional rendering.

With (1.&&) operator

The ampersand operator && renders the content on the right when the left value is true.

return <div>{show && <div>Content here.</div>}</div>;
Copy the code

2. The ternary operator (? :)

Just like in JS syntax, render the previous content if the conditions are met, and the latter if the conditions are not met.

return (
  <div>
    {renderFirst ? <div>First content.</div> : <div>Second content.</div>}
  </div>
);
Copy the code

3. Multiple statements

Do not write too many conditional nested judgments in return statements. For example, try not to use more than one layer of the trinary operator, otherwise the code will become very difficult to read. Else or switch case to filter.

import React, { useState, Fragment } from "react";

function App() {
  const [order, setOrder] = useState("first");

  const renderContent = (order) = > {
    if (order === "first") {
      / / Frament will not have a real DOM generates, grouping can be more than content
      return (
        <Fragment>
          <div>First content.</div>
          <div>Extra content of first part.</div>
        </Fragment>
      );
    } else if (order === "second") {
      return <div>Second content.</div>;
    } else if (order === "third") {
      return <div>Third content.</div>;
    } else {
      return null; }};return <div>{renderContent(order)}</div>;
}

export default App;
Copy the code

Vue implementation

To achieve conditional rendering in VUE, just use the instructions V-if, V-else -if, v-else.

1. V-if, V-else -if, v-else

And js syntax consistent.

<template> <div id="app"> <div v-if="type === 'first'">First content.</div> <div v-else-if="type === 'second'">Second content.</div> <div v-else>Third content.</div> </div> </template> <script> export default { name: "App", data() { return { type: "first", }; }}; </script>Copy the code

2. Use templatev-if

Using v-if on

<template> <div id="app"> <template v-if="type === 'first'"> <div>First content.</div> <div>Extra content of first part.</div> </template> <div v-else-if="type === 'second'">Second content.</div> <div v-else>Third content.</div> </div>  </template>Copy the code

Element show hide

Unlike conditional rendering, we can also control the explicit and implicit elements through styles, which also reduces the performance impact of frequent addition and deletion of DOM nodes.

The react to realize

React does this by changing the inline style or the class selector, mainly the display property.

Modify inline style style mode:

<div style={{ display: showName ? "block" : "none" }}>vortesnail</div>
Copy the code

Add or delete class names:

import React, { useState } from "react";

function App() {
  const [show, setShow] = useState(true);

  return <div classNames={`appThe ${show ? "show" : "hide"} `} ></div>;
}

export default App;
Copy the code

Vue implementation

The V-show directive is provided in Vue to quickly manipulate whether or not an element is displayed, essentially modifying the inline style display attribute.

<div v-show="showName">vortensnail</div>
Copy the code

inshowName δΈΊ falseWhen,style ηš„ display δΈΊ none :

inshowName δΈΊ trueWhen,style ηš„ displayProperty is removed using the element’s default value:

The list of rendering

React uses the native JS array syntax Map to render lists, while Vue uses the v-for directive to render lists. React allows you to use lists. Filter (…) .map(…) Filter.

A unique key is added to each list item to reduce unnecessary diff algorithm comparisons.

The react to realize

Render array:

import React, { useState } from "react";

function App() {
  const [lists, setLists] = useState([
    { name: "vortesnail" },
    { name: "sean"},]);return (
    <ul id="app">
      {lists.map((item, index) => (
        <li key={item.name + index} >{item.name}</li>
      ))}
    </ul>
  );
}

export default App;
Copy the code

Render object:

import React, { useState } from "react";

function App() {
  const [obj, setObj] = useState({
    name: "vortesnail".age: 26.sex: "male".height: 171});const renderObj = () = > {
    const keys = Object.keys(obj);
    return keys.map((key, index) = > <li key={key + index} >{obj[key]}</li>);
  };

  return <ul id="obj-rendering">{renderObj()}</ul>;
}

export default App;
Copy the code

Js syntax.

Vue implementation

Render array:

<template> <ul id="app"> <li v-for="(item, index) in lists" :key="item.name + index"> {{ item.name }} </li> </ul> </template> <script> export default { name: "App", data() { return { lists: [{ name: "vortesnail" }, { name: "sean" }], }; }}; </script>Copy the code

Render object:

<template> <ul id="app"> <li v-for="(value, key, index) in obj" :key="key + index"> {{ value }} </li> </ul> </template> <script> export default { name: "App", data() { return { obj: { name: "vortesnail", age: 26, sex: "male", height: 171, }, }; }}; </script>Copy the code

The event processing

React and Vue both encapsulate native DOM events, but their usage is quite different.

react

The React element handles events in a similar way to the native DOM, but with some syntax differences:

  • Events are named in small hump (e.gonClick) instead of pure lowercase.
  • To use JSX syntax you need to pass in a function as an event handler.
  1. Event handlers do not pass arguments

When no argument is passed, an event object is implicitly passed as the first argument to the handler function.

import React from "react";

function App() {
  const handleClick = (e) = > {
    console.log(e.target);
  };

  return <div onClick={handleClick}>Click me</div>;
}

export default App;
Copy the code
  1. Event handlers pass arguments

Most of the time we need to pass other arguments to the handler, we can also write:

import React from "react";

function App() {
  const handleClick = (e, str) = > {
    console.log(e.target);
    console.log(str);
  };

  return <div onClick={(e)= > handleClick(e, "vortesnail")}>Click me</div>;
}

export default App;
Copy the code

vue

Event processing in Vue requires a directive V-on, abbreviated @, that takes a method name and passes it in as a string.

  1. Event handlers do not pass arguments
<template> <div @click="handleClick">Click me! </div> </template> <script> export default { name: "App", methods: { handleClick(e) { console.log(e.target); ,}}}; </script>Copy the code
  1. Event handlers pass arguments (can be used$eventPlaceholder access eventeventObject)
<template> <div @click="handleClick($event, 'vortesnail')">Click me</div> </template> <script> export default { name: "App", methods: { handleClick(e, str) { console.log(e.target); console.log(str); ,}}}; </script>Copy the code

Common event modifiers and keypress modifiers are also available in Vue, allowing us to focus on the data logic rather than the event details of the DOM.

Component communication

When developing components, it is inevitable to encounter communication problems between parent and child components and components across multiple levels. Both React and Vue have corresponding solutions.

Parent-child component communication

A common scenario is that a parent component maintains a set of data, and changes to that data are performed by a function defined by the parent component. This function can be called in the parent component and its children, and the child component can call the child’s data.

For example, clicking the “Change random number” button generates a new random number and updates the page value.

The react to realize

React is implemented using the props + callback function.

Parent component app.jsx:

import React, { useState } from "react";
import RandomNum from "./RandomNum";

function App() {
  const [randomNum, setRandomNum] = useState(0);

  const handleUpdateNum = (num) = > {
    setRandomNum(num);
  };

  return (
    <div id="app">
      <p>The current random number is:</p>
      <RandomNum num={randomNum} changeNum={handleUpdateNum} />
    </div>
  );
}

export default App;
Copy the code

Subcomponent randomNum.jsx:

import React from "react";

function RandomNum(props) {
  const { num, changeNum } = props;

  const handleChangeTitle = () = > {
    changeNum(~~(Math.random() * 100));
  };

  return (
    <div>
      <h4>{num}</h4>
      <button onClick={handleChangeTitle}>Changing random number</button>
    </div>
  );
}

export default RandomNum;
Copy the code

On the one hand, the parent component’s randomNum data is transmitted to the child component through NUM, on the other hand, the child component receives the child component’s data through the parent component callback function passed by changeNum, so as to achieve the effect of parent-child component communication.

Parent tone component methods can be implemented through the forwardRef and useImperativeHandle.

Vue implementation

The first method is similar to the react implementation above. The same idea can be implemented in Vue via the props + callback function.

Parent component app.vue:

<template> <div id="app"> <p> Current random number is :</p> <random-num :num="randomNum" :changeNum="handleUpdateNum"  <script> import RandomNum from "./RandomNum.vue"; export default { name: "App", components: { RandomNum }, data() { return { randomNum: 0, }; }, methods: { handleUpdateNum(num) { this.randomNum = num; ,}}}; </script>Copy the code

Subcomponent RandomNum. Vue:

<template> <div> <h4>{{num}}</h4> < button@click ="handleChangeTitle"> Change the random number </button> </div> </template> <script> export  default { name: "RandomNum", props: ["num", "changeNum"], methods: { handleChangeTitle() { this.changeNum(~~(Math.random() * 100)); ,}}}; </script>Copy the code

The second way is through the props + custom event method.

The parent component passes data to the child component via props. The child component fires custom events using $emit. The parent component listens for custom events of the child component to get data from the child component. In essence, a callback function is used to allow a child component to pass data to its parent.

Parent component app.vue:

<template> <div id="app"> <p> Current random number is :</p> <random-num :num="randomNum" @changenum ="handleUpdateNum" /> </div> </template>  <script> import RandomNum from "./RandomNum.vue"; export default { name: "App", components: { RandomNum }, data() { return { randomNum: 0, }; }, methods: { handleUpdateNum(num) { this.randomNum = num; ,}}}; </script>Copy the code

Subcomponent RandomNum. Vue:

<template> <div> <h4>{{num}}</h4> < button@click ="handleChangeTitle"> Change the random number </button> </div> </template> <script> export  default { name: "RandomNum", props: ["num"], methods: { handleChangeTitle() { this.$emit("changeNum", ~~(Math.random() * 100)); ,}}}; </script>Copy the code

The parent tune component method can be implemented via this.$refs.

Communicate across multiple tiers of components

In theory, we could use a common parent component to communicate with sibling components, and multiple props to communicate with parent and grandson components. However, this would be very difficult to maintain. I can’t even maintain myself after that.)

So we needed a more efficient and maintainable solution, and React and Vue both provide this capability.

The react to realize

React is implemented using the React. CreateContext and useContext apis.

Root component app.jsx:

import React, { useState } from "react";
import Title from "./Title";

// Create the Context object
export const AppContext = React.createContext();

function App() {
  const [randomNum, setRandomNum] = useState(0);

  const handleUpdateNum = (num) = > {
    setRandomNum(num);
  };

  return (
    <AppContext.Provider value={{ num: randomNum.changeNum: handleUpdateNum}} >
      <div id="app">
        <Title />
      </div>
    </AppContext.Provider>
  );
}

export default App;
Copy the code

Component title.jsx, one layer below the root component:

import React from "react";
import RandomNum from "./RandomNum";

function Title() {
  return (
    <div>
      <p>The current random number is:</p>
      <RandomNum />
    </div>
  );
}

export default Title;
Copy the code

Componentrandomnum. JSX:

import React, { useContext } from "react";
import { AppContext } from "./App";

function RandomNum() {
  const { num, changeNum } = useContext(AppContext);

  const handleChangeTitle = () = > {
    changeNum(~~(Math.random() * 100));
  };

  return (
    <div>
      <h4>{num}</h4>
      <button onClick={handleChangeTitle}>Changing random number</button>
    </div>
  );
}

export default RandomNum;
Copy the code

You can also use useReducer to simulate the use of React-redux in component communication. This is usually used when developing complex components. For our project itself, use React-Redux and Mobx.

Vue implementation

There are a lot of ways to communicate across components in Vue (except for vuex). Here are two ways to use it:

  • $attrs 与 $listeners
  • provide 与 inject

The first method is provide and inject.

This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, and remain in effect for as long as its upstream and downstream relationships are established. If you’re familiar with React, this is very similar to the context features of React.

Root component app.vue:

<template> <div id="app"> <title-component /> </div> </template> <script> import Title from "./Title.vue"; export default { name: "App", components: { "title-component": Title }, provide() { return { num: this.randomNum, changeNum: this.handleUpdateNum, }; }, data() { return { randomNum: { value: 0, }, }; }, methods: { handleUpdateNum(num) { this.randomNum.value = num; ,}}}; </script>Copy the code

Provide () {this.randomNum (); this.handleUpdateNum ();

And indataDefined in therandomNumMust be an object, the reason being to make the injected data responsive.

Component title.vue, one layer below the root component:

<template> <div> <p> Current random number is :</p> <random-num /> </div> </template> <script> import RandomNum from "./RandomNum"; export default { name: "Title", components: { RandomNum, }, }; </script>Copy the code

Grandson component RandomNum. Vue:

<template> <div> <h4>{{num. Value}}</h4> < button@click ="handleChangeTitle"> Change the random number </button> </div> </template> <script> export default { name: "RandomNum", inject: ["num", "changeNum"], methods: { handleChangeTitle() { this.changeNum(~~(Math.random() * 100)); ,}}}; </script>Copy the code

The second method is $listeners and $attrs. $attrs allows the props to access the data passed by the root via v-bind. $Listeners can cause child components to trigger custom event callbacks from the root component via the V-ON binding via this.$emit.

Root component app.vue:

<template> <div id="app"> <title-component :num="randomNum" @changeNum="handleUpdateNum" /> </div> </template> <script> import Title from "./Title.vue"; export default { name: "App", components: { "title-component": Title }, data() { return { randomNum: 0, }; }, methods: { handleUpdateNum(num) { this.randomNum = num; ,}}}; </script>Copy the code

Component title.vue, one layer below the root component:

<template> <div> <p> Current random numbers are :</p> <random-num v-bind="$attrs" v-on="$listeners" /> </div> </template> <script> import RandomNum from "./RandomNum"; export default { name: "Title", components: { RandomNum, }, }; </script>Copy the code

Grandson component RandomNum. Vue:

<template> <div> <h4>{{num}}</h4> < button@click ="handleChangeTitle"> Change the random number </button> </div> </template> <script> export  default { name: "RandomNum", props: ["num"], methods: { handleChangeTitle() { this.$emit("changeNum", ~~(Math.random() * 100)); ,}}}; </script>Copy the code

Vue has a way to realize communication between all components, called GlobalEventBus (EventBus), can be interested in: Vue GlobalEventBus principle and exploration process

Cache optimization

React and Vue both provide optimized methods. For functions with the same input and the same output, the result is cacheable and does not need to be recalculated every time they are re-rendered.

React useMemo and useCallback

React provides two main hooks, useMemo and useCallback. Use useMemo to cache values and useCallback to cache functions.

When a child component uses React. Memo, consider using useMemo and useCallback to encapsulate the props provided to the child component so that you can take advantage of the shallow comparison capabilities of the Memo and reduce unnecessary repetitive but meaningless rendering.

If you now have the following scenario: click the “Generate random number” button, randomNum will be changed and the component will be re-rendered. Click “Change the number of small watermelons” button, the list length will increase.

App. JSX:

import React, { useState } from "react";
import List from "./List";

function App() {
  const [randomNum, setRandomNum] = useState(0);
  const [listLen, setListLen] = useState(100);

  const handleGenerateRandomNum = () = > {
    setRandomNum(~~(Math.random() * 100));
  };

  const handleChangeListLength = () = > {
    setListLen((pre) = > pre + 1);
  };

  const list = new Array(listLen).fill(1).map((item, index) = > {
    return {
      id: index,
      text: `${index}A small watermelon}; });return (
    <div id="app">
      <List data={list} />
      <span>Random number is: {randomNum}</span>
      <button onClick={handleGenerateRandomNum}>Random number generation</button>
      <button onClick={handleChangeListLength}>Change the number of small watermelons</button>
    </div>
  );
}

export default App;
Copy the code

List. JSX:

import React, { memo } from "react";

function List(props) {
  const { data = [] } = props;
  console.log('I'm a List component, I'm rendered, and my length is${data.length}`);

  return (
    <ul>
      {data.map((item) => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
}

export default memo(List);
Copy the code

If you open the console at this point and frantically click on the “Generate random number” button, you’ll find something like this:

Theoretically, we don’t want the List to be rerendered when “Generate random number” is clicked, because randomNum is independent of the List. You can see that even if we use the react. memo in the child component, it is useless because each time we generate a new array in memory.

All you need to do is wrap useMemo around the places that depend on listLen to compute the list, so that the list will only be recalculated if listLen changes.

const list = useMemo(() = > {
  return new Array(listLen).fill(1).map((item, index) = > {
    return {
      id: index,
      text: `${index}A small watermelon}; }); }, [listLen]);Copy the code

The effect is as follows:

Similarly, useCallback can be used when a parent component passes a method to a child component. As long as the dependency remains unchanged, there is no need to generate a new method or re-render the child component.

The computed vue

Vue uses computed to represent computed properties, which are cached based on their responsive dependencies. They are reevaluated only when the associated reactive dependencies change.

App. Vue:

<template>
  <div id="app">
    <list :data="list" />
    <span>ιšζœΊζ•°ζ˜―οΌš{{ randomNum }}</span>
    <button @click="handleGenerateRandomNum">δΊ§η”ŸιšζœΊζ•°</button>
    <button @click="handleChangeListLength">ζ”Ήε˜ε°θ₯Ώη“œζ•°ι‡</button>
  </div>
</template>

<script>
import List from "./List.vue";

export default {
  name: "App",
  components: { List },
  data() {
    return {
      randomNum: 0,
      listLen: 100,
    };
  },
  computed: {
    list() {
      return new Array(this.listLen).fill(1).map((item, index) => {
        return {
          id: index,
          text: `${index}δΈͺ小θ₯Ώη“œ`,
        };
      });
    },
  },
  methods: {
    handleGenerateRandomNum() {
      this.randomNum = ~~(Math.random() * 100);
    },
    handleChangeListLength() {
      this.listLen += 1;
    },
  },
};
</script>
Copy the code

List. The vue:

<template> <ul> <li v-for="item in data" :key="item.id">{{ item.text }}</li> </ul> </template> <script> export default { Name: "List", props: ["data"], updated() {console.log(' I am List component, I am rendered, my length is ${this.data.length} '); }}; </script>Copy the code

In addition, if you put the evaluation of the list into methods, the child component will also be re-rendered.

<template> <div id="app"> <list :data="list()" /> </div> </template> <script> import List from "./List.vue"; export default { methods: { //... List () {return new Array(this.listlen).fill(1).map((item, index) => {return {id: index, text: '${index} small watermelon',}; }); ,}}}; </script>Copy the code

Compute properties have getters as well as setters. See the official documentation for details.

The listener

The concept of watch is unique to Vue. It listens for changes in props, data, and computed, and performs asynchronous or expensive operations. In React, functions similar to Vue Watch can also be realized through custom hook. The specific implementation can be written in another article, and we will share it when we have the opportunity to study it.

Used in Vue as follows:

watch: { message: { handler(newMsg, oldMsg) { this.msg = newMsg; }, // execute handler method immediate: true, // deep: true,}}Copy the code

ref

React and Vue both provide access to the native DOM, implemented using ref.

The react to realize

Use ref in the current component to get the actual DOM.

import React, { useEffect, useRef } from "react";

function App() {
  const ref = useRef(null);

  useEffect(() = > {
    ref.current?.focus();
  }, []);

  return (
    <div id="app">
      <input ref={ref} />
    </div>
  );
}

export default App;
Copy the code

But sometimes we need to use the React. ForwardRef if we want to get an actual DOM element from the parent component.

Parent component app.jsx:

import React, { useEffect, useRef } from "react";
import InputComponent from "./InputComponent";

function App() {
  const inputRef = useRef(null);

  useEffect(() = > {
    inputRef.current?.focus();
  }, []);

  return (
    <div id="app">
      <InputComponent ref={inputRef} />
    </div>
  );
}

export default App;
Copy the code

Child component inputComponent.jsx:

import React from "react";

const InputComponent = React.forwardRef((props, ref) = > {
  return (
    <div id="input-component">
      <input ref={ref} />
    </div>
  );
});

export default InputComponent;
Copy the code

Vue implementation

Use ref in the current component to get the actual DOM.

<template> <div id="app"> <input ref="input" /> </div> </template> <script> export default { name: "App", mounted() { this.$refs.input.focus(); }}; </script>Copy the code

To get the actual DOM of a subcomponent in vUE, you need to get the instance of the subcomponent and then get the DOM via the $refs of the subcomponent instance.

Parent component app.vue:

<template> <div id="app"> <input-component ref="inputComponent" /> </div> </template> <script> import InputComponent from "./InputComponent.vue"; export default { name: "App", components: { InputComponent, }, mounted() { this.$refs.inputComponent.$refs.inputRef.focus(); }}; </script>Copy the code

Inputcomponent.vue:

<template>
  <div id="input-component">
    <input ref="inputRef" />
  </div>
</template>

<script>
export default {
  name: "InputComponent",
};
</script>
Copy the code

Controlled and v – model

React input, Textarea and other uncontrolled components get the current input through the onChange event and pass it in as a value. Then they become controlled components. The purpose of this is to control user input through the onChange event. For example, regular expressions are used to filter out improper input.

Vue uses V-Model to achieve two-way data binding.

The react to realize

import React, { useState } from "react";

function App() {
  const [name, setName] = useState("vortesnail");

  const handleChange = (e) = > {
    setName(e.target.value);
  };

  return (
    <div id="app">
      <input value={name} onChange={handleChange}></input>
    </div>
  );
}

export default App;
Copy the code

Vue implementation

The V-Model is used for bidirectional binding of form data, which is essentially a syntax sugar. There are two operations behind this:

  • v-bindBind avalueProperties.
  • v-onThe directive binds the current elementinputEvents.
<template> <div id="app"> <input V-model ="name" /> -- <input :value="name" @input="(e) => (name = e.target.value)" /> --> </div> </template> <script> export default { name: "App", data() { return { name: "vortensial", }; }}; </script>Copy the code

The v-Model can also be used for custom components, and the child component should do the following:

  • To receive avalueAs aprop 。
  • The triggerinputEvent and pass in a new value.

Parent component app.vue:

<template> <div id="app"> <input-component v-model="name" /> </div> </template> <script> import InputComponent from "./InputComponent.vue"; export default { name: "App", components: { InputComponent }, data() { return { name: "vortensial", }; }}; </script>Copy the code

Inputcomponent.vue:

<template> <div id="input-component"> <input :value="value" @input="handleInput" /> </div> </template> <script> export default { name: "InputComponent", props: ["value"], methods: { handleInput(e) { this.$emit("input", e.target.value); ,}}}; </script>Copy the code

slot

This division starts with slots in Vue. In my opinion, the division of default slots, named slots, and scope slots in Vue covers at least all the scenes I touched. React doesn’t have this division, everything is props.

vue

Vue uses

to implement slots, including default slots, named slots, and scoped slots.

The default slot

The default slot uses
to occupy a reserved space in the component, into which all content contained in the start and end tags used by the component is rendered.

Parent component app.vue:

<template> <div id="app"> <title-component> <div> I am content </div> </title-component> </div> </template> <script> import title from "./Title.vue"; export default { name: "App", components: { "title-component": Title }, }; </script> <style></style>Copy the code

Subcomponent title.vue:

<template> <div> <h1> I am the Title </h1> <slot></slot> </div> </template> <script> export default {name: "Title",}; </script>Copy the code

The rendered real DOM structure verifies what we call “placeholder” :

A named slot

Only one slot can be inserted into the default slot. When multiple slots are inserted, a named slot is required. Use the

form to define named slots.

The default slot name is default, and v-slot can be shortened to #.

Child component Page.vue:

<template>
  <div>
    <header>
      <slot name="header"> Header content. </slot>
    </header>
    <main>
      <slot> Main content. </slot>
    </main>
    <footer>
      <slot name="footer"> Footer content. </slot>
    </footer>
  </div>
</template>

<script>
export default {
  name: "Page",
};
</script>
Copy the code

Parent component app.vue:

<template>
  <div id="app">
    <page>
      <template #header>
        <div>This is header content.</div>
      </template>
      <template>
        <div>This is main content.</div>
      </template>
      <template #footer>
        <div>This is footer content.</div>
      </template>
    </page>
  </div>
</template>

<script>
import Page from "./Page.vue";

export default {
  name: "App",
  components: { Page },
};
</script>
Copy the code

Scope slot

Sometimes it is useful to give slot content access to data that is only available in child components.

Child component Page.vue:

<template> <div> <header> <slot name="header"> Header content. </slot> </header> <main> <slot :main="main"> Main content. </slot> </main> </div> </template> <script> export default { name: "Page", data() { return { main: { title: "I am the title of the article ",},}; }}; </script>Copy the code

Parent component app.vue:

<template>
  <div id="app">
    <page>
      <template v-slot:header>
        <div>This is header content.</div>
      </template>
      <template v-slot:default="{ main }">
        <div>{{ main.title }}</div>
        <div>{{ main.content }}</div>
      </template>
    </page>
  </div>
</template>

<script>
import Page from "./Page.vue";

export default {
  name: "App",
  components: { Page },
};
</script>
Copy the code

Note that there is no shorthand for v-slot:default. This can only be used if the name is explicitly defined, such as #header=” XXX “.

react

React supports props. Children or Render props to implement slots in Vue.

props.children

Children is any element wrapped around the start and end tags of the child component, just like the default slot.

Parent component app.jsx:

import React from "react";
import Title from "./Title";

function App() {
  return (
    <div id="app">
      <Title>
        <div>I am content</div>
      </Title>
    </div>
  );
}

export default App;
Copy the code

Subcomponent title.jsx:

import React from "react";

function Title(props) {
  return (
    <div>
      <h1>I am heading</h1>
      {props.children}
    </div>
  );
}

export default Title;
Copy the code

render props

Remember that in React everything is props, so let’s simulate the implementation scope slot.

Child component Page.jsx:

import React, { useState } from "react";

function Page(props) {
  const [main, setMain] = useState({
    title: "I am the title of the article.".content: "I am the content of the article"});return (
    <div>
      <header>{props.header || "Header content."}</header>
      <main>{props.renderMain ? props.renderMain(main) : "Main content."}</main>
    </div>
  );
}

export default Page;
Copy the code

Parent component app.jsx:

import React from "react";
import Page from "./Page";

function App() {
  return (
    <div id="app">
      <Page
        header={<div>This is header content.</div>}
        renderMain={(main) => (
          <>
            <div>{main.title}</div>
            <div>{main.content}</div>
          </>)} / >
    </div>
  );
}

export default App;
Copy the code

I personally haven’t used anything like the scope slot feature in React development so far, so I’m not sure why it’s being promoted in Vue. πŸ˜‚

Logic reuse

In React, you can use custom hooks to separate frequently used logic for reuse. In Vue2, you can use mixins to do this.

react

Before React 16.8, we used HOC to reuse code logic. Now we use custom hooks to reuse code. Suppose you have A scenario where both components A and B want to request data during the first rendering, but you don’t want to write the requested code logic in both components.

HOC

First we define a higher-order function withData, which receives the component and returns an anonymous function of the props to which we want to reuse the code logic. Then we pass the props and the “result” of the reuse code as prop to the component wrapped by the higher-order function. In this component you can get the “result” of the reuse code through props.

import React, { useState, useEffect } from "react";

const withData = (Component) = > (props) = > {
  const [results, setResults] = useState([]);

  const fetchData = async() = > {const response = await fetch("https://pokeapi.co/api/v2/pokemon");
    const data = await response.json();
    setResults(data.results);
  };

  useEffect(() = >{ fetchData(); } []);return <Component {. props} results={results} />;
};

const A = (props) = > {
  const { title, results } = props;
  return (
    <div>
      <h1>{title}</h1>
      <ul>
        {results.map((pokemon) => (
          <li key={pokemon.name}>{pokemon.name}</li>
        ))}
      </ul>
    </div>
  );
};

const B = (props) = > {
  return <div>List length: {props.results.length}</div>;
};

const WrappedA = withData(A);
const WrappedB = withData(B);

function App() {
  return (
    <div id="app">
      <WrappedA title="The list data is as follows:" />
      <WrappedB />
    </div>
  );
}

export default App;
Copy the code

HOC is prone to deep nesting, poor readability, difficult debugging, and common props can be overridden.

Customize the hook

Custom hooks seem much simpler, essentially wrapping code that was originally written in a component into another hook and calling it when needed.

import React, { useState, useEffect } from "react";

const useData = () = > {
  const [results, setResults] = useState([]);

  const fetchData = async() = > {const response = await fetch("https://pokeapi.co/api/v2/pokemon");
    const data = await response.json();
    setResults(data.results);
  };

  useEffect(() = >{ fetchData(); } []);return results;
};

const A = (props) = > {
  const results = useData();
  const { title } = props;

  return (
    <div>
      <h1>{title}</h1>
      <ul>
        {results.map((pokemon) => (
          <li key={pokemon.name}>{pokemon.name}</li>
        ))}
      </ul>
    </div>
  );
};

const B = (props) = > {
  const results = useData();

  return <div>List length: {results.length}</div>;
};

function App() {
  return (
    <div id="app">
      <A title="The list data is as follows:" />
      <B />
    </div>
  );
}

export default App;
Copy the code

vue

mixins

A mixin object can contain any component option. When a component uses mixin, all mixin options are “blended” into the component’s own options.

Root component app.vue:

<template> <div id="app"> <a-component title=" " /> <b-component /> </div> </template> <script> import A from "./A.vue"; import B from "./B.vue"; export default { name: "App", components: { "a-component": A, "b-component": B }, }; </script>Copy the code

Subcomponent A. ue:

<template>
  <div>
    <h1>{{ title }}</h1>
    <ul>
      <li v-for="pokemon in results" :key="pokemon.name">
        {{ pokemon.name }}
      </li>
    </ul>
  </div>
</template>

<script>
import { dataMixin } from "./mixins";

export default {
  name: "A",
  mixins: [dataMixin],
  props: ["title"],
};
</script>
Copy the code

Sub-component B.ue:

<template>
  <div>List length: {{ results.length }}</div>
</template>

<script>
import { dataMixin } from "./mixins";

export default {
  name: "A",
  mixins: [dataMixin],
};
</script>
Copy the code

With mixins. Js:

export const dataMixin = {
  data() {
    return {
      results: [],}; },created() {
    this.fetchData();
  },
  methods: {
    async fetchData() {
      const response = await fetch("https://pokeapi.co/api/v2/pokemon");
      const data = await response.json();
      this.results = data.results; ,}}};Copy the code

This is similar to the React custom hook, where the logic is written in the component itself.

  • Mixins are prone to conflicts: because the properties of each feature are merged into the same component, a property or method with the same name in the component overwrites the mixins.
  • Limited reusability: Mixins cannot be passed any parameters to change their logic, which reduces their flexibility in abstract logic.
  • Unclear data source: Data or methods in mixins used by components are not searchable in the current component code, which may lead to incorrect interpretation, such as being deleted as error code or redundant code.

conclusion

After reading the whole article, you will find that the content is relatively simple. The purpose of this article is to establish a cognitive bridge between the two frameworks by means of comparison, which makes it easier to accept the switch of technology stack. Personally, I also think this way of learning will be more efficient for work. First of all, I can work, and then I can think about in-depth problems.

React or Vue is not the whole story of React or Vue

This is my github/blog. If you can help, please click star 🌟

reference

Why did we abandon Vue? Vue and React are compared in depth