The title React for Vue developers
Chinese title React Guide for Vue users
provenance Sebastiandedeyne.com/react-for-v…
The starting www.yuque.com/freeroadmap…
The release date May 20, 2019
The translation date January 23, 2021
keywords React, Vue

I’ve been using React and Vue on different projects for the past three years.

Last month I wrote about why I Like React Better than Vue. Then I sat on Adam Wathan’s Full Stack Radio (a podcast interview). Talk about React from a Vue developer’s point of view.

We’ve covered a lot of this in the podcast, but most of what we’ve covered can be inspired by the following snippet of code that explains their similarities and differences.

This article is a brief summary of most of vUE’s features, which I will implement in 2019 using Rect Hooks.

If I’m missing something, or want to see other comparisons, or you want to share your thoughts on vue and React, check me out on Twitter

Template Template

React alternative: JSX

Vue uses HTML strings and a special Directive to write templates. The use of.vue files to separate templates, logical scripts, and style styles is encouraged.

basis

<! -- Gretter.vue --> <template> <p>Hello, {{ name }}! </p> </template> <script> export default { props: ['name'] }; </script>Copy the code

React uses JSX, which is an extension of ECMAScript.

export default function Greeter({ name }) {
  return <p>Hello, {name}!</p>;
}
Copy the code

Conditions apply colours to a drawing

React options: && operator Logical && operator, Ternary statements, early returns

Vue uses the V-if, V-ELSE, and V-else directives to render part of the conditional template.

<! -- Awesome.vue --> <template> <article> <h1 v-if="awesome">Vue is awesome! </h1> </article> </template> <script> export default { props: ['awesome'] }; </script>Copy the code

React doesn’t support directives, so use JS to determine what you want to return.

The && operator provides the succinct way to write if statements. React

export default function Awesome({ awesome }) {
  return (
    <article>
      {awesome && <h1>React is awesome!</h1>};
    </article>
  );
}
Copy the code

If you need to use else, you can use a ternary expression.

export default function Awesome({ awesome }) {
  return (
    <article>
      {awesome ? (
        <h1>React is awesome!</h1>
      ) : (
        <h1>Oh no 😢</h1>
      )};
    </article>
}
Copy the code

You can also end the judgment prematurely by using return to separate the two conditions,

export default function Awesome({ awesome }) {
  if(! awesome) {return (
      <article>
        <h1>Oh no 😢</h1>
      </article>
    );
  }

  return (
    <article>
      <h1>React is awesome!</h1>
    </article>
  );
}
Copy the code

The list of rendering

React alternatives: array.map.

Vue uses the V-for instruction to loop through arrays and objects.

<! -- Recipe.vue --> <template> <ul> <li v-for="(ingredient, index) in ingredients" :key="index"> {{ ingredient }} </li> </ul> </template> <script> export default { props: ['ingredients'] }; </script>Copy the code

In React, you can use the js built-in arrap. map to implement array traversal.

export default function Recipe({ ingredients }) {
  return (
    <ul>
      {ingredients.map((ingredient, index) => (
        <li key={index}>{ingredient}</li>
      ))}
    </ul>
  );
}
Copy the code

Iterating over objects is a bit tricky, but Vue still uses the V-for directive to get the property key and value on the object.

<! -- KeyValueList.vue --> <template> <ul> <li v-for="(value, key) in object" :key="key"> {{ key }}: {{ value }} </li> </ul> </template> <script> export default { props: ['object'] // E.g. { a: 'Foo', b: 'Bar' } }; </script>Copy the code

I like to use the Object.entries method in React to get the contents of objects.

export default function KeyValueList({ object }) {
  return (
    <ul>
      {Object.entries(object).map(([key, value]) => (
        <li key={key}>{value}</li>
      ))}
    </ul>
  );
}
Copy the code

CSS: Class and style binding

React option: pass props manually.

Vue automatically binds the component’s element class and style

<! -- Post.vue --> <template> <article> <h1>{{ title }}</h1> </article> </template> <script> export default { props: ['title'], }; </script> <! -- <post :title="About CSS" class="margin-bottom" style="color: red" /> -->Copy the code

React requires you to manually pass the className and style properties. Note that style in React must be an object and does not support strings.

export default function Post({ title, className, style }) {
  return (
    <article className={className} style={style}>
      {title}
    </article>
  );
}

{/* 
       */}
Copy the code

If you want to pass all attributes, you can use… The operator

export default function Post({ title, ... props }) {
  return (
    <article {. props} >
      {title}
    </article>
  );
}
Copy the code

If you miss the class API in Vue, take a look at the library classnames

Props

React option: Props

React Props behaves much like Vue. One minor difference: The React component does not inherit Unknown properties.

<! -- Post.vue --> <template> <h1>{{ title }}</h1> </template> <script> export default { props: ['title'], }; </script>Copy the code

react

export default function Post({ title }) {
  return <h3>{title}</h3>;
}
Copy the code

Vue uses props, which can use the: prefix, which is an alias for the V-bind directive. React uses {} as dynamic values

<! -- Post.vue --> <template> <post-title :title="title" /> </template> <script> export default { props: ['title'], }; </script>Copy the code

react

export default function Post({ title }) {
  return <PostTitle title={title} />;
}
Copy the code

Data from the Data

basis

React option: useState hook

Data in Vue is used to store the state of local components.

<! -- ButtonCounter.vue --> <template> <button @click="count++"> You clicked me {{ count }} times. </button> </template> <script> export default { data() { return { count: 0 } } }; </script>Copy the code

React provides a useState hook that returns an array of two elements corresponding to the current state and setting method.

import { useState } from 'react';

export default function ButtonCounter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={()= > setCount(count + 1)}>
      {count}
    </button>
  );
}
Copy the code

