都快2024年了,別只使用React,需要學(xué)習(xí)一下Vue,不然沒出路了
英文 | https://fatfish.medium.com/its-202-don-t-just-use-react-anymore-96f3101b7863
翻譯 | 楊小愛
最近,我的朋友因?yàn)椴皇煜?Vue.js 而未能通過面試。
她平時(shí)工作中大部分時(shí)間都在使用React,所以也懶得去了解其他前端框架。
世界上所有的前端框架我們都應(yīng)該熟悉嗎?
不,這是極其不合理的。
但為了生存,朋友還是要學(xué)習(xí)Vue的框架。
讓我們看看如何使用 Vue 來實(shí)現(xiàn) React 的一些功能。
1. v-if:如何顯示和隱藏元素?
控制元素或組件的顯示和隱藏是我們最常見的事情之一,在React中,我們經(jīng)常這樣編碼。
JavaScript 中的三元表達(dá)式和“&”也可以實(shí)現(xiàn)同樣的目標(biāo)。

React
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 }>Toggle</button>{/* Of course you can also use ternary expressions */}{/* isShow ? <div>fatfish has shown</div> : null */}{isShow && <div>fatfish has shown</div>}</div>)}
Vue
那么在Vue中如何實(shí)現(xiàn)同樣的功能呢?
是的,我們可以使用 v-if 指令來控制元素的顯示和隱藏,這很簡單,不是嗎?
<template><div class="v-if"><button @click="onToggleShow">Toggle</button><div v-if="isShow">fatfish has shown</div></div></template><script>export default {name: 'vif',data () {return {isShow: true}},methods: {onToggleShow () {this.isShow = !this.isShow}}}</script>
2. v-show:如何顯示和隱藏元素?
v-if 導(dǎo)致元素或組件被重復(fù)刪除和創(chuàng)建,如果我們只想“顯示”或“隱藏”它怎么辦?
也許我們可以借助 CSS 中的 display 屬性來做到這一點(diǎn)。

React
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 }>Toggle</button>{<div style={{ display: isShow ? '' : 'none' }}>fatfish has shown</div>}</div>)}
Vue
你可能已經(jīng)猜到了,我們可以使用 v-show 來實(shí)現(xiàn)與它相同的功能??雌饋?Vue 更簡潔,你不覺得嗎?
<template><div class="v-show"><button @click="onToggleShow">Toggle</button><div v-show="isShow">fatfish has shown</div></div></template><script>export default {name: 'vshow',data () {return {isShow: true}},methods: {onToggleShow () {this.isShow = !this.isShow}}}</script>
3. v-for:渲染列表?
在React中,我們可以使用數(shù)組的map方法來創(chuàng)建列表,這非常簡單。
看一下這段代碼,它創(chuàng)建了三個(gè)不同職業(yè)的列表。

React
import React, { useState } from "react"export default function VFor (){const [ list, setList ] = useState([{id: 1,name: 'Front end',},{id: 2,name: 'Android',},{id: 3,name: 'IOS',},])return (<div className="v-for">{list.map((item) => {return <div key={ item.id } className="v-for-item">{ item.name }</div>})}</div>)}
Vue
你喜歡 Vue 中的 v-for 指令嗎?
<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: 3,name: "Android",},{id: 4,name: "IOS",},],};},};</script>
4. 計(jì)算
如果我們想計(jì)算兩個(gè)數(shù)的和,有什么好的方法嗎?
我的意思是,當(dāng) num1 和 num2 發(fā)生變化時(shí),它們的總和會(huì)自動(dòng)變化,我們不需要手動(dòng)處理。

