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
<! -- 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.