ng vue

 

For the past three years, I’ve been using both React and Vue in different projects, ranging from smaller websites to large scale apps.

Last month I wrote a post about why I prefer React over Vue. Shortly after I joined Adam Wathan on Full Stack Radio to talk about React from a Vue developer’s perspective.

We covered a lot of ground on the podcast, but most things we talked about could benefit from some code snippets to illustrate their similaraties and differences.

This post is a succinct rundown of most Vue features, and how I would write them with React in 2019 with hooks.

Did I miss anything? Are there other comparisons you’d like to see? Or do you just want to share your thoughts about Vue or React? Talk to me on Twitter!

Table of contents

Templates

React alternative: JSX

Vue uses HTML strings with some custom directives for templating. They recommend using .vue files to seperate the templates and script (and optionally styles).

<!-- Greeter.vue -->

<template>
  <p>Hello, {{ name }}!</p>
</template>

<script>
export default {
  props: ['name']
};
</script>

React uses JSX, which is an extension of ECMAScript.

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

CONDITIONAL RENDERING

React alternative: Logical && operator, ternary statements, or early returns

Vue uses v-if, v-else and v-else-if directives to conditionally render parts of a template.

<!-- Awesome.vue -->

<template>
  <article>
    <h1 v-if="awesome">Vue is awesome!</h1>
  </article>
</template>

<script>
export default {
  props: ['awesome']
};
</script>

React doesn’t support directives, so you need to use the language to conditionally return parts of a template.

The && operator provides a succinct way to write an if statement.

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

If you need an else clause, use a ternary statement instead.

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

You could also opt to keep the two branches completely separated, and use an early return instead.

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

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

LIST RENDERING

React alternative: Array.map

Vue uses the v-for directive to loop over 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>

With React, you can “map” the array to a set of elements using the built in Array.map function.

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

Iterating objects is a bit trickier. Vue allows you to use the same v-for directive for keys & values.

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

I like to use the built in Object.entries function with React to iterate over objects.

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

CLASS AND STYLE BINDINGS

React alternative: Manually pass props

Vue automatically binds class and style props to the outer HTML element of a component.

<!-- 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"
/>
-->

With React, you need to manually pass className and style props. Note that style must be an object with React, strings are not supported.

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

{/* <Post
  title="About CSS"
  className="margin-bottom"
  style={{ color: 'red' }}
/> */}

If you want to pass down all remaining props, the object rest spread operator comes in handy.

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

If you miss Vue’s excellent class API, look into Jed Watson’s classnames library.

Props

React alternative: Props

Props behave pretty much the same way in React as Vue. One minor difference: React components won’t inherit unknown attributes.

<!-- Post.vue -->

<template>
  <h1>{{ title }}</h1>
</template>

<script>
export default {
  props: ['title'],
};
</script>
export default function Post({ title }) {
  return <h3>{title}</h3>;
}

Using expressions as props in Vue is possible with a : prefix, which is an alias for the v-bind directive. React uses curly braces for dynamic values.

<!-- Post.vue -->

<template>
  <post-title :title="title" />
</template>

<script>
export default {
  props: ['title'],
};
</script>
export default function Post({ title }) {
  return <PostTitle title={title} />;
}

Data

React alternative: The useState hook

In Vue the data option is used to store local component state.

<!-- ButtonCounter.vue -->

<template>
  <button @click="count++">
    You clicked me {{ count }} times.
  </button>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
};
</script>

React exposes a useState hook which returns a two-element array containing the current state value and a setter function.

import { useState } from 'react';

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

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

You can choose whether you prefer to distribute state between multiple useState calls, or keep it in a single object.

import { useState } from 'react';

export default function ProfileForm() {
  const [name, setName] = useState('Sebastian');
  const [email, setEmail] = useState('sebastian@spatie.be');

  // ...
}
import { useState } from 'react';

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

  // ...
}

V-MODEL

v-model is a convenient Vue directive that combines passing down a value prop with listening to an input event. This makes it look like Vue does two-way binding, while it’s still just “props down, events up” under the hood.

<!-- Profile.vue -->

<template>
  <input type="text" v-model="name" />
</template>

<script>
export default {
  data() {
    return {
      name: 'Sebastian'
    }
  }
};
</script>

Vue expands the v-model directive to the following:

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

React doesn’t have a v-model equivalent. You always need to be explicit:

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)}
    />
  );
}

Computed properties

React alternative: Variables, optionally wrapped in useMemo

Vue has computed properties for two reasons: to avoid mixing logic and markup in templates, and to cache complex computations in a component instance.

Without computed properties:

<!-- ReversedMessage.vue -->

<template>
  <p>{{ message.split('').reverse().join('') }}</p>
</template>

<script>
export default {
  props: ['message']
};
</script>
export default function ReversedMessage({ message }) {
  return <p>{message.split('').reverse().join('')}</p>;
}

With React, you can extract the computation from the template by assigning the result to a variable.

<!-- ReversedMessage.vue -->

<template>
  <p>{{ reversedMessage }}</p>
</template>

<script>
export default {
  props: ['message'],

  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('');
    }
  }
};
</script>
export default function ReversedMessage({ message }) {
  const reversedMessage = message.split('').reverse().join('');

  return <p>{reversedMessage}</p>;
}

If performance is a concern, the computation can be wrapped in a useMemo hook. useMemo requires a callback that returns a computed result, and an array of dependencies.

In the following example, reversedMessage will only be recomputed if the message dependency changes.

import { useMemo } from 'react';

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

  return <p>{reversedMessage}</p>;

}

 

منبع : React for Vue developers — Sebastian De Deyne


نظرات کاربران

0 نظر



نام شما

عنوان نظر شما
متن نظر شما (اجباری)



Web hosting by Somee.com