preface

In this era of roll god, just proficient Vue chubbyhead fish, has been beaten many times, was questioned in the interview: “You can’t React?” I was at a loss for words.

This article tries to some common features in Vue is realized in the React again, if you happen to be a Vue turned the React, or turn the React Vue, expecting some help to you.

If you are a student familiar with React and Vue, kneel for light spray (manual survival)

For each function, there are Vue and React versions, as well as screenshots and screenshots

Vue warehouse

The React warehouse

1. v-if

We’ll start with the most common form of show hiding. In Vue, show hiding of an element is usually handled with the V-if or V-show command, except that v-if is “true” conditional rendering, and event listeners and subcomponents within the conditional block are destroyed and rebuilt appropriately during the switch. V-show is simple, just CSS styling control.

V-if source code point here

Vue

<template>
  <div class="v-if">
    <button @click="onToggleShow">switch</button>
    <div v-if="isShow">The front bighead is showing</div>
  </div>
</template>

<script>
export default {
  name: 'vif',
  data () {
    return {
      isShow: true}},methods: {
    onToggleShow () {
      this.isShow = !this.isShow
    }
  }
}
</script>
Copy the code

React

Vif source code point here

import React, { useState } from "react"

export default function Vif (){
  const [ isShow, setIsShow ] = useState(true)
  const onToggleShow = () = >{ setIsShow(! isShow) }return (
    <div className="v-if">
      <button onClick={ onToggleShow} >switch</button>{/* Can also be used with the trinary operator */} {/* {isShow?<div>The front bighead is showing</div> : null } */}
      {
        isShow && <div>The front bighead is showing</div>
      }
    </div>)}Copy the code

preview

2. v-show

Again, this time we’ll use V-show to show the hidden functionality and watch the DOM style change

Note: The reason why the display is not set to block is because some elements are not block-level elements themselves, and if you force the display to block, you may end up with the wrong style.

Vue

V-show source click here

<template>
  <div class="v-show">
    <button @click="onToggleShow">switch</button>
    <div v-show="isShow">The front bighead is showing</div>
  </div>
</template>

<script>
export default {
  name: 'vshow',
  data () {
    return {
      isShow: true}},methods: {
    onToggleShow () {
      this.isShow = !this.isShow
    }
  }
}
</script>


Copy the code

React

VShow source code point here

import React, { useState } from "react"

export default function VShow (){
  const [ isShow, setIsShow ] = useState(true)
  const onToggleShow = () = >{ setIsShow(! isShow) }return (
    <div className="v-show">
      <button onClick={ onToggleShow} >switch</button>
      {
        <div style={{ display: isShow? ' ': 'none' }}>The front bighead is showing</div>
      }
    </div>)}Copy the code

preview

3. v-for

In general, rendering a list in Vue uses the V-for instruction, which requires a special syntax in the form of item in Items, where items are the source data array and item is the alias of the array element being iterated over. Of course, each element needs a unique key

Vue

V-for source code point here

<template>
  <div class="v-for">
    <div 
      class="v-for-item"
      v-for="item in list"
      :key="item.id"
    >
      {{ item.name }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'vfor',
  data () {
    return {
      list: [{id: 1.name: 'front end'}, {id: 2.name: 'back-end'}, {id: 3.name: 'android'}, {id: 4.name: 'ios',},]}}}</script>

Copy the code

React

React does not have a V-for directive, so we can use map traversal to implement similar functions

VFor source code point here

import React, { useState } from "react"

export default function VFor (){
  const [ list, setList ] = useState([
    {
      id: 1.name: 'front end'}, {id: 2.name: 'back-end'}, {id: 3.name: 'android'}, {id: 4.name: 'ios',}])return (
    <div className="v-for">
      {
        list.map((item) => {
          return <div className="v-for-item" key={ item.id} >{ item.name }</div>})}</div>)}Copy the code

preview

4. computed

When a variable needs to be evaluated by other variables, it is very convenient to use computed attributes, and the computed attributes of Vue are cached based on their responsive dependencies. The dependency values are not changed and will not be recalculated, thus achieving the function of cache.

Let’s look at a simple addition example: num3 is the sum of num1 and num2, and num1 is incremented by 10 with each click of the button

Vue

Computed source code point here

<template>
  <div class="computed">
    <button @click="onAdd">+ 10</button>
    <div>{{num3}}</div>
  </div>
</template>

<script>
export default {
  name: 'computed',
  data () {
    return {
      num1: 10.num2: 10,}},computed: {
    num3 () {
      return this.num1 + this.num2
    }
  },
  methods: {
    onAdd () {
      this.num1 += 10}}}</script>