If you want to assign multiple states, you can do so in two ways:

Use multiple useState, multiple calls.

import { useState } from 'react';

export default function ProfileForm() {
  const [name, setName] = useState('Sebastian');
  const [email, setEmail] = useState('[email protected]');

  // ...
}
Copy the code

Or use an object.

import { useState } from 'react';

export default function ProfileForm() {
  const [values, setValues] = useState({
    name: 'Sebastian'.email: '[email protected]'
  });

  // ...
}
Copy the code

v-model

V-model is an instruction in Vue that passes value and listens for input events. This looks like a two-way bindling for Vue, but the principle behind it is still to pass props and receive events — props Down,events up

<! -- Profile.vue --> <template> <input type="text" v-model="name" /> </template> <script> export default { data() { return  { name: 'Sebastian' } } }; </script>Copy the code

The V-Model expansion looks like this:

<template>
  <input
    type="text"
    :value="name"
    @input="name = $event.target.value"
  />
</template>
Copy the code

React doesn’t have a V-model, you need to always do this:

import { useState } from 'react';

export default function Profile() {
  const [name, setName] = useState('Sebastian');

  return (
    <input
      type="text"
      value={name}
      onChange={event= > setName(event.target.name)}
    />
  );
}
Copy the code

Calculate properties Computed Properties

React options: Variable Variable, optional packaging useMemo

Vue uses computed attributes for two reasons:

  • Avoid Mixing logical Mixing logic and tags in HTML
  • Caches complex computation operations in a component instance

If you do not use computed attributes:

<! -- ReversedMessage.vue --> <template> <p>{{ message.split('').reverse().join('') }}</p> </template> <script> export default { props: ['message'] }; </script>Copy the code

React

export default function ReversedMessage({ message }) {
  return <p>{message.split('').reverse().join('')}</p>;
}
Copy the code

With React, you can extract the result of a calculation from the template as a variable.

<! -- ReversedMessage.vue --> <template> <p>{{ reversedMessage }}</p> </template> <script> export default { props: ['message'], computed: { reversedMessage() { return this.message.split('').reverse().join(''); }}}; </script>Copy the code

react

export default function ReversedMessage({ message }) {
  const reversedMessage = message.split(' ').reverse().join(' ');

  return <p>{reversedMessage}</p>;
}
Copy the code

If performance is a concern, the calculation process can use the useMemo hook, which requires: a callback to return the calculation result, an array dependency.

In the following example, the reverseMessage is recalculated after a dependency message changes.

import { useMemo } from 'react';

export default function ReversedMessage({ message }) {
  const reversedMessage = useMemo(() = > {
    return message.split(' ').reverse().join(' ');
  }, [message]);

  return <p>{reversedMessage}</p>;
}
Copy the code

Methods the Methods

React alternatives: Function

Vue has a Methods option that declares the methods used by the component.

