<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Vue 3.0 這個迷人的小妖精,到底好在哪里?

          共 15348字,需瀏覽 31分鐘

           ·

          2021-07-29 09:46

          6477318df50ead76fe55e49d37d09821.webp

          前端獵手
          ?鏈接每一位開發(fā)者,讓編程更有趣兒!關(guān)注


          前言

          這幾天 Vue 3.0 Beta 版本發(fā)布了,本以為是皆大歡喜的一件事情,但是論壇里還是看到了很多反對的聲音。主流的反對論點大概有如下幾點:

          1. 意大利面代碼結(jié)構(gòu)吐槽:

          “太失望了。雜七雜八一堆丟在setup里,我還不如直接用react”

          我的天,3.0這么搞的話,代碼結(jié)構(gòu)不清晰,語義不明確,無異于把vue自身優(yōu)點都扔了

          怎么感覺代碼結(jié)構(gòu)上沒有2.0清晰了呢??這要是代碼量上去了是不是不好維護啊

          1. 抄襲 React 吐槽:

          抄來抄去沒自己的個性

          有react香嗎?越來越像react了

          在我看來,Vue 黑暗的一天還遠遠沒有過去,很多人其實并沒有認真的去看?Vue-Composition-Api?文檔中的?動機?章節(jié),本文就以這個章節(jié)[1]為線索,從?代碼結(jié)構(gòu)底層原理?等方面來一一打消大家的一些顧慮。

          在文章的開頭,首先要標(biāo)明一下作者的立場,我對于 React 和 Vue 都非常的喜歡。他們都有著各自的優(yōu)缺點,本文絕無引戰(zhàn)之意。兩個框架都很棒!只是各有優(yōu)缺點而已。React 的 Immutable 其實也帶來了很多益處,并且 Hook 的思路還是 Facebook 團隊的大佬們首創(chuàng)的,真的是很讓人贊嘆的設(shè)計,我對 React 100% 致敬!

          設(shè)計動機

          大如 Vue3 這種全球熱門的框架,任何一個?breaking-change?的設(shè)計一定有它的深思熟慮和權(quán)衡,那么?composition-api?出現(xiàn)是為了解決什么問題呢?這是一個我們需要首先思考明白的問題。

          首先拋出 Vue2 的代碼模式下存在的幾個問題。

          1. 隨著功能的增長,復(fù)雜組件的代碼變得越來越難以維護。尤其發(fā)生你去新接手別人的代碼時。根本原因是Vue的現(xiàn)有API通過「選項」組織代碼,但是在大部分情況下,通過邏輯考慮來組織代碼更有意義。
          2. 缺少一種比較「干凈」的在多個組件之間提取和復(fù)用邏輯的機制。
          3. 類型推斷不夠友好。

          邏輯重用

          相信很多接觸過 React Hook 的小伙伴已經(jīng)對這種模式下組件間邏輯復(fù)用的簡單性有了一定的認知,自從 React 16.7 發(fā)布以來,社區(qū)涌現(xiàn)出了海量的 Hook 輪子,以及主流的生態(tài)庫?react-router,react-redux?等等全部擁抱 Hook,都可以看出社區(qū)的同好們對于 Hook 開發(fā)機制的贊同。

          其實組件邏輯復(fù)用在 React 中是經(jīng)歷了很長的一段發(fā)展歷程的,mixin?->?HOC & render-props?->?Hook,mixin?是 React 中最早啟用的一種邏輯復(fù)用方式,因為它的缺點實在是多到數(shù)不清[2],而后面的兩種也有著自己的問題,比如增加組件嵌套啊、props 來源不明確啊等等。可以說到目前為止,Hook 是相對完美的一種方案。

          當(dāng)然,我的一貫風(fēng)格就是上代碼對比,我就拿 HOC 來說吧,Github 上的一個真實的開源項目里就出現(xiàn)了這樣的場景:

          HOC 對比 Hook

          class?MenuBar?extends?React.Component?{
          ???handleClickNew?()?{
          ????????const?readyToReplaceProject?=?this.props.confirmReadyToReplaceProject(
          ????????????this.props.intl.formatMessage(sharedMessages.replaceProjectWarning)
          ????????);
          ????????this.props.onRequestCloseFile();
          ????????if?(readyToReplaceProject)?{
          ????????????this.props.onClickNew(this.props.canSave?&&?this.props.canCreateNew);
          ????????}
          ????????this.props.onRequestCloseFile();
          ????}
          ????handleClickRemix?()?{
          ????????this.props.onClickRemix();
          ????????this.props.onRequestCloseFile();
          ????}
          ????handleClickSave?()?{
          ????????this.props.onClickSave();
          ????????this.props.onRequestCloseFile();
          ????}
          ????handleClickSaveAsCopy?()?{
          ????????this.props.onClickSaveAsCopy();
          ????????this.props.onRequestCloseFile();
          ????}
          }

          export?default?compose(
          ????injectIntl,
          ????MenuBarHOC,
          ????connect(
          ????????mapStateToProps,
          ????????mapDispatchToProps
          ????)
          )(MenuBar);

          沒錯,這里用 compose 函數(shù)組合了好幾個 HOC,其中還有 connect 這種?接受幾個參數(shù)返回一個接受組件作為函數(shù)的函數(shù)?這種東西,如果你是新上手(或者哪怕是 React 老手)這套東西的人,你會在 「這個 props 是從哪個 HOC 里來的?」,「這個 props 是外部傳入的還是 HOC 里得到的?」這些問題中迷失了大腦,最終走向墮落(誤)。

          不談 HOC,我的腦子已經(jīng)快炸開來了,來看看用 Hook 的方式復(fù)用邏輯是怎么樣的場景吧?

          function?MenuBar?()?{
          ???//?菜單
          ???const?{?onClickRemix,?onClickNew?}?=?useMenuBar()
          ???//?國際化
          ???const?{?intl?}?=?useIntl()
          }

          export?default?MenuBar

          一切都變得很明朗,我可以非常清楚的知道這個方法的來源,intl?是哪里注入進來的,點擊了?useMenuBar?后,就自動跳轉(zhuǎn)到對應(yīng)的邏輯,維護和可讀性都極大的提高了。

          當(dāng)然,這是一個比較「刻意」的例子,但是相信我,我在 React 開發(fā)中已經(jīng)體驗過這種收益了。隨著組件的「職責(zé)」越來越多,只要你掌握了這種代碼組織的思路,那么你的組件并不會膨脹到不可讀。

          常見的請求場景

          再舉個非常常見的請求場景。

          在Vue2中如果我需要請求一份數(shù)據(jù),并且在loadingerror時都展示對應(yīng)的視圖,一般來說,我們會這樣寫:

          <template>
          ????<div?v-if="error">failed?to?load</div>
          ????<div?v-else-if="loading">loading...</div>
          ????<div?v-else>hello?{{fullName}}!</div>
          </template>

          <script>
          import?{?createComponent,?computed?}?from?'vue'

          export?default?{
          ??data()?{
          ????//?集中式的data定義?如果有其他邏輯相關(guān)的數(shù)據(jù)就很容易混亂
          ????return?{
          ????????data:?{
          ????????????firstName:?'',
          ????????????lastName:?''
          ????????},
          ????????loading:?false,
          ????????error:?false,
          ????},
          ??},
          ??async?created()?{
          ??????try?{
          ????????//?管理loading
          ????????this.loading?=?true
          ????????//?取數(shù)據(jù)
          ????????const?data?=?await?this.$axios('/api/user')??
          ????????this.data?=?data
          ??????}?catch?(e)?{
          ????????//?管理error
          ????????this.error?=?true??
          ??????}?finally?{
          ????????//?管理loading
          ????????this.loading?=?false
          ??????}
          ??},
          ??computed()?{
          ??????//?沒人知道這個fullName和哪一部分的異步請求有關(guān)?和哪一部分的data有關(guān)?除非仔細閱讀
          ??????//?在組件大了以后更是如此
          ??????fullName()?{
          ??????????return?this.data.firstName?+?this.data.lastName
          ??????}
          ??}
          }
          </script>

          這段代碼,怎么樣都談不上優(yōu)雅,湊合的把功能完成而已,并且對于loadingerror等處理的可復(fù)用性為零。

          數(shù)據(jù)和邏輯也被分散在了各個option中,這還只是一個邏輯,如果又多了一些邏輯,多了data、computed、methods?如果你是一個新接手這個文件的人,你如何迅速的分辨清楚這個method是和某兩個data中的字段關(guān)聯(lián)起來的?

          讓我們把zeit/swr[3]的邏輯照搬到Vue3中,

          看一下swr在Vue3中的表現(xiàn):

          <template>
          ????<div?v-if="error">failed?to?load</div>
          ????<div?v-else-if="loading">loading...</div>
          ????<div?v-else>hello?{{fullName}}!</div>
          </template>

          <script>
          import?{?createComponent,?computed?}?from?'vue'
          import?useSWR?from?'vue-swr'

          export?default?createComponent({
          ??setup()?{
          ??????//?useSWR幫你管理好了取數(shù)、緩存、甚至標(biāo)簽頁聚焦重新請求、甚至Suspense...
          ??????const?{?data,?loading,?error?}?=?useSWR('/api/user',?fetcher)
          ??????//?輕松的定義計算屬性
          ??????const?fullName?=?computed(()?=>?data.firstName?+?data.lastName)??
          ??????return?{?data,?fullName,?loading,?error?}
          ??}
          })
          </script>

          就是這么簡單,對嗎?邏輯更加聚合了。

          對了,順嘴一提,?use-swr?的威力可遠遠不止看到的這么簡單,隨便舉幾個它的能力:

          1. 間隔輪詢

          2. 請求重復(fù)數(shù)據(jù)刪除

          3. 對于同一個 key 的數(shù)據(jù)進行緩存

          4. 對數(shù)據(jù)進行樂觀更新

          5. 在標(biāo)簽頁聚焦的時候重新發(fā)起請求

          6. 分頁支持

          7. 完備的 TypeScript 支持

          等等等等……而這么多如此強大的能力,都在一個小小的?useSWR()?函數(shù)中,誰能說這不是魔法呢?

          類似的例子還數(shù)不勝數(shù)。

          umi-hooks[4]

          react-use[5]

          代碼組織

          上面說了那么多,還只是說了 Hook 的其中一個優(yōu)勢。這其實并不能解決「意大利面條代碼」的問題。當(dāng)邏輯多起來以后,組件的邏輯會糅合在一起變得一團亂麻嗎?

          從 Vue 官方的例子講起

          舉一個?Vue CLI UI file explorer[6]?官方吐槽的例子,這個組件是 Vue-CLI 的 gui 中(也就是平常我們命令行里輸入?vue ui?出來的那個圖形化控制臺)的一個復(fù)雜的文件瀏覽器組件,這是 Vue 官方團隊的大佬寫的,相信是比較有說服力的一個案例了。

          這個組件有以下的幾個功能:

          1. 跟蹤當(dāng)前文件夾狀態(tài)并顯示其內(nèi)容

          2. 處理文件夾導(dǎo)航(打開,關(guān)閉,刷新...)

          3. 處理新文件夾的創(chuàng)建

          4. 切換顯示收藏夾

          5. 切換顯示隱藏文件夾

          6. 處理當(dāng)前工作目錄更改

          文檔中提出了一個尖銳的靈魂之問,你作為一個新接手的開發(fā)人員,能夠在茫茫的?methoddata、computed?等選項中一目了然的發(fā)現(xiàn)這個變量是屬于哪個功能嗎?比如「創(chuàng)建新文件夾」功能使用了兩個數(shù)據(jù)屬性,一個計算屬性和一個方法,其中該方法在距數(shù)據(jù)屬性「一百行以上」的位置定義。

          當(dāng)一個組價中,維護同一個邏輯需要跨越上百行的「空間距離」的時候,即使是讓我去維護 Vue 官方團隊的代碼,我也會暗搓搓的吐槽一句,「這寫的什么玩意,這變量干嘛用的!」

          尤大很貼心的給出了一張圖,在這張圖中,不同的色塊代表著不同的功能點。

          35d782477322fd43e497cfbf8907f24e.webp

          其實已經(jīng)做的不錯了,但是在維護起來的時候還是挺災(zāi)難的,比如淡藍色的那個色塊代表的功能。我想要完整的理清楚它的邏輯,需要「上下反復(fù)橫跳」,類似的事情我已經(jīng)經(jīng)歷過好多次了。

          而使用 Hook 以后呢?我們可以把「新建文件夾」這個功能美美的抽到一個函數(shù)中去:

          function?useCreateFolder?(openFolder)?{
          ??//?originally?data?properties
          ??const?showNewFolder?=?ref(false)
          ??const?newFolderName?=?ref('')

          ??//?originally?computed?property
          ??const?newFolderValid?=?computed(()?=>?isValidMultiName(newFolderName.value))

          ??//?originally?a?method
          ??async?function?createFolder?()?{
          ????if?(!newFolderValid.value)?return
          ????const?result?=?await?mutate({
          ??????mutation:?FOLDER_CREATE,
          ??????variables:?{
          ????????name:?newFolderName.value
          ??????}
          ????})
          ????openFolder(result.data.folderCreate.path)
          ????newFolderName.value?=?''
          ????showNewFolder.value?=?false
          ??}

          ??return?{
          ????showNewFolder,
          ????newFolderName,
          ????newFolderValid,
          ????createFolder
          ??}
          }

          我們約定這些「自定義 Hook」以?use?作為前綴,和普通的函數(shù)加以區(qū)分。

          右邊用了 Hook 以后的代碼組織色塊:

          91b568c533966b834ca7fdf1de1a4c49.webp

          我們想要維護紫色部分功能的邏輯,那就在紫色的部分去找就好了,反正不會有其他「色塊」里的變量或者方法影響到它,很快咱就改好了需求,6 點準時下班!

          這是 Hook 模式下的組件概覽,真的是一目了然。感覺我也可以去維護?@vue/ui?了呢(假的)。

          export?default?{
          ??setup()?{?//?...
          ??}
          }

          function?useCurrentFolderData(networkState)?{?//?...
          }

          function?useFolderNavigation({?networkState,?currentFolderData?})?{?//?...
          }

          function?useFavoriteFolder(currentFolderData)?{?//?...
          }

          function?useHiddenFolders()?{?//?...
          }

          function?useCreateFolder(openFolder)?{?//?...
          }

          再來看看被吐槽成「意大利面條代碼」的?setup?函數(shù)。

          export?default?{
          ??setup?()?{
          ????//?Network
          ????const?{?networkState?}?=?useNetworkState()

          ????//?Folder
          ????const?{?folders,?currentFolderData?}?=?useCurrentFolderData(networkState)
          ????const?folderNavigation?=?useFolderNavigation({?networkState,?currentFolderData?})
          ????const?{?favoriteFolders,?toggleFavorite?}?=?useFavoriteFolders(currentFolderData)
          ????const?{?showHiddenFolders?}?=?useHiddenFolders()
          ????const?createFolder?=?useCreateFolder(folderNavigation.openFolder)

          ????//?Current?working?directory
          ????resetCwdOnLeave()
          ????const?{?updateOnCwdChanged?}?=?useCwdUtils()

          ????//?Utils
          ????const?{?slicePath?}?=?usePathUtils()

          ????return?{
          ??????networkState,
          ??????folders,
          ??????currentFolderData,
          ??????folderNavigation,
          ??????favoriteFolders,
          ??????toggleFavorite,
          ??????showHiddenFolders,
          ??????createFolder,
          ??????updateOnCwdChanged,
          ??????slicePath
          ????}
          ??}
          }

          這是誰家的小仙女這么美?。∵@邏輯也太清晰明了,和意大利面沒半毛錢關(guān)系??!

          從獲取鼠標(biāo)位置的需求講起

          我們有這樣一個跨組件的需求,我想在組件里獲得一個響應(yīng)式的變量,能實時的指向我鼠標(biāo)所在的位置。

          Vue 官方給出的自定義 Hook 的例子是這樣的:

          import?{?ref,?onMounted,?onUnmounted?}?from?'vue'

          export?function?useMousePosition()?{
          ??const?x?=?ref(0)
          ??const?y?=?ref(0)

          ??function?update(e)?{
          ????x.value?=?e.pageX
          ????y.value?=?e.pageY
          ??}

          ??onMounted(()?=>?{
          ????window.addEventListener('mousemove',?update)
          ??})

          ??onUnmounted(()?=>?{
          ????window.removeEventListener('mousemove',?update)
          ??})

          ??return?{?x,?y?}
          }

          在組件中使用:

          import?{?useMousePosition?}?from?'./mouse'

          export?default?{
          ??setup()?{
          ????const?{?x,?y?}?=?useMousePosition()
          ????//?other?logic...
          ????return?{?x,?y?}
          ??}
          }

          就這么簡單,無需多言。

          對比

          Hook 和 Mixin & HOC 對比

          說到這里,還是不得不把官方對于「Mixin & HOC 模式」所帶來的缺點整理一下。

          1. 渲染上下文中公開的屬性的來源不清楚。例如,當(dāng)使用多個mixin讀取組件的模板時,可能很難確定從哪個mixin注入了特定的屬性。
          2. 命名空間沖突。Mixins可能會在屬性和方法名稱上發(fā)生沖突,而HOC可能會在預(yù)期的prop名稱上發(fā)生沖突。
          3. 性能問題,HOC和無渲染組件需要額外的有狀態(tài)組件實例,這會降低性能。

          而 「Hook」模式帶來的好處則是:

          1. 暴露給模板的屬性具有明確的來源,因為它們是從 Hook 函數(shù)返回的值。
          2. Hook 函數(shù)返回的值可以任意命名,因此不會發(fā)生名稱空間沖突。
          3. 沒有創(chuàng)建僅用于邏輯重用的不必要的組件實例。

          當(dāng)然,這種模式也存在一些缺點,比如?ref?帶來的心智負擔(dān),詳見drawbacks[7]。

          React Hook 和 Vue Hook 對比

          其實 React Hook 的限制非常多,比如官方文檔中就專門有一個章節(jié)[8]介紹它的限制:

          1. 不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook
          2. 確??偸窃谀愕?React 函數(shù)的最頂層調(diào)用他們。
          3. 遵守這條規(guī)則,你就能確保 Hook 在每一次渲染中都按照同樣的順序被調(diào)用。這讓 React 能夠在多次的 useState 和 useEffect 調(diào)用之間保持 hook 狀態(tài)的正確。

          而 Vue 帶來的不同在于:

          1. 與React Hooks相同級別的邏輯組合功能,但有一些重要的區(qū)別。與React Hook不同,setup?函數(shù)僅被調(diào)用一次,這在性能上比較占優(yōu)。

          2. 對調(diào)用順序沒什么要求,每次渲染中不會反復(fù)調(diào)用 Hook 函數(shù),產(chǎn)生的的 GC 壓力較小。

          3. 不必考慮幾乎總是需要useCallback的問題,以防止傳遞函數(shù)prop給子組件的引用變化,導(dǎo)致無必要的重新渲染。

          4. React Hook 有臭名昭著的閉包陷阱問題(甚至成了一道熱門面試題,omg),如果用戶忘記傳遞正確的依賴項數(shù)組,useEffect和useMemo可能會捕獲過時的變量,這不受此問題的影響。Vue的自動依賴關(guān)系跟蹤確保觀察者和計算值始終正確無效。

          5. 不得不提一句,React Hook 里的「依賴」是需要你去手動聲明的,而且官方提供了一個 eslint 插件,這個插件雖然大部分時候挺有用的,但是有時候也特別煩人,需要你手動加一行丑陋的注釋去關(guān)閉它。

          我們認可React Hooks的創(chuàng)造力,這也是 Vue-Composition-Api 的主要靈感來源。上面提到的問題確實存在于 React Hook 的設(shè)計中,我們注意到Vue的響應(yīng)式模型恰好完美的解決了這些問題。

          順嘴一題,React Hook 的心智負擔(dān)是真的很嚴重,如果對此感興趣的話,請參考:

          使用react hooks帶來的收益抵得過使用它的成本嗎? - 李元秋的回答 - 知乎 https://www.zhihu.com/question/350523308/answer/858145147

          并且我自己在實際開發(fā)中,也遇到了很多問題,尤其是在我想對組件用?memo?進行一些性能優(yōu)化的時候,閉包的問題爆炸式的暴露了出來。最后我用?useReducer?大法解決了其中很多問題,讓我不得不懷疑這從頭到尾會不會就是?Dan?的陰謀……(別想逃過?reudcer

          React Hook + TS 購物車實戰(zhàn)(性能優(yōu)化、閉包陷阱、自定義hook)[9]

          原理

          既然有對比,那就從原理的角度來談一談兩者的區(qū)別,

          在 Vue 中,之所以?setup?函數(shù)只執(zhí)行一次,后續(xù)對于數(shù)據(jù)的更新也可以驅(qū)動視圖更新,歸根結(jié)底在于它的「響應(yīng)式機制」,比如我們定義了這樣一個響應(yīng)式的屬性:

          Vue

          <template>
          ??<div>
          ????<span>{{count}}</span>
          ????<button?@click="add">?+1?</button>
          ??</div>
          </template>

          export?default?{
          ????setup()?{
          ????????const?count?=?ref(0)
          ????????
          ????????const?add?=?()?=>?count.value++
          ????????
          ????????return?{?count,?add?}
          ????}
          }

          這里雖然只執(zhí)行了一次?setup?但是?count?在原理上是個 「響應(yīng)式對象」,對于其上?value?屬性的改動,

          是會觸發(fā)「由 template 編譯而成的 render 函數(shù)」 的重新執(zhí)行的。

          如果需要在?count?發(fā)生變化的時候做某件事,我們只需要引入?effect?函數(shù):

          <template>
          ??<div>
          ????<span>{{count}}</span>
          ????<button?@click="add">?+1?</button>
          ??</div>
          </template>

          export?default?{
          ????setup()?{
          ????????const?count?=?ref(0)
          ????????
          ????????const?add?=?()?=>?count.value++
          ????????
          ????????effect(function?log(){
          ????????????console.log('count?changed!',?count.value)
          ????????})
          ????????
          ????????return?{?count,?add?}
          ????}
          }

          這個?log?函數(shù)只會產(chǎn)生一次,這個函數(shù)在讀取?count.value?的時候會收集它作為依賴,那么下次?count.value?更新后,自然而然的就能觸發(fā)?log?函數(shù)重新執(zhí)行了。

          仔細思考一下這之間的數(shù)據(jù)關(guān)系,相信你很快就可以理解為什么它可以只執(zhí)行一次,但是卻威力無窮。

          實際上 Vue3 的 Hook 只需要一個「初始化」的過程,也就是?setup,命名很準確。它的關(guān)鍵字就是「只執(zhí)行一次」。

          React

          同樣的邏輯在 React 中,則是這樣的寫法:

          export?default?function?Counter()?{
          ????const?[count,?setCount]?=?useState(0)
          ????
          ????const?add?=?()?=>?setCount(prev?=>?prev?+?1)
          ????
          ????//?下文講解用
          ????const?[count2,?setCount2]?=?useState(0)
          ????
          ????return?(
          ??????<div>
          ????????<span>{count}</span>
          ????????<button?onClick={add}>?+1?</button>
          ??????</div>

          ????)
          }

          它是一個函數(shù),而父組件引入它是通過?<Counter />?這種方式引入的,實際上它會被編譯成?React.createElement(Counter)?這樣的函數(shù)執(zhí)行,也就是說每次渲染,這個函數(shù)都會被完整的執(zhí)行一次。

          而?useState?返回的?count?和?setCount?則會被保存在組件對應(yīng)的?Fiber?節(jié)點上,每個 React 函數(shù)每次執(zhí)行 Hook 的順序必須是相同的,舉例來說。這個例子里的?useState?在初次執(zhí)行的時候,由于執(zhí)行了兩次?useState,會在?Fiber?上保存一個?{ value, setValue } -> { value2, setValue2 }?這樣的鏈表結(jié)構(gòu)。

          而下一次渲染又會執(zhí)行?count 的 useState、?count2 的 useState,那么 React 如何從?Fiber?節(jié)點上找出上次渲染保留下來的值呢?當(dāng)然是只能按順序找啦。

          第一次執(zhí)行的 useState 就拿到第一個?{ value, setValue },第二個執(zhí)行的就拿到第二個?{ value2, setValue2 },

          這也就是為什么 React 嚴格限制 Hook 的執(zhí)行順序和禁止條件調(diào)用。

          假如第一次渲染執(zhí)行兩次 useState,而第二次渲染時第一個 useState 被 if 條件判斷給取消掉了,那么第二個?count2 的 useState?就會拿到鏈表中第一條的值,完全混亂了。

          如果在 React 中,要監(jiān)聽?count?的變化做某些事的話,會用到?useEffect?的話,那么下次?render

          之后會把前后兩次?render?中拿到的?useEffect?的第二個參數(shù)?deps?依賴值進行一個逐項的淺對比(對前后每一項依次調(diào)用 Object.is),比如

          export?default?function?Counter()?{
          ????const?[count,?setCount]?=?useState(0)
          ????
          ????const?add?=?()?=>?setCount(prev?=>?prev?+?1)
          ????
          ????useEffect(()?=>?{
          ??????console.log('count?updated!',?count)
          ????},?[count])
          ????
          ????return?(
          ??????<div>
          ????????<span>{count}</span>
          ????????<button?onClick={add}>?+1?</button>
          ??????</div>

          ????)
          }

          那么,當(dāng) React 在渲染后發(fā)現(xiàn)?count?發(fā)生了變化,會執(zhí)行?useEffect?中的回調(diào)函數(shù)。(細心的你可以觀察出來,每次渲染都會重新產(chǎn)生一個函數(shù)引用,也就是 useEffect 的第一個參數(shù))。

          是的,React 還是不可避免的引入了?依賴?這個概念,但是這個?依賴?是需要我們?nèi)ナ謩訒鴮懙模瑢崟r上 React 社區(qū)所討論的「心智負擔(dān)」也基本上是由于這個?依賴?所引起的……

          由于每次渲染都會不斷的執(zhí)行,這當(dāng)中所不斷產(chǎn)生的閉包和性能也會稍遜于 Vue3。那么它的關(guān)鍵字是「每次渲染都重新執(zhí)行」。

          關(guān)于抄襲 React Hook

          其實前端開源界談抄襲也不太好,一種新的模式的出現(xiàn)的值得框架之間相互借鑒和學(xué)習(xí)的,畢竟框架歸根結(jié)底的目的不是為了「標(biāo)榜自己的特立獨行」,而是「方便廣大開發(fā)者」。這是值得思考的一點,很多人似乎覺得一個框架用了某種模式,另一個框架就不能用,其實這對于框架之間的進步和發(fā)展并沒有什么好處。

          這里直接引用尤大在 17 年回應(yīng)[10]「Vue 借鑒虛擬dom」的一段話吧:

          再說 vdom。React 的 vdom 其實性能不怎么樣。Vue 2.0 引入 vdom 的主要原因是 vdom 把渲染過程抽象化了,從而使得組件的抽象能力也得到提升,并且可以適配 DOM 以外的渲染目標(biāo)。這一點是借鑒 React 毫無爭議,因為我認為 vdom 確實是個好思想。但要分清楚的是 Vue 引入 vdom 不是因為『react 有所以我們也要有』,而是因為它確實有技術(shù)上的優(yōu)越性。社區(qū)里基于 vdom 思想造的輪子海了去了,而 ng2 的渲染抽象層和 Ember Glimmer 2 的模板 -> opcode 編譯也跟 vdom 有很多思想上的相似性。

          這段話如今用到 Hook 上還是一樣的適用,程序員都提倡開源精神,怎么到了 Vue 和 React 之間有些人又變得小氣起來了呢?說的難聽點,Vue 保持自己的特立獨行,那你假如換了一家新公司要你用 Vue,你不是又得從頭學(xué)一遍嘛。

          更何況 React 社區(qū)也一樣有對 Vue 的借鑒,比如你看?react-router@6?的 api,你會發(fā)現(xiàn)很多地方和?vue-router?非常相似了。比如 useRoutes 的「配置式路由」,以及在組件中使子路由的代碼結(jié)構(gòu)等等。當(dāng)然這只是我淺顯的認知,不對的地方也歡迎指正。

          擴展閱讀

          對于兩種 Hook 之間的區(qū)別,想要進一步學(xué)習(xí)的同學(xué)還可以看黃子毅大大的好文:

          精讀《Vue3.0 Function API》[11]

          尤小右在官方 issue 中對于 React Hook 詳細的對比看法:

          Why remove time slicing from vue3?[12]

          總結(jié)

          其實總結(jié)下來,社區(qū)中還是有一部分的反對觀點是由于「沒有好好看文檔」造成的,那本文中我就花費自己一些業(yè)余時間整理社區(qū)和官方的一些觀點作為一篇文章,至于看完文章以后你會不會對 Vue3 的看法有所改觀,這并不是我能決定的,只不過我很喜歡 Vue3,我也希望能夠盡自己的一點力量,讓大家能夠不要誤解它。

          對于意大利面代碼:

          1. 提取共用的自定義 Hook(在寫 React 購物車組件的時候,我提取了 3個以上可以全局復(fù)用的 Hook)。
          2. 基于「邏輯功能」去組織代碼,而不是?state?放在一塊,method?放在一塊,這樣和用 Vue2 沒什么本質(zhì)上的區(qū)別(很多很多新人在用 React Hook 的時候犯這樣的錯誤,包括我自己)。

          對于心智負擔(dān):

          1. 更強大的能力意味著更多的學(xué)習(xí)成本,但是 Vue3 總體而言我覺得已經(jīng)把心智負擔(dān)控制的很到位了。對于?ref?這個玩意,確實是需要仔細思考一下才能理解。
          2. React Hook 的心智負擔(dān)已經(jīng)重的出名了,在我實際的開發(fā)過程中,有時候真的會被整到頭禿…… 尤其是抽了一些自定義 Hook,deps?依賴會層層傳遞的情況下(隨便哪一層的依賴錯了,你的應(yīng)用就爆炸了)。
          3. 不學(xué)習(xí)怎么能升職加薪,迎娶白富美,走向人生巔峰呢!(瞎扯)

          Vue3 有多香呢?甚至《React 狀態(tài)管理與同構(gòu)實戰(zhàn)》的作者、React 的忠實粉絲Lucas HC[13]在這篇?Vue 和 React 的優(yōu)點分別是什么?[14]?中都說了這樣的一句話:

          我不吐槽更多了:一個 React 粉絲向 Vue3.0 致敬!

          Vue3 目前也已經(jīng)有了 Hook 的一些嘗試:

          https://github.com/u3u/vue-hooks

          總之,希望看完這篇文章的你,能夠更加喜歡 Vue3,對于它的到來我已經(jīng)是期待的不行了。

          最后再次強調(diào)一下作者的立場,我對于 React 和 Vue 都非常的喜歡。他們都有著各自的優(yōu)缺點,本文絕無引戰(zhàn)之意。兩個框架都很棒!只是各有優(yōu)缺點而已。React 的 Immutable 其實也帶來了很多益處,并且 Hook 的思路還是 Facebook 團隊的大佬們首創(chuàng)的,真的是很讓人贊嘆的設(shè)計,我對 React 100% 致敬!

          本文的唯一目的就是想消除一些朋友對于 Vue 3.0 的誤解,絕無他意,如有冒犯敬請諒解~

          [1]

          這個章節(jié):?https://vue-composition-api-rfc.netlify.app/#logic-reuse-code-organization

          [2]

          多到數(shù)不清:?https://www.zhihu.com/question/67588479

          [3]

          zeit/swr:?https://github.com/zeit/swr

          [4]

          umi-hooks:?https://link.zhihu.com/?target=http%3A//github.com/umijs/hooks

          [5]

          react-use:?https://github.com/streamich/react-use

          [6]

          Vue CLI UI file explorer:?https://github.com/vuejs/vue-cli/blob/a09407dd5b9f18ace7501ddb603b95e31d6d93c0/packages/@vue/cli-ui/src/components/folder/FolderExplorer.vue#L198-L404

          [7]

          drawbacks:?https://vue-composition-api-rfc.netlify.app/#drawbacks

          [8]

          章節(jié):?https://zh-hans.reactjs.org/docs/hooks-rules.html

          [9]

          React Hook + TS 購物車實戰(zhàn)(性能優(yōu)化、閉包陷阱、自定義hook):?https://juejin.im/post/5e5a57b0f265da575b1bc055

          [10]

          回應(yīng):?https://www.zhihu.com/people/evanyou/posts

          [11]

          精讀《Vue3.0 Function API》:?https://juejin.im/post/5d1955e3e51d4556d86c7b09

          [12]

          Why remove time slicing from vue3?:?https://github.com/vuejs/rfcs/issues/89

          [13]

          Lucas HC:?https://www.zhihu.com/people/lucas-hc

          [14]

          Vue 和 React 的優(yōu)點分別是什么?:?https://www.zhihu.com/question/301860721/answer/724759264



          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色成人网站在线播放 | 豆花视频一区在线观看 | 日本亚欧高清视频 | 成人激情直播 | 大香蕉午夜人网站 |