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

          吐槽一下 Vue3 的語法設(shè)計

          共 7213字,需瀏覽 15分鐘

           ·

          2024-07-20 11:03

          很長一段時間以來,老有人私信跟我說,Vue 很先進(jìn),代表了未來,你不要沉迷在 React 這個年老色衰的技術(shù)棧里自娛自樂啦,睜開眼睛去看看世界吧,別的技術(shù)棧都發(fā)展早就比 React 更先進(jìn)啦!!

          然后這些人估計都不會相信,我真的有非常認(rèn)真的去把 Vue 從頭到尾學(xué)了個遍,甚至 Vue2、Vue3 的原理我也理清楚了... 很多年前,我甚至還在我的付費小冊《JavaScript核心進(jìn)階》里,聊設(shè)計模式的時候,分享一個 Vue2 簡易版的實現(xiàn)原理作為案例...

          不僅如此,我還睜眼看了 rust 生態(tài)的 Leptos,看了 Solid.js,看了 Svelte,還看了鴻蒙開發(fā)的 ArkUI,看了 Android 開發(fā)的 compose,這些都是近些年新出的技術(shù)方案,我都有認(rèn)真的學(xué)過奧

          也不知道這些喊我睜眼看世界的,有沒有我看得多...

          然后這篇文章,我就主要以吐槽 Vue 的語法設(shè)計為主,來聊一下我 Vue3 的學(xué)習(xí)和使用體會


          一、ref 與 reactive

          一個很不好的體驗就是 ref 與 reactive 都太容易丟失響應(yīng)了。為了防止丟失響應(yīng),我需要隨時注意我的數(shù)據(jù)使用方式,我不能隨心所欲的按照 JavaScript 的基礎(chǔ)語法去任意妄為。

          首先嚴(yán)格踐行語義化的我,第一反應(yīng)是不想使用 ref。因為 ref 是 引用的縮寫,從語義上來說,他是不應(yīng)該具備響應(yīng)性的。但是偏偏 Vue3 的語法設(shè)計就沒這么講究,于是我的語義化思維,在我學(xué)習(xí)和使用響應(yīng)式數(shù)據(jù)時給我造成了極大的困擾...

          我剛開始在項目中,就偏好于使用 reactive。但是現(xiàn)實很快就把我的偏好捏碎了。比如下面這個例子,我將一個列表作為響應(yīng)性數(shù)據(jù)定義在 reactive

          let data = reactive([])

          但是我萬萬沒想到的是,這樣使用是有問題的。因為當(dāng)我從接口里面獲得一個新數(shù)據(jù)的時候,想要直接用新的列表覆蓋初始列表,結(jié)果居然沒有什么好的辦法能讓這種覆蓋生效!!!

          // 口請求成功之后執(zhí)行,數(shù)據(jù)失去響應(yīng)
          data = result.data

          然后我就只能這么寫

          // 定義
          const res = reactive({ list: [] })

          // 口請求成功之后調(diào)用
          res.list = result.data

          我這個組件只有一個響應(yīng)式數(shù)據(jù)的時候,就賊難受,所以我就想著法又加了一個,這樣就舒服一點了

          const res = reactive({ 
            list: [],
            show: false
          })

          這樣處理之后呢,我想著用的時候,就很自然的想著用解構(gòu)語法來使用吧。但是呢,響應(yīng)性又丟失了...

          這樣寫不行

          const {lsit, show} = reactive({ 
            list: [],
            showfalse
          })

          這樣也不行

          const data = reactive({ 
            list: [],
            showfalse
          })

          const {list, show} = data

          必須要引入一個新的 api 來解決這個問題 toRefs

          let data = reactive({
            list: [],
            openfalse
          })

          const {list, open} = toRefs(data)

          然后我就含淚看著我的 reactive 被強行變成了 ref. 這其實我還勉強可以接受,最令我崩潰的是,由于 list 和 open 都被轉(zhuǎn)化成 ref,因此使用的時候,我必須這樣用,把 .value 的尾巴加回來...

          list.value = [
            {message'hello'}, 
            {message'world'}
          ]

          所以 reactive 一個符合語義的響應(yīng)式 api,給我的使用感受就是,在設(shè)計上就是非常失敗。但是一個不太符合語義的的響應(yīng)式 api ref 被處理得還相對好一些。在這樣的情況之下,也就不得不更多的在項目中使用 ref

          但是使用 ref 的時候,除了不符合語義化之外,還不符合一致性。因為在 script 中使用,我們必須加上 .value 來處理。但是呢,template 中又不用... 甚至如果我為了一致性在模板中用了還會出問題...

          直接給我一直以來自認(rèn)為良好的編碼標(biāo)準(zhǔn)干碎了...

          所以我現(xiàn)在的用法是,使用 reactive,但是忍痛放棄解構(gòu),從而避免使用 toRefs。盡量避免使用 ref。雖然很多人發(fā)文章說官方強烈建議使用 ref,但是確實語義和一致性有點挑戰(zhàn)我的底線。當(dāng)然我也知道他在能力上處理得更好一些。

          二、傳參設(shè)計得真復(fù)雜

          從一個新手的角度,要理解 Vue3 的參數(shù)傳遞,居然是一件學(xué)習(xí)成本非常高的事情。原因就是因為為了確保響應(yīng)性和區(qū)分普通參數(shù),這里又設(shè)計了許多新的 api 來解決問題

          首先是參數(shù)的類型很復(fù)雜。

          因為 Vue 中設(shè)計了一個指令系統(tǒng),用于處理一些條件渲染的邏輯。比如 v-if

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

          等價于

          awesome && <h1>Vue is awesome!</h1>

          但是這個機制就由此就導(dǎo)致了在父組件使用自定義組件時,往子組件傳參就變得非常復(fù)雜。因為在子組件內(nèi)部就沒辦法統(tǒng)一接收屬性參數(shù)了。因為有的屬性呢,他是自定義指令,是不應(yīng)該往下傳的,但是有的指令呢,又需要往下傳

          例如事件回調(diào)

          <button v-on:click="greet">Greet</button>

          這個時候我們在學(xué)習(xí)的時候,就必須的保證區(qū)分如下幾種傳參

          一種就是有特殊含義的內(nèi)置指令或者自定義指令

          <h1 v-if="awesome">Vue is awesome!</h1>
          <h1 v-else>Oh no ??</h1>

          一種是函數(shù)類型的參數(shù),多為事件回調(diào)函數(shù)

          <MyComponent @some-event="callback" />

          一種是數(shù)據(jù)綁定類型的,這種被 Vue 官方成為動態(tài)類型。

          <BlogPost :title="post.title" />

          還有一種就是正常的參數(shù)傳遞,這種被 Vue 官方文檔稱為靜態(tài)參數(shù)類型。

          <BlogPost title="My journey with Vue" />

          然后這里有一個很魔性的約定,如果你參數(shù)的 key 值使用橫杠的方式,如下

          <MyComponent greeting-message="hello" />

          到子組件接收的時候,它居然強制把這個key 的寫法改了 ... ...

          你得寫成這樣才能被接收

          defineProps({
            greetingMessageString
          })

          還有一個對我來說,誤導(dǎo)性更大的一種情況。就是當(dāng)我試圖使用靜態(tài)參數(shù)類型傳遞一個靜態(tài)對象時,你猜怎么著?傳不了!

          我只能改成動態(tài)的綁定寫法,才能正常傳遞。這里為啥誤導(dǎo)性很強呢,因為在我看來,雖然我聲明的是一個對象,但是他就是一個靜態(tài)的數(shù)據(jù),也不是一個響應(yīng)式數(shù)據(jù)

          // 我覺得這是一個靜態(tài)數(shù)據(jù)
          const a = {
            message'hello'
          }
          <HelloWrold :message='a' />

          所以這個就給我干懵了。沒辦法,雖然我已經(jīng)知道怎么用了,但是我到現(xiàn)在也不太確定官方文檔說的動態(tài)屬性表達(dá)的準(zhǔn)確定義是啥。

          然后完了之后呢,還有一種參數(shù)類型,叫做透傳 Attributes,比如像這種,他可以直接在內(nèi)部元素貼上去生效

          <MyButton class="large" />

          所以我個人的感受就是,不僅設(shè)計得復(fù)雜,還有一些我覺得不夠合理的地方。有人說,這個學(xué)習(xí)成本低,我是不太信的。

          ?

          但是我得說一下,這些,我都學(xué)會了,也知道怎么區(qū)分怎么用了,非常的熟練,難不到我。但不妨礙我不喜歡這樣的設(shè)計。


          三、接收參數(shù)的迷惑行為

          在子組件中,接收參數(shù)我最迷惑的一個行為是

          defineProps({
            msgString
          })

          當(dāng)我這樣寫的時候,可以直接在 template 中使用 msg

          <template>
            <h1>{{ msg }}</h1>
          </template>

          但是在 <scirpt> 中就用不了... 不一致的表現(xiàn)讓我覺得非常難受。

          然后另外一個讓我覺得非常難受的語法設(shè)計就是對于事件回調(diào)函數(shù)的處理。例如我想要通過 @click 傳遞一個回調(diào)函數(shù)到子組件,但是這個時候,子組件怎么接收這個回調(diào)函數(shù)呢?

          他的接收邏輯,又跟 props 的邏輯完全不一樣了。

          我認(rèn)為的常規(guī)邏輯無非就是在父組件中,一個 key=value 的方式傳遞下去,然后在子組件中通過識別 key 來獲得這個 value,但是 Vue 又設(shè)計了一個新的思路,重新用了一個宏來處理這個事情

          <script setup>
          defineEmits(['inFocus''submit'])
          </script>

          而且調(diào)用的邏輯我也覺得有點懵... 這是啥?連回調(diào)函數(shù)的執(zhí)行都不見了...

          <script setup>
          const emit = defineEmits(['inFocus''submit'])

          function buttonClick({
            emit('submit')
          }
          </script>

          我寧愿這樣傳

          <HelloWrold :onclick='clickhandler' />

          這樣接收和使用,更符合我的一致性的想法。

          <script setup>
          const props = defineProps({
            msgString,
            onclickFunction
          })
          </script>

          <template>
            <h1 @click='onclick' text-center'>{{ msg }}</h1>
          </template>

          四、watch 的怪異行為

          在 watch 的時候,也有一個奇怪的行為。那就是當(dāng)我使用 reactive 聲明了狀態(tài)時,偶爾想要某個屬性被 watch 一下,結(jié)果卻發(fā)現(xiàn),普通的屬性值,居然不可以...

          const obj = reactive({ count0 })

          // 錯誤,因為 watch() 得到的參數(shù)是一個 number
          watch(obj.count, (count) => {
            console.log(`Count is: ${count}`)
          })

          我必須額外提供一個 getter 函數(shù)才能做到

          // 提供一個 getter 函數(shù)
          watch(
            () => obj.count,
            (count) => {
              console.log(`Count is: ${count}`)
            }
          )

          這又給我偏執(zhí)的想要使用 reactive 增加了不舒服的感覺... 難頂啊。當(dāng)然有的人比較喜歡用 watchEffect ,但是這里有一個非常重要的問題,就是他會自動追蹤所有能訪問到的響應(yīng)式屬性。很明顯這是一種簡單粗暴,并且也存在冗余監(jiān)聽風(fēng)險的一種方式。他的響應(yīng)性依賴關(guān)系并不明確,所以我并不是很喜歡使用它。

          五、總結(jié)

          很顯然, Vue3 為了底層的 Proxy 實現(xiàn)原理,在逐步放棄虛擬 DOM 的過程中,在語法設(shè)計上做了非常多的犧牲和妥協(xié),它為了解決數(shù)據(jù)響應(yīng)性丟失的問題,新增了許多的 api。因為很多東西是不得不這么處理,否則能力上就會存在問題。所以很多人在說,React 為什么不擁抱 Signal,難道你真的認(rèn)為,擁抱了 Signal,就不會做出任何犧牲嗎?全是正向收益?

          別做夢了!不可能的!哪怕是 Solid.js 這種沒有歷史負(fù)擔(dān),重新設(shè)計的類 React 框架,在響應(yīng)性的丟失上也備受困擾,怎么可能那么簡單就全是正向的收益?

          無論是從語法設(shè)計的角度來考慮,還是從設(shè)計模式的方向來考慮,基于類似 signal 的底層實現(xiàn),語法表現(xiàn)上明顯更適合設(shè)計為面向?qū)ο蟆N覀兛梢曰谘b飾器和依賴注入來完整底層邏輯的設(shè)想,例如

          <script setup>
          @Inject
          class HelloWorld extends Vue {
            @reactive
            counter = 0
            ...
          }
          </script>

          <template>
            ...
          </template>
          ?

          如果能通過解析省掉 script 和 template 標(biāo)簽就更好了

          這樣設(shè)計之后,就完全不需要擔(dān)心任何響應(yīng)性丟失的問題。從而極大的降低了學(xué)習(xí)成本和使用心智負(fù)擔(dān)。深度使用之后給我整體感受就是,Vue3 擁抱函數(shù)式,擁抱得很勉強。一方面是上手難度提高了,另外一方面是使用過程中的心智負(fù)擔(dān)也變重了。所以,從 Vue2 切換到 Vue3,絕非有的人認(rèn)為的那么平滑,甚至可以說是重新學(xué)了另外一個框架。甚至我認(rèn)為,React 開發(fā)者到 Vue3 才是平滑的切換,他們比 Vue2 開發(fā)者更容易接受 Vue3. 并且一個很有意思的事情是,如果你要學(xué)習(xí) Vue3 的最佳實踐,我這篇寫給 React 開發(fā)者的文章,反而完美的契合了 Vue3 的使用思考。

          React 哲學(xué)

          除此之外,由于 Vue2 發(fā)展得非常成熟,所以哪怕 Vue3 已經(jīng)發(fā)布了四周年了,Vue3 的學(xué)習(xí)資料也經(jīng)常和 Vue2 混雜在一起,它的被接受程度遠(yuǎn)低于預(yù)期。

          也許越往后發(fā)展,angular 更有機會重新大放異彩。畢竟他的底層邏輯和上層表現(xiàn)是一脈相承的,有比較扎實的設(shè)計理論基礎(chǔ),angular 在保持現(xiàn)有開發(fā)方式不變的情況下,擁抱 signal 非常的自然。

          只能說,自定義 hook 這種的邏輯復(fù)用的方式,和面向?qū)ο笾械睦^承、注入、mixin 等方式相比,確實在易用性和可讀性上的優(yōu)勢太明顯了,因此函數(shù)式才這么受歡迎,大多數(shù)新的前端框架都在這個模式下實現(xiàn)自己的理念... 但是在語法層面,React 依然是邏輯最自恰的

          瀏覽 124
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  操b视频免费 | 五月天婷婷激情综合网 | 小早川怜子爆乿护士 | chaopeng超碰永久 | 伊人影院大香蕉 |