React
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>result:{ num3 }</div></div>)}
哇,太棒了,useMemo 幫我們解決了一個(gè)問題。
Vue
那么,Vue中有沒有更好的實(shí)現(xiàn)呢?它實(shí)際上提供了一種稱為“計(jì)算”的機(jī)制,該機(jī)制更智能且更易于使用。
<template><div class="computed"><button @click="onAdd">+10</button><div>result:{{ 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>
5.watch
當(dāng)我們需要監(jiān)聽數(shù)據(jù)變化然后執(zhí)行回調(diào)函數(shù)時(shí),可以在React中使用useEffect來完成。
讓我們嘗試模擬一個(gè)場景:
我們點(diǎn)擊男孩或女孩按鈕,選中時(shí)發(fā)送請(qǐng)求,最后顯示請(qǐng)求結(jié)果(我們通過setTimeout模擬異步請(qǐng)求過程)。

React
export default function Watch() {const [fetching, setFetching] = useState(false)const [selects, setSelects] = useState(['boy','girl'])const [selectValue, setSelectValue] = useState('')const result = useMemo(() => {return fetching ? 'requesting...' : `the result of the request: ${selectValue || '~'}`}, [ fetching ])const onSelect = (value) => {setSelectValue(value)}const fetch = () => {if (!fetching) {setFetching(true)setTimeout(() => {setFetching(false)}, 1000)}}// Pay attention hereuseEffect(() => {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>)}
Vue
別擔(dān)心,我們可以在 Vue 中做同樣的事情,你知道它的秘密是什么嗎?
是的,答案是watch。
<template><div class="watch"><div class="selects"><buttonv-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 ? 'requesting...' : `the result of the request: ${this.selectValue || '~'}`}},// Pay attention herewatch: {selectValue () {this.fetch()}},methods: {onSelect (value) {this.selectValue = value},fetch () {if (!this.fetching) {this.fetching = truesetTimeout(() => {this.fetching = false}, 1000)}}}}</script><style>button{margin: 10px;padding: 10px;}</style>
6. style
有時(shí)我們需要?jiǎng)討B(tài)地向元素添加樣式,Vue 和 React 都為我們提供了一種便捷的使用方式。
從用途上來說,它們基本上是相似的。
相同點(diǎn):
CSS 屬性名稱可以是駝峰命名法或短橫線命名法(記住將它們用引號(hào)引起來)
差異:
在Vue中,我們可以通過數(shù)組語法綁定多個(gè)樣式對(duì)象,React主要是單個(gè)對(duì)象的形式(這一點(diǎn)Vue也可以)
在React中,對(duì)于屬性的數(shù)量,它會(huì)自動(dòng)給內(nèi)聯(lián)樣式添加“px”(這一點(diǎn)Vue不會(huì)自動(dòng)處理)后綴,其他單位需要手動(dòng)指定
在 React 中,樣式不會(huì)自動(dòng)添加前綴。當(dāng) v-bind:style 使用需要瀏覽器引擎前綴的 CSS 屬性時(shí),例如 Transform,Vue.js 會(huì)自動(dòng)檢測并添加相應(yīng)的前綴。

