vue3:雙向綁定 vs 單向數(shù)據(jù)流

慣性思維

一開始看到vue說是雙向綁定的,就自動認為數(shù)據(jù)流向也是雙向的,后來發(fā)現(xiàn)我錯了。

以前的想法是:我給你,你給我,這就是雙向綁定,同時也是雙向流動,但是vue不是這么干的。那么vue是怎么做的呢?

拋物線的單向數(shù)據(jù)流

為了便于說明,畫個圖先:

綁定與流向

步驟:

  1. 在父組件里面定義一個 info
  2. 在子組件里面定義一個 props 接收
  3. 子組件內(nèi)部使用 emit “修改”props
  4. 模板重新渲染

看表面效果:

  • 父組件修改info,父組件、子組件同時響應(yīng)。
  • 在子組件修改info(props),父組件和子組件也是同時響應(yīng)

這不就是雙向流動嗎?

但是實際情況并不是這樣的。

實際情況

表面上看,emit 是在子組件寫的,但是具體的賦值操作并不是在子組件里執(zhí)行的。

看上圖里面的1,2,3,特意調(diào)整了字號和顏色,應(yīng)該很明顯吧。這是一個“拋物線”,兜了一個圈子才實現(xiàn)了修改。

緣由

那么為啥要這么折騰呢?大概是因為響應(yīng)性吧。
以前有個段子,不能給郵箱設(shè)置“自動回復(fù)”功能,如果設(shè)置了可能會這樣:

比如我給你發(fā)了一個郵件,你的郵箱服務(wù)會自動給我發(fā)送一個“已收到”的反饋。

然后我的郵箱服務(wù)收到你的反饋后,認為是收到了新郵件,于是又給你的郵箱發(fā)了一個“已收到”的反饋。

于是開始了無限循環(huán)。

那么vue是否也有類似的麻煩呢?不太清楚。

但是從設(shè)定來看,大概是為了避免這樣的問題吧。

直接修改,又是咋回事?

不是規(guī)定子組件不能直接修改props嗎?怎么還來個直接修改,違規(guī)了!

經(jīng)??吹竭@樣的說法,其實并不是這樣的。

這里特指vue3,vue2沒研究過。

在 vue3 里面 vuex 的狀態(tài)是 proxy 的。
子組件的 props 也是 proxy 的,并且把第一層屬性設(shè)置為只讀,這樣在子組件里面就無法直接修改第一層屬性。

但是留了個“缺口”,沒有限制第二層屬性也是只讀,這樣的話我們就可以利用這個漏洞做點騷操作。

比如 info 定義為:

const dialog = reactive({
  visible: false,
  width: '80%'
})

子組件的 props 定義為這樣,然后可以這樣操作:

const props = defineProps({
  moduleId: [Number, String],
  dialog: Object
})
const dialogs = props.dialog

熟悉 el-dialog 的話,就會發(fā)現(xiàn)我想做什么了吧。

  <el-dialog
      :title="'添加模塊:' + props.moduleId"
      v-model="dialogs.visible"
      :width="dialogs.width"
  >

我喜歡在父組件里面放一個按鈕,然后把 el-dialog 放到一個子組件里面,這樣父組件的代碼不容易亂,單擊父組件的按鈕,可以打開 el-dialog。
但是問題來了,是否顯示是通過 v-model 來設(shè)置的,而子組件的 v-model 不允許直接寫 props。

一般的做法是寫一個 computed ,設(shè)置 get 和 set,
get 獲取 props 的屬性,set 里面用 emit 來提交。
但是這樣做很麻煩有沒有。

于是我“直接”修改了,我覺得代碼就應(yīng)該直接一點。(emit 內(nèi)部代碼比較復(fù)雜,我沒看懂,斷點跟蹤都沒跟下去)

可能有人就不樂意了,你這是瞎干,違規(guī)了!

看看上面的圖,proxy 會攔截 set 操作,然后實際修改值的操作在哪里呢?肯定不在子組件。

既然不是在子組件里修改的,那么這么做也沒有違規(guī),和使用 emit 是一樣的原理。

使用emit是合規(guī)的吧。

補充,proxy 的 set 到底在哪里?

按F12看一下源碼,可以找到 proxy 的 set 的位置,在一個單獨的js文件里面,那么怎么算呢?應(yīng)該看js文件是在哪個組件里面引入的。
看看代碼,是不是在父組件寫的import { reactive } from 'vue',所以修改操作實際發(fā)生在父組件,和emit是一樣的原理。

proxy 的 set

不解

props 本身就是一個 proxy ,攔截一下就可以在父組件進行修改,那么為啥還非得使用 emit 繞圈圈呢?(不會又是為了向下兼容吧)

如果你想說,這樣無法跟蹤,不知道哪個子組件修改了,的話,那么可以看看上一篇:

使用proxy 給狀態(tài)加一個跟蹤功能: http://www.itdecent.cn/p/c8b0d5c8ef42

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容