<! -- ImportantButton.vue --> <template> <button onClick="doSomething"> Do something! </button> </template> <script> export default { methods: { doSomething() { // ... }}}; </script>Copy the code

React allows you to declare a simple function inside a component.

export default function ImportantButton() {
  function doSomething() {
    // ...
  }

  return (
    <button onClick={doSomething}>
      Do something!
    </button>
  );
}
Copy the code

Events Events

React alternatives: callback props

An event is essentially a callback to something that a child component fires. Vue treats events as first-class citizens, so you can use V-on or @ to listen:

<! -- PostForm.vue --> <template> <form> <button type="button" @click="$emit('save')"> Save </button> <button type="button"  @click="$emit('publish')"> Publish </button> </form> </template>Copy the code

React events have no special meaning; they are just props callbacks for child components.

export default function PostForm({ onSave, onPublish }) {
  return (
    <form>
      <button type="button" onClick={onSave}>
        Save
      </button>
      <button type="button" onClick={onPublish}>
        Publish
      </button>
    </form>
  );
}
Copy the code

Events to modify

React: Consider HOC higher-order components

Vue has the prevent Stop modifier to change the behavior of the event without modifying the processing logic.

<! -- AjaxForm.vue --> <template> <form @submit.prevent="submitWithAjax"> <! -... --> </form> </template> <script> export default { methods: { submitWithAjax() { // ... }}}; </script>Copy the code

React has no modifier syntax. Blocking default events and stopping bubbling are usually handled in callbacks.

export default function AjaxForm() {
  function submitWithAjax(event) {
    event.preventDefault();
    // ...
  }

  return (
    <form onSubmit={submitWithAjax}>{/ *... * /}</form>
  );
}
Copy the code

If you really want to have something like modifiers, you can consider HOC, a higher-order component

function prevent(callback) {
  return (event) = > {
      event.preventDefault();
      callback(event);
  };
}

export default function AjaxForm() {
  function submitWithAjax(event) {
    // ...
  }

  return (
    <form onSubmit={prevent(submitWithAjax)}>{/ *... * /}</form>
  );
}
Copy the code

Lifecycle methods

React: useEffect hook

In the Class Component, React and Vue both have very similar lifecycle apis. Most lifecycle related issues can be resolved using useEffect hooks. The two are completely different ideas, so it is difficult to compare them. This section only uses a few examples to illustrate the effect.

Lifecycle methods are commonly used to create and destroy third-party libraries.

<template>
  <input type="text" ref="input" />
</template>

<script>
import DateTimePicker from 'awesome-date-time-picker';

export default {
  mounted() {
   this.dateTimePickerInstance =
     new DateTimePicker(this.$refs.input);
  },

  beforeDestroy() {
    this.dateTimePickerInstance.destroy();
  }
};
</script>
Copy the code

React Uses useEffect, you can declare a side effect that needs to run after rendering. Callbacks using useEffect are triggered after completion. In this case, the side effects occur after the component is destroyed.

import { useEffect, useRef } from 'react';
import DateTimePicker from 'awesome-date-time-picker';

export default function Component() {
  const dateTimePickerRef = useRef();

  useEffect(() = > {
    const dateTimePickerInstance =
      new DateTimePicker(dateTimePickerRef.current);

    return () = >{ dateTimePickerInstance.destroy(); }; } []);return <input type="text" ref={dateTimePickerRef} />;
}
Copy the code

This is similar to Vue registering a beforeDestroy listener in Mounted.

<script> export default { mounted() { const dateTimePicker = new DateTimePicker(this.$refs.input); this.$once('hook:beforeDestroy', () => { dateTimePicker.destroy(); }); }}; </script>Copy the code

UseEffect, like useMemo, takes a dependency array as its second argument.

If the dependency is not set, it is triggered after each render and cleared before the next. This function is similar to Vue’s Mounted updated beforeUpdate beforeDestroy.

useEffect(() = > {
    // Happens after every render

    return () = > {
        // Optional; Clean up before next render Optional, clear before next render
    };
});
Copy the code

If you specify no dependencies, the trigger condition is only triggered when the component is first rendered, because it has no trigger condition. This function is similar to Vue’s Moutned and beforeDestroyed.

useEffect(() = > {
    // Happens on mount

    return () = > {
        // Optional; clean up before unmount}; } []);Copy the code

If a dependency is executed, it is fired after the dependency changes. We will review this in a later episode on Watchers.

const [count, setCount] = useState(0);

useEffect(() = > {
    // Happens when `count` changes

    return () = > {
        // Optional; clean up when `count` changed
    };
}, [count]);
Copy the code

Do not consider converting all lifecycle methods to UseEffects. Better to restate a set of side effects.

As Ryan Florence noted:

The question is not, “When will the side effect be triggered?” but, “What state does this effect synchronize with?”

useEffect(fn) // all state

useEffect(fn, []) // no state

useEffect(fn, [these, states])

by @ryanflorence on Twitter

The listener, which

React: useEfeect hook

Watcher is conceptually similar to the lifecycle approach: when X happens, do Y. React doesn’t have Watcher, but you can use useEffect to achieve the same effect.

<! -- AjaxToggle.vue --> <template> <input type="checkbox" v-model="checked" /> </template> <script> export default { data() { return { checked: false } }, watch: { checked(checked) { syncWithServer(checked); } }, methods: { syncWithServer(checked) { // ... }}}; </script>Copy the code

React

import { useEffect, useState } from 'react';

export default function AjaxToggle() {
  const [checked, setChecked] = useState(false);

  function syncWithServer(checked) {
    // ...
  }

  useEffect(() = > {
    syncWithServer(checked);
  }, [checked]);

  return (
    <input
      type="checkbox"
      checked={checked}
      onChange={()= >setChecked(! checked)} />
  );
}
Copy the code

Note that useEffect is triggered on the first rendering, just as Vue sets the immediate option in Watcher.

If you don’t want to fire on the first render, you can create a ref variable to control whether the first render fires. (Set a flag)

import { useEffect, useRef, useState } from 'react';

export default function AjaxToggle() {
  const [checked, setChecked] = useState(false);
  const firstRender = useRef(true);

  function syncWithServer(checked) {
    // ...
  }

  useEffect(() = > {
    if (firstRender.current) {
      firstRender.current = false;
      return;
    }
    syncWithServer(checked);
  }, [checked]);

  return (
    <input
      type="checkbox"
      checked={checked}
      onChange={()= >setChecked(! checked)} />
  );
}
Copy the code

Slots and scopes Slots & Scoped Slots

React: JSX props or render props

If you render another template inside a component, React uses the children property.

Vue can declare a
to represent the contents inside. React You can render children.

<! -- RedParagraph.vue --> <template> <p style="color: red"> <slot /> </p> </template>Copy the code

React

export default function RedParagraph({ children }) {
  return (
    <p style={{ color: 'red' }}>
      {children}
    </p>
  );
}
Copy the code

React slots are props, we don’t need to declare them in the template, we just need to receive JSX props and render them in render.

<! -- Layout.vue --> <template> <div class="flex"> <section class="w-1/3"> <slot name="sidebar" /> </section> <main class="flex-1"> <slot /> </main> </div> </template> <! -- In use: --> <layout> <template #sidebar> <nav>... </nav> </template> <template #default> <post>... </post> </template> </layout>Copy the code

React

export default function RedParagraph({ sidebar, children }) {
  return (
    <div className="flex">
      <section className="w-1/3">
        {sidebar}
      </section>
      <main className="flex-1">
        {children}
      </main>
    </div>
  );
}

// In use:

return (
  <Layout sidebar={<nav>.</nav>} ><Post>.</Post>
  </Layout>
);
Copy the code

Vue has scoped slots, which pass data to the slot to be rendered. Key point: to render

Conventional slots are rendered before the parent component renders. The parent component then decides what to do with the rendered fragment.

Scoped slots cannot be rendered before the parent because they rely on data passed by the parent, so scoped slots are delayed.

Deferring doing something in JS is simple: wrap function around it and call it when needed. If you need to use scope slots in React, just pass in a function that handles rendering.

We can also use Chidren, or some other prop

<! -- CurrentUser.vue --> <template> <span> <slot :user="user" /> </span> </template> <script> export default { inject: ['user'] }; </script> <! -- In use: --> <template> <current-user> <template #default="{ user }"> {{ user.firstName }} </template> </current-user> </template>Copy the code

React

import { useContext } from 'react';
import UserContext from './UserContext';

export default function CurrentUser({ children }) {
  const { user } = useContext(UserContext);

  return (
    <span>
      {children(user)}
    </span>
  );
}

// In use:

return (
  <CurrentUser>
    {user => user.firstName}
  </CurrentUser>
);
Copy the code

Provide/inject

React: createContext and useContext hook

Provide /inject allows a component to share state with all its children. React has the same property: context

<! -- MyProvider.vue --> <template> <div><slot /></div> </template> <script> export default { provide: { foo: 'bar' }, }; </script> <! -- Must be rendered inside a MyProvider instance: --> <template> <p>{{ foo }}</p> </template> <script> export default { inject: ['foo'] }; </script>Copy the code

React

import { createContext, useContext } from 'react';

const fooContext = createContext('foo');

function MyProvider({ children }) {
  return (
    <FooContext.Provider value="foo">
      {children}
    </FooContext.Provider>
  );
}

// Must be rendered inside a MyProvider instance:

function MyConsumer() {
  const foo = useContext(FooContext);

  return <p>{foo}</p>;
}
Copy the code

Custom directives

React instead: Components

React has no instructions. But many problems solved by instructions can be solved by components.

<div v-tooltip="Hello!" > <p>... </p> </div>Copy the code

React

return (
  <Tooltip text="Hello">
    <div>
      <p>.</p>
    </div>
  </Tooltip>
);
Copy the code

Transitions

React alternatives: third-party libraries.

React doesn’t have a built-in transition tool. Consider using react-transition-groups, which do not provide animations but can be choreographed by class names.

If you want a library that provides more help, consider The React pose for web has been deprecated by Framer Motion.

To the end.