React
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>)}
Vue
<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>
7.class
我們應(yīng)該如何動(dòng)態(tài)地為元素添加類?
在Vue中,我更喜歡使用數(shù)組語法(當(dāng)然也有對(duì)象方式),在React中也可以使用一些第三方包比如classnames來起到更方便的方式來添加類的效果。
React
import React, { useMemo, useState } from "react"import './class.css'export default function Class (){const [ isActive, setIsActive ] = useState(false)const buttonText = useMemo(() => {return isActive ? 'Selected' : 'UnSelected'}, [ isActive ])const buttonClass = useMemo(() => {return [ 'button', isActive ? 'active' : '' ].join(' ')}, [ isActive ])const onClickActive = () => {setIsActive(!isActive)}return (<div className={ buttonClass } onClick={onClickActive}>{ buttonText }</div>)}
Vue
<template><button :class="buttonClasses" @click="onClickActive">{{ buttonText }}</button></template><script>export default {name: 'class',data () {return {isActive: false,}},computed: {buttonText () {return this.isActive ? 'Selected' : 'UnSelected'},buttonClasses () {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>
8. provide/inject
Vue 和 React 對(duì)于全局狀態(tài)的管理都有自己很好的解決方案,比如 Vue 中的 Vuex、Redux 中的 React 和 Mobx,但是,當(dāng)然,這些小項(xiàng)目的引入對(duì)于小用途來說有點(diǎn)太大了,就沒有其他解決方案?
provide/inject可以在Vue中使用
React 可以使用 Context
假設(shè)我們有一個(gè)用于用戶信息的全局變量“userInfo”,需要在每個(gè)組件中輕松訪問它,如何在Vue和React中實(shí)現(xiàn)它?

React
為了在 React 中實(shí)現(xiàn)這一點(diǎn),您可以借助 Context 將全局狀態(tài)共享給任何子節(jié)點(diǎn)。
上下文/index.js
import { createContext } from "react";export const UserInfoContext = createContext({userInfo: {name: ''}})
App.js
import { UserInfoContext } from './context/index'function App() {return (<BrowserRouter>// Pay attention here<UserInfoContext.Providervalue={{ userInfo: { name: 'fatfish' } }}><div className="title">I am React App</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>);}
provide.js
import React, { useContext } from "react"import { UserInfoContext } from '../context/index'export default function Provide() {// We can use the defined "UserInfoContext" through the userContextconst { userInfo } = useContext(UserInfoContext)return (<div class="provide-inject">{ userInfo.name }</div>)}
Vue
在Vue中,我們可以使用“provide/inject”將頂級(jí)狀態(tài)傳遞給任何子節(jié)點(diǎn),假設(shè)我們在app.vue中聲明一個(gè)“userInfo”數(shù)據(jù)。
App.vue
<template><div id="app"><div class="title">I am Vue App</div><router-view/></div></template><script>export default {name: 'app',// Pay attention hereprovide () {return {userInfo: {name: 'fatfish'}}}}</script>
provide.vue
<template><div class="provide-inject">{{ userInfo.name }}</div></template><script>export default {name: 'provideInject',// Use datainject: [ 'userInfo' ]}</script>
9. Slots
假設(shè)我們要實(shí)現(xiàn)一個(gè)簡單的對(duì)話組件,基本功能是標(biāo)題可以作為字符串傳遞,內(nèi)容部分可以完全自定義,我們應(yīng)該如何實(shí)現(xiàn)呢?
React
雖然React中沒有槽的概念,但是可以通過props.children獲取組件內(nèi)部的子元素,通過這個(gè)可以實(shí)現(xiàn)默認(rèn)的槽。
Dialog.js
import React, { useState, useEffect } from "react"import './dialog.css'export default function Dialog(props) {// Forgive me for implementing this in a silly way like visible -1 first, but that's not the pointconst { children, title = '', visible = -1 } = propsconst [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">{/* Note here that the default slot function is implemented via children */}{children}</div><div className="dialog-footer"><div className="button-cancel" onClick={ onHide }>Cancel</div><div className="button-confirm" onClick={ onHide }>Confirm</div></div ></div ></div >)}
slot.js
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 }>Toggle</button><Dialogvisible={visible}title="default slot">{/* Note that here, it will be read and replaced by the children of the Dialog component */}<div className="slot-body">fatfish</div></Dialog></div>)}
Vue
同樣的功能在Vue中應(yīng)該如何實(shí)現(xiàn)呢?
dialog.vue
<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: A default slot has been placed here --><slot></slot></div><div class="dialog-footer"><div class="button-cancel" @click="onHide">Cancel</div><div class="button-confirm" @click="onHide">Confirm</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>
slot.vue
<template><div class="slot"><button @click="onToggleVisible">切換dialog</button><Dialog:visible.sync="visible"title="default slot"><!-- The <slot></slot> will be replaced here --><div class="slot-body">fatfish</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}}}
寫在最后
不管是開發(fā)語言,還是開發(fā)框架,都是我們實(shí)現(xiàn)創(chuàng)造產(chǎn)品的工具,最終,我們能夠?qū)崿F(xiàn)什么樣的效果,做出什么樣的產(chǎn)品,都是依賴實(shí)現(xiàn)它的人。
作為技術(shù)人,多學(xué)技術(shù)沒有錯(cuò),但是不能為了學(xué)習(xí)技術(shù)而學(xué)習(xí)技術(shù),我們學(xué)習(xí)技術(shù)的目的,是要解決一些問題,這個(gè)是我對(duì)學(xué)習(xí)技術(shù)的一些感悟。
當(dāng)然,也希望我今天分享的內(nèi)容,對(duì)你有用。
最后,感謝你閱讀到這里,如果你喜歡的話,請(qǐng)記得點(diǎn)贊我,關(guān)注我,你將會(huì)閱讀到更多優(yōu)質(zhì)文章。
學(xué)習(xí)更多技能
請(qǐng)點(diǎn)擊下方公眾號(hào)
![]()
