慣性思維
一開始看到vue說是雙向綁定的,就自動認為數(shù)據(jù)流向也是雙向的,后來發(fā)現(xiàn)我錯了。
以前的想法是:我給你,你給我,這就是雙向綁定,同時也是雙向流動,但是vue不是這么干的。那么vue是怎么做的呢?
拋物線的單向數(shù)據(jù)流
為了便于說明,畫個圖先:

步驟:
- 在父組件里面定義一個 info
- 在子組件里面定義一個 props 接收
- 子組件內(nèi)部使用 emit “修改”props
- 模板重新渲染
看表面效果:
- 父組件修改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是一樣的原理。

不解
props 本身就是一個 proxy ,攔截一下就可以在父組件進行修改,那么為啥還非得使用 emit 繞圈圈呢?(不會又是為了向下兼容吧)
如果你想說,這樣無法跟蹤,不知道哪個子組件修改了,的話,那么可以看看上一篇:
使用proxy 給狀態(tài)加一個跟蹤功能: http://www.itdecent.cn/p/c8b0d5c8ef42