Copy the code

React

React has no calculated properties, but we can do this with the useMemo hook. Unlike Vue computed, we have to maintain dependencies manually

Computed source code point here

import React, { useMemo, useState } from "react"

export default function Computed (){
  const [ num1, setNum1 ] = useState(10)
  const [ num2, setNum2 ] = useState(10)

  const num3 = useMemo((a, b) = > {
    return num1 + num2
  }, [ num1, num2 ])

  const onAdd = () = > {
    setNum1(num1 + 10)}return (
    <div className="computed">
      <button onClick={ onAdd} >+ 10</button>
      <div>Results: {num3}</div>
    </div>)}Copy the code

preview

5. watch

Watch can be used in Vue when we need to listen for data changes and perform asynchronous or expensive operations

Let’s simulate such a scenario and implement it through Watch: select boy or girl, send the request after it is selected, and display the request result. (Here setTimeout is used to simulate the asynchronous request process)

Vue

Watch source code here

<template>
  <div class="watch">
    <div class="selects">
      <button 
        v-for="(item, i) in selects"
        :key="i"
        @click="onSelect(item)"
      >
        {{ item }}
      </button>
    </div>
    <div class="result">
      {{ result }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'watch',
  data () {
    return {
      fetching: false.selects: [
        'boy'.'girl'].selectValue: ' '}},computed: {
    result () {
      return this.fetching ? 'On request' : 'Request result: selectedThe ${this.selectValue || '~'}`}},watch: {
    selectValue () {
      this.fetch()
    }
  },
  methods: {
    onSelect (value) {
      this.selectValue = value  
    },
    fetch () {
      if (!this.fetching) {
        this.fetching = true

        setTimeout(() = > {
          this.fetching = false
        }, 1000)}}}}</script>
Copy the code

React

In React, you can use useEffect to listen for data changes and respond

Watch source code here

import React, { useState, useMemo, useEffect } from "react"
import './watch.css'

export default function Watch() {
  const [fetching, setFetching] = useState(false)
  const [selects, setSelects] = useState([
    'boy'.'girl'
  ])
  const [selectValue, setSelectValue] = useState(' ')

  const result = useMemo(() = > {
    return fetching ? 'On request' : 'Request result: selected${selectValue || '~'}`
  }, [ fetching ])

  const onSelect = (value) = > {
    setSelectValue(value)
  }
  const fetch = () = > {
    if(! fetching) { setFetching(true)

      setTimeout(() = > {
        setFetching(false)},1000)
    }
  }

  useEffect(() = > {
    fetch()
  }, [ selectValue ])

  return (
    <div className="watch">
      <div className="selects">
        {
          selects.map((item, i) => {
            return <button key={ i } onClick={() = > onSelect(item) }>{ item }</button>})}</div>
      <div className="result">
        { result }
      </div>
    </div>)}Copy the code

preview

6. style

Sometimes it is unavoidable to add styles to elements dynamically. Both Vue and React provide convenient ways to use them.

Basically the same in use:

Similarities:

CSS property names can be either camelCase or kebab-case delimited (remember to use quotes)

Difference:

  1. Vue can bind multiple style objects using array syntax. React is primarily a single object (Vue can do this too)
  2. React automatically adds the suffix “px” (which Vue does not automatically handle) to properties with numeric inline styles. Other units need to be specified manually
  3. The React style does not automatically complete prefixes. To support older browsers, you need to manually add the corresponding style attributes. Vue automatically detects when v-bind:style uses CSS properties that require a browser engine prefix, such as transform, vue.js.

Vue

Style source code point here

<template>
  <div class="style" :style="[ style, style2 ]"></div>
</template>

<script>
export default {
  name: 'style',
  data () {
    return {
      style: {
        width: '100%'.height: '500px',},style2: {
        backgroundImage: 'linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%)'.borderRadius: '10px',}}}}</script>
Copy the code

React

Style source code point here

import React from "react"

export default function Style (){
  const style = {
    width: '100%'.height: '500px',}const style2 = {
    backgroundImage: 'linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%)'.borderRadius: '10px',}return (
    <div className="style" style={ { . style.. style2}} ></div>)}Copy the code

preview

7. class

How do I dynamically add classes to elements? In Vue, I prefer to use the array syntax (and of course the object syntax). React also uses third-party packages such as classNames to add classes more easily.

Now let’s see, how do we do this without using any library

Vue

Class source code here

<template>
  <button :class="buttonClasses" @click="onClickActive">{{ buttonText }}</button>
</template>

<script>
export default {
  name: 'class',
  data () {
    return {
      isActive: false,}},computed: {
    buttonText () {
      return this.isActive ? 'Selected' : 'Not selected'
    },
    buttonClasses () {
	  // Maintain a class dynamic list as an array
      return [ 'button'.this.isActive ? 'active' : ' ']}},methods: {
    onClickActive () {
      this.isActive = !this.isActive
    }
  }
}
</script>

<style scoped>
.button{
  display: block;
  width: 100px;
  height: 30px;
  line-height: 30px;
  border-radius: 6px;
  margin: 0 auto;
  padding: 0;
  border: none;
  text-align: center;
  background-color: #efefef;
}

.active{
  background-image: linear-gradient(120deg.#84fab0 0%.#8fd3f4 100%);
  color: #fff
}

</style>
Copy the code

React

Class source code here

import React, { useMemo, useState } from "react"

import './class.css' // The style is the same as above


export default function Class (){
  const [ isActive, setIsActive ] = useState(false)
  const buttonText = useMemo(() = > {
    return isActive ? 'Selected' : 'Not selected'
  }, [ isActive ])
  const buttonClass = useMemo(() = > {
    // Different from Vue, we need to join manually and change to 'button active' form
    return [ 'button', isActive ? 'active' : ' ' ].join(' ')
  }, [ isActive ])

  const onClickActive = () = >{ setIsActive(! isActive) }return (
    <div className={ buttonClass } onClick={onClickActive}>{ buttonText }</div>)}Copy the code

preview

8.provide/inject

Vue and React have their own solutions for managing global state, such as Vuex for Vue, Redux for React and Mobx for Mobx.

Provide /inject can be used in Vue

React allows you to use Context

If there is a global variable named userInfo, it needs to be easily accessed in various components. How to implement this variable in Vue and React?

Vue

Vue provides the provide/inject top-level state to any child node, assuming we declare a userInfo data in app. Vue

Provide the source code point here

app.vue


<template>
  <div id="app">
    <div class="title">I'm Vue Chestnut</div>
    <router-view/>
  </div>
</template>
<script>

export default {
  name: 'app'.// Declare data
  provide () {
    return {
      userInfo: {
        name: 'Front End Bighead'}}}}</script>
Copy the code

provide.vue

<template>
  <div class="provide-inject">{{ userInfo.name }}</div>
</template>

<script>
export default {
  name: 'provideInject'.// Use data
  inject: [ 'userInfo']}</script>

Copy the code

React

React implements a similar function by using Context to share the global state with any child node

Provide the source code point here

context/index.js

import { createContext } from "react";

export const UserInfoContext = createContext({
  userInfo: {
    name: ' '}})Copy the code

app.js

import { UserInfoContext } from './context/index'

function App() {
  return (
    <BrowserRouter>// Notice here<UserInfoContext.Provider
        value={{ userInfo: { name:'Head bighead'}}} >
        <div className="title">I'm React Chestnut</div>
        <Routes>
          <Route path="/v-if" element={<Vif />} / ><Route path="/v-show" element={<VShow />} / ><Route path="/v-for" element={<VFor />} / ><Route path="/computed" element={<Computed />} / ><Route path="/watch" element={<Watch />} / ><Route path="/style" element={<Style />} / ><Route path="/class" element={<Class />} / ><Route path="/slot" element={<Slot />} / ><Route path="/nameSlot" element={<NameSlot />} / ><Route path="/scopeSlot" element={<ScopeSlot />} / ><Route path="/provide" element={<Provide />} / ></Routes>
      </UserInfoContext.Provider>
    </BrowserRouter>
  );
}
Copy the code

provide.js

import React, { useContext } from "react"
import { UserInfoContext } from '.. /context/index'


export default function Provide() {
  // With userContext, use the defined UserInfoContext
  const { userInfo } = useContext(UserInfoContext)

  return (
    <div class="provide-inject">{ userInfo.name }</div>)}Copy the code

preview

9. Slot (default)

Slots are a very useful feature in React. Slots can be divided into default, named, and scope slots, waiting for you to fill them in from the outside.

Suppose we wanted to implement a simple Dialog component with a title that could be passed as a string and a content section that could be completely customizable.

Vue

Slot source point here

dialog

<template>
  <div class="dialog" v-show="visible">
    <div class="dialog-mask" @click="onHide"></div>
    <div class="dialog-body">
      <div class="dialog-title" v-if="title">{{ title }}</div>
      <div class="dialog-main">// Note that there is a default slot pit<slot></slot>
      </div>
      <div class="dialog-footer">
        <div class="button-cancel" @click="onHide">cancel</div>
        <div class="button-confirm" @click="onHide">determine</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "dialog".props: {
    title: {
      type: String.default: "",},visible: {
      type: Boolean.default: false,}},methods: {
    onHide () {
      this.$emit('update:visible'.false)}}};</script>

Copy the code

slot

<template>
  <div class="slot">
    <button @click="onToggleVisible">To switch the dialog</button>
    <Dialog
      :visible.sync="visible"
      title="Default slot"
    >// This will be replaced by<slot></slot>The location of<div class="slot-body">Front end bighead fish</div>
    </Dialog>
  </div>
</template>

<script>
import Dialog from './components/dialog.vue'

export default {
  name: 'slot'.components: {
    Dialog,
  },
  data () {
    return {
      visible: false}},methods: {
    onToggleVisible () {
      this.visible = !this.visible
    }
  }
}

Copy the code

React

How do you implement the same functionality in React? React doesn’t have a slot! React doesn’t have any slots, but you can use props. Children to get the internal child elements of the component, and use this to implement the default slots

Slot source point here

Dialog

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

import './dialog.css'

export default function Dialog(props) {
  // Forgive me for implementing visible-1 the silly way first, that's not the point
  const { children, title = ' ', visible = -1 } = props
  const [visibleInner, setVisibleInner] = useState(false)

  const onHide = () = > {
    setVisibleInner(false)
  }

  useEffect(() = > {
    setVisibleInner(visible > 0)
  }, [ visible ])

  return( <div className="dialog" style={ { display: visibleInner ? 'block' : 'none' }}> <div className="dialog-mask" onClick={ onHide }></div> <div className="dialog-body"> { title ? <div className="dialog-title">{ title }</div> : Null} <div className="dialog-main"> {/* } {children} </div> <div className="dialog-footer"> <div className="button-cancel" onClick={onHide </div> </div> </div> </div>Copy the code

slot

import React, { useState, useEffect } from "react"
import Dialog from './components/dialog'

export default function Slot() {
  const [visible, setVisible] = useState(-1)

  const onToggleVisible = () = > {
    setVisible(Math.random())
  }

  return (
    <div className="slot">
      <button onClick={ onToggleVisible} >To switch the dialog</button>
      <Dialog
        visible={visible}
        title="Default slot"
      >{/* Notice that this is read and replaced by the children of the Dialog component */}<div className="slot-body">Front end bighead fish</div>
      </Dialog>
    </div>)}Copy the code

preview

10. Name slot

When a component has multiple dynamic contents inside that need to be filled externally, a default slot is no longer sufficient, and we need to give the slot a name so that the externally can “step by step” into the specified location.

Let’s enrich the Dialog component. What if title also supports dynamic content transfer?

Vue

Vue uses
to declare slots, and then uses v-slot:main to fill a slot

NameSlot source code point here

Dialog transformation

<template>
  <div class="dialog" v-show="visible">
    <div class="dialog-mask" @click="onHide"></div>
    <div class="dialog-body">
      <div class="dialog-title" v-if="title">{{ title }}</div>
      <! -- notice that the title attribute is not passed, and the content is carried through the slot -->
      <slot name="title" v-else></slot>
      <div class="dialog-main">
        <! -- Declare main section -->
        <slot name="main"></slot>
      </div>
      <div class="dialog-footer">
        <div class="button-cancel" @click="onHide">cancel</div>
        <div class="button-confirm" @click="onHide">determine</div>
      </div>
    </div>
  </div>
</template>
/ /... The other places are the same as the interview

Copy the code

nameSlot

<template>
  <div class="slot">
    <button @click="onToggleVisible">To switch the dialog</button>
    <Dialog
      :visible.sync="visible"
    >
      <template v-slot:title>
        <div class="dialog-title">A named slot</div>
      </template>
      <template v-slot:main>
        <div class="slot-body">Front end bighead fish</div>
      </template>
    </Dialog>
  </div>
</template>

<script>
import Dialog from './components/dialog.vue'

export default {
  name: 'nameSlot'.components: {
    Dialog,
  },
  data () {
    return {
      visible: false}},methods: {
    onToggleVisible () {
      this.visible = !this.visible
    }
  }
}
</script>
Copy the code

React

The props. Children attribute can be used to read the contents of the component tag, which is the same as the default Vue slot. One of the cool things about React, I think, is that you can pass properties, strings, numbers, functions, even DOM. So it’s easy to implement named slots, just pass them as properties

NameSlot source code point here

Dialog transformation

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

import './dialog.css'

export default function Dialog(props) {
  // Forgive me for implementing visible-1 the silly way first, that's not the point
  const { title, main, visible = -1 } = props
  const [visibleInner, setVisibleInner] = useState(false)

  const onHide = () = > {
    setVisibleInner(false)
  }

  useEffect(() = > {
    setVisibleInner(visible > 0)
  }, [ visible ])

  return( <div className="dialog" style={ { display: visibleInner ? 'block' : 'none' }}> <div className="dialog-mask" onClick={ onHide }></div> <div className="dialog-body"> {/* { title ? <div className="dialog-title">{ title }</div> : } {title} <div className="dialog-main"> } {/* {children} */} {/* {children} */ <div className="button-cancel" onClick={onHide}> Cancel </div> <div className="button-confirm" onClick={onHide}> </div> </div> </div> </div>)}Copy the code

nameSlot

import React, { useState } from "react"
import Dialog from './components/dialog'

import './slot.css'

export default function NameSlot() {
  const [visible, setVisible] = useState(-1)

  const onToggleVisible = () = > {
    setVisible(Math.random())
  }

  return (
    <div className="slot">
      <button onClick={ onToggleVisible} >To switch the dialog</button>
      <Dialog
        visible={visible}// Notice that this is passed directlyDOM
        title={ <div className="dialog-title">The default slot</div>} // Notice that we pass DOM main={<div className="slot-body">Front end bighead fish</div> }
      >
      </Dialog>
    </div>)}Copy the code

preview

You can see the named slot. React uses properties instead

11. Scope slot

There are default slots, named slots and, finally, scoped slots. Sometimes it can be useful to give slot content access to data that is only available in child components, which is where scoped slots come in

What if the Dialog component has a userInfo: {name: ‘front bighead’} data object inside it that you want to access using the external slot of the Dialog component?

Vue

ScopeSlot source code point here

Dialog

<template>
  <div class="dialog" v-show="visible">
    <div class="dialog-mask" @click="onHide"></div>
    <div class="dialog-body">
      <div class="dialog-title" v-if="title">{{ title }}</div>
      <! -- Notice that this can be used externally by binding userInfo -->
      <slot name="title" :userInfo="userInfo" v-else></slot>
      <div class="dialog-main">
	    <! -- Notice that this can be used externally by binding userInfo -->
        <slot name="main" :userInfo="userInfo"></slot>
      </div>
      <div class="dialog-footer">
        <div class="button-cancel" @click="onHide">cancel</div>
        <div class="button-confirm" @click="onHide">determine</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "dialog".// ...
  data () {
    return {
      userInfo: {
        name: 'Front End Bighead'}}},// ...	
};
</script>

Copy the code

scopeSlot

<template>
  <div class="slot">
    <button @click="onToggleVisible">To switch the dialog</button>
    <Dialog
      :visible.sync="visible"
    >
      <template v-slot:title>
        <div class="dialog-title">Scope slot</div>
      </template>
      <! -- Notice here -->
      <template v-slot:main="{ userInfo }">
        <! -- Notice that userInfo is the data inside the Dialog component -->
        <div class="slot-body">Hello {{userinfo.name}}</div>
      </template>
    </Dialog>
  </div>
</template>

Copy the code

React

React allows you to pass in the DOM directly, but it also allows you to pass in functions to use userInfo data inside the Dialog component

ScopeSlot source code point here

Dialog transformation

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

import './dialog.css'

export default function Dialog(props) {
  // Forgive me for implementing visible-1 the silly way first, that's not the point
  const { title, main, visible = -1 } = props
  const [visibleInner, setVisibleInner] = useState(false)
  const [ userInfo ] = useState({
    name: 'Front End Bighead'
  })

  const onHide = () = > {
    setVisibleInner(false)
  }

  useEffect(() = > {
    setVisibleInner(visible > 0)
  }, [ visible ])

  return( <div className="dialog" style={ { display: visibleInner ? 'block' : 'none'}}> <div className="dialog-mask" onClick={onHide}></div> <div className="dialog-body"> { */} {title(userInfo)} <div className="dialog-main"> {/* */} {main(userInfo)} </div> <div className="dialog-footer"> <div className="button-cancel" onClick={onHide </div> </div> </div> </div>Copy the code

scopeSlot

import React, { useState } from "react"
import Dialog from './components/dialog'

import './slot.css'

export default function ScopeSlot() {
  const [visible, setVisible] = useState(-1)

  const onToggleVisible = () = > {
    setVisible(Math.random())
  }

  return (
    <div className="slot">
      <button onClick={ onToggleVisible} >To switch the dialog</button>
      <Dialog
        visible={visible}// Implement slots with functionstitle={() = > <div className="dialog-title">Scope slot</div>} main={(userInfo) =><div className="slot-body">Hello {userinfo.name}</div> }
      >
      </Dialog>
    </div>)}Copy the code

preview