前端組件之前,我為什么一定讓 Codex 先查調(diào)用鏈)
上一篇我整理了 5 類項(xiàng)目規(guī)則其中一條是修改公共組件、公共契約或狀態(tài)來源之前先查調(diào)用方和兼容影響。這句話寫進(jìn)規(guī)則不難真正執(zhí)行時(shí)卻很容易被簡化成搜索一下組件名看看哪些文件導(dǎo)入了它。這只能查到調(diào)用鏈的一部分。前端組件不是一個(gè)只被import的函數(shù)。它可能通過全局注冊(cè)使用通過路由或動(dòng)態(tài)組件加載通過包裝組件再次暴露它與父級(jí)之間還存在 Props、Emits、v-model、插槽和暴露方法。組件內(nèi)部又可能讀寫狀態(tài)、發(fā)請(qǐng)求、改路由、操作緩存甚至依賴特定 DOM 結(jié)構(gòu)和外部樣式。只找到“誰引用了這個(gè)組件”還不足以回答真正重要的問題誰依賴了它的哪一項(xiàng)行為哪些依賴在類型層面可見哪些只在運(yùn)行時(shí)出現(xiàn)組件的輸出被誰繼續(xù)消費(fèi)某個(gè)默認(rèn)行為改變后會(huì)在哪條用戶路徑上暴露哪些檢查可以證明調(diào)用方?jīng)]有被破壞所以我讓 Codex 修改組件前查的不是一份引用列表而是一條行為調(diào)用鏈。調(diào)用鏈不是“誰導(dǎo)入了誰”而是行為怎樣穿過邊界我會(huì)把前端組件調(diào)用鏈寫成下面這條路徑用戶入口 → 頁面或容器 → 組件輸入 → 組件內(nèi)部狀態(tài)與副作用 → 組件輸出 → 調(diào)用方后續(xù)動(dòng)作 → 頁面結(jié)果其中任何一段發(fā)生變化都可能讓一個(gè)局部修改擴(kuò)散出去。例如一個(gè)編輯彈窗保存成功后觸發(fā)事件。單看彈窗文件可能只是這樣一條輸出保存成功 → 觸發(fā) saved 事件放進(jìn)完整鏈路后真實(shí)行為可能是列表頁點(diǎn)擊編輯 → 傳入當(dāng)前記錄標(biāo)識(shí) → 彈窗請(qǐng)求詳情并回填 → 用戶提交 → 彈窗調(diào)用保存接口 → 觸發(fā) saved 事件 → 父頁面關(guān)閉彈窗 → 保留當(dāng)前篩選條件刷新列表 → 必要時(shí)調(diào)整當(dāng)前頁碼如果只修改事件名稱或關(guān)閉邏輯后面的刷新、頁碼和篩選狀態(tài)都可能受影響。這就是“組件文件只改了幾行頁面行為卻變了”的來源。第一個(gè)誤區(qū)搜索組件名就認(rèn)為找到了全部調(diào)用方名稱搜索很有用但它默認(rèn)只能找到文本中可見的引用。下面這些情況很容易漏掉經(jīng)過統(tǒng)一出口再次導(dǎo)出業(yè)務(wù)文件可能不是直接導(dǎo)入目標(biāo)組件而是從組件庫入口、模塊索引或別名路徑導(dǎo)入。經(jīng)過包裝組件使用頁面使用的是BusinessDialog真正修改的是內(nèi)部的BaseDialog。直接搜索目標(biāo)組件只會(huì)找到包裝層真正依賴默認(rèn)行為的是更上層頁面。全局注冊(cè)模板中直接使用組件標(biāo)簽沒有本地導(dǎo)入。名稱映射可能發(fā)生在應(yīng)用入口或自動(dòng)導(dǎo)入配置中。動(dòng)態(tài)組件組件由配置、映射表或字符串名稱決定。靜態(tài)搜索可能只找到注冊(cè)表找不到真實(shí)業(yè)務(wù)路徑。路由或文件掃描頁面組件可能由路由生成、模塊掃描或約定式目錄自動(dòng)接入文件中沒有一條直觀的手寫引用。所以第一輪搜索得到的是“候選調(diào)用方”還需要沿導(dǎo)出、注冊(cè)、包裝和入口關(guān)系繼續(xù)向上確認(rèn)。我會(huì)要求 Codex 給每個(gè)調(diào)用方標(biāo)注發(fā)現(xiàn)方式直接導(dǎo)入、二次導(dǎo)出、包裝、全局注冊(cè)、動(dòng)態(tài)映射或路由入口。這樣能看出哪些結(jié)論可靠哪些仍需要運(yùn)行路徑驗(yàn)證。第二個(gè)誤區(qū)只查組件文件不查它的公開契約組件調(diào)用鏈的核心是公開契約。我至少會(huì)讓 Codex 檢查 5 個(gè)位置。Props不只看屬性名稱還要看是否必填默認(rèn)值是什么是否允許對(duì)象被組件內(nèi)部修改父級(jí)傳入的是原始狀態(tài)、計(jì)算結(jié)果還是臨時(shí)副本多個(gè)屬性之間是否存在隱含組合規(guī)則。把一個(gè)可選 Prop 改成必填類型檢查可能暴露部分影響改變默認(rèn)值則可能悄悄改變所有沒有顯式傳值的調(diào)用方。Emits 與v-model要查事件名稱、觸發(fā)時(shí)機(jī)、事件負(fù)載和消費(fèi)方式。同一個(gè)事件可能被不同頁面用于關(guān)閉彈窗刷新列表更新本地對(duì)象觸發(fā)下一步流程記錄操作結(jié)果。如果只確認(rèn)“父級(jí)監(jiān)聽了事件”仍然不知道事件行為變化會(huì)影響什么。插槽插槽依賴往往不容易通過類型和普通引用搜索發(fā)現(xiàn)。調(diào)用方可能依賴具名插槽是否存在插槽作用域暴露哪些數(shù)據(jù)DOM 包裹層級(jí)空插槽時(shí)的默認(rèn)內(nèi)容插槽渲染順序。刪除一層容器看似只是模板整理可能破壞插槽布局或外部樣式。暴露方法與組件引用父級(jí)可能通過組件引用調(diào)用open、reset、validate或其他方法。這類依賴未必出現(xiàn)在 Props 和 Emits 定義中。修改方法簽名、返回值或調(diào)用時(shí)機(jī)需要查所有組件引用和類型聲明。透傳屬性與原生事件一些組件會(huì)把未聲明屬性、類名或事件繼續(xù)傳給內(nèi)部節(jié)點(diǎn)。調(diào)用方依賴的行為可能沒有被組件 API 明確寫出卻在頁面中真實(shí)存在。公開契約查完后我才知道組件修改是內(nèi)部實(shí)現(xiàn)變化還是已經(jīng)觸碰外部行為。第三個(gè)誤區(qū)只向上找調(diào)用方不向下查狀態(tài)和副作用調(diào)用鏈有兩個(gè)方向向上誰使用組件、怎樣消費(fèi)它的輸出向下組件依賴什么狀態(tài)、請(qǐng)求、路由和公共能力。很多影響并不來自組件 API而來自內(nèi)部副作用。例如組件可能打開時(shí)請(qǐng)求詳情寫入共享狀態(tài)修改路由查詢參數(shù)更新瀏覽器緩存訂閱全局事件注冊(cè)定時(shí)器觸發(fā)埋點(diǎn)或日志在卸載時(shí)清理資源。如果修改初始化時(shí)機(jī)受影響的可能不只是當(dāng)前頁面還包括共享狀態(tài)的其他消費(fèi)者如果把請(qǐng)求從打開時(shí)改到首次掛載緩存頁面和重復(fù)打開路徑可能出現(xiàn)不同結(jié)果。我會(huì)讓 Codex 為每個(gè)副作用回答四個(gè)問題誰觸發(fā)它它讀寫什么外部狀態(tài)成功、失敗和取消時(shí)分別怎樣收尾還有誰會(huì)觀察到這次變化。這一步能把“組件內(nèi)部重構(gòu)”中隱藏的全局影響暴露出來。第四個(gè)誤區(qū)忽略生命周期和調(diào)用順序同一組函數(shù)執(zhí)行順序不同行為可能完全不同。前端組件里需要特別檢查首次掛載每次打開Props 變化路由切換緩存激活與失活彈窗關(guān)閉組件卸載異步請(qǐng)求返回。例如父級(jí)先更新記錄 ID再打開彈窗組件可能監(jiān)聽 ID 變化請(qǐng)求詳情也可能在打開動(dòng)作中讀取 ID。如果 Codex 只看到兩段邏輯都存在就可能把它們合并成一個(gè)更“簡潔”的初始化函數(shù)卻改變了調(diào)用順序。調(diào)用順序還會(huì)影響舊請(qǐng)求是否覆蓋新對(duì)象校驗(yàn)信息何時(shí)清除Loading 是否在關(guān)閉后繼續(xù)默認(rèn)值是否在回填后被重置父級(jí)刷新是否發(fā)生在彈窗關(guān)閉前后。所以調(diào)用鏈不能只畫靜態(tài)箭頭還要標(biāo)記關(guān)鍵時(shí)間點(diǎn)。第五個(gè)誤區(qū)DOM 和樣式?jīng)]有 import就不算依賴組件模板改變經(jīng)常會(huì)產(chǎn)生代碼搜索難以發(fā)現(xiàn)的影響。調(diào)用方或全局樣式可能依賴特定類名子元素層級(jí)第幾個(gè)子元素深層選擇器組件根節(jié)點(diǎn)屬性透傳位置固定尺寸或溢出行為。測試也可能依賴文本內(nèi)容標(biāo)簽語義data-*標(biāo)記可訪問名稱DOM 查詢路徑截圖基線。因此“只改模板結(jié)構(gòu)不改功能”不代表影響范圍小。我會(huì)把樣式、測試、故事、示例和文檔視為組件的外圍消費(fèi)者。它們不一定全部要修改但必須進(jìn)入檢查范圍。我讓 Codex 查調(diào)用鏈的實(shí)際順序第一步先寫清準(zhǔn)備改變什么調(diào)用鏈范圍取決于改動(dòng)類型?!靶薷膬?nèi)部變量名”和“改變默認(rèn)關(guān)閉行為”需要查的范圍完全不同。開始前先寫一條變化聲明準(zhǔn)備改變 - 哪個(gè)可觀察行為或公開契約 - 保持不變的行為 - 當(dāng)前認(rèn)為是內(nèi)部實(shí)現(xiàn)的部分如果連變化是什么都說不清搜索會(huì)無限擴(kuò)散。第二步列出組件全部公開面包括 Props、Emits、v-model、插槽、暴露方法、屬性透傳、根節(jié)點(diǎn)和公開類型。這一步得到“可能傳播影響的出口”。第三步查組件怎樣進(jìn)入項(xiàng)目沿直接導(dǎo)入、統(tǒng)一導(dǎo)出、包裝組件、全局注冊(cè)、動(dòng)態(tài)映射、路由和自動(dòng)掃描確認(rèn)真實(shí)入口。這一步得到“誰可能使用它”。第四步逐個(gè)查看調(diào)用方怎樣依賴不能只記錄文件名要記錄使用的契約和依賴的行為調(diào)用方使用方式依賴內(nèi)容變化后風(fēng)險(xiǎn)頁面 A直接使用Prop 默認(rèn)值、保存事件可能改變刷新時(shí)機(jī)包裝組件 B二次封裝插槽與暴露方法可能繼續(xù)影響上層頁面動(dòng)態(tài)入口 C配置映射組件名稱與屬性透傳需要運(yùn)行時(shí)確認(rèn)第五步向下追蹤狀態(tài)和副作用確認(rèn)請(qǐng)求、狀態(tài)、路由、緩存、全局事件和生命周期清理。這一步得到“組件會(huì)改變什么外部狀態(tài)”。第六步補(bǔ)查外圍依賴包括樣式、測試、故事、示例、文檔和類型使用。第七步把未知項(xiàng)變成暫停條件例如動(dòng)態(tài)組件入口無法靜態(tài)確認(rèn)插槽作用域存在未聲明使用兩個(gè)調(diào)用方依賴相反的默認(rèn)行為公共類型被其他應(yīng)用復(fù)用頁面驗(yàn)證環(huán)境無法啟動(dòng)。未知項(xiàng)沒有解決前不應(yīng)該讓 Codex 假設(shè)“其他調(diào)用方不受影響”。我會(huì)要求一份“調(diào)用鏈交付物”# 組件調(diào)用鏈檢查 ? ## 1. 計(jì)劃變化 - 要改變的行為或契約 - 必須保持不變 ? ## 2. 組件公開面 - Props - Emits / v-model - 插槽 - 暴露方法 - 透傳與根節(jié)點(diǎn) - 公開類型 ? ## 3. 進(jìn)入路徑 - 直接導(dǎo)入 - 統(tǒng)一導(dǎo)出或包裝 - 全局注冊(cè) - 動(dòng)態(tài)組件、路由或自動(dòng)掃描 ? ## 4. 調(diào)用方 | 調(diào)用方 | 使用契約 | 依賴行為 | 影響類型 | 驗(yàn)證方式 | | --- | --- | --- | --- | --- | ? ## 5. 下游依賴 - 狀態(tài) - 請(qǐng)求 - 路由 - 緩存與全局事件 - 生命周期清理 ? ## 6. 外圍依賴 - 樣式 - 測試 - 示例與文檔 ? ## 7. 結(jié)論 - 直接影響 - 間接影響 - 條件性影響 - 尚未確認(rèn) - 是否可以進(jìn)入修改計(jì)劃這份交付物不應(yīng)該退化成幾十個(gè)文件名。每個(gè)文件都要說明它通過哪條契約或行為與組件發(fā)生關(guān)系。直接、間接和條件性影響要分開直接影響調(diào)用方明確使用了即將變化的 Prop、事件、插槽或方法。間接影響調(diào)用方使用包裝組件、共享狀態(tài)或公共類型變化經(jīng)過中間層傳播。條件性影響只有特定路由、權(quán)限、配置、環(huán)境、數(shù)據(jù)或操作順序下才出現(xiàn)。三種影響的驗(yàn)證方式不同。直接影響適合類型檢查和目標(biāo)頁面回歸間接影響需要沿包裝與狀態(tài)繼續(xù)追蹤條件性影響則必須寫清觸發(fā)條件不能用一次正常路徑代替。哪些情況下可以停止繼續(xù)查調(diào)用鏈也不能無限擴(kuò)張。我通常在下面幾個(gè)條件成立時(shí)停止組件所有公開面已經(jīng)列清每個(gè)入口都能對(duì)應(yīng)到真實(shí)調(diào)用方或明確排除計(jì)劃變化能映射到具體依賴行為狀態(tài)、副作用和生命周期責(zé)任已經(jīng)定位樣式與測試的關(guān)鍵依賴已檢查每類影響都有驗(yàn)證出口剩余未知不會(huì)改變實(shí)現(xiàn)方向或已明確標(biāo)成后續(xù)人工驗(yàn)證。目標(biāo)不是理解整個(gè)項(xiàng)目而是證明當(dāng)前改動(dòng)不會(huì)越過尚未看見的責(zé)任邊界。寫在最后修改前查調(diào)用鏈不是為了生成一份漂亮的依賴圖而是為了回答三個(gè)問題這個(gè)組件的哪些行為對(duì)外構(gòu)成契約誰直接、間接或在特定條件下依賴這些行為修改以后分別用什么證據(jù)證明調(diào)用方仍然成立。僅搜索組件名最多找到部分靜態(tài)入口。真正的調(diào)用鏈還包括 Props、Emits、v-model、插槽、暴露方法、狀態(tài)、副作用、生命周期、DOM、樣式和測試。下一篇我會(huì)把這份調(diào)用鏈進(jìn)一步轉(zhuǎn)成影響范圍評(píng)估表怎樣區(qū)分內(nèi)部實(shí)現(xiàn)、公開契約和用戶行為變化怎樣確定需要修改與只需回歸的位置以及怎樣據(jù)此調(diào)整 Codex 的執(zhí)行批次和驗(yàn)收路徑。本系列持續(xù)更新。接下來會(huì)從“找到依賴”進(jìn)入“判斷影響”為后面的代碼差異審查建立一條清楚的基線。每日好工具推薦在這里推薦一款超好用的圖片壓縮工具——“圖壓”在線圖片壓縮免費(fèi)壓縮 JPG、PNG、WebP - 圖壓工具。同事安利給我的用過后真的覺得太香了支持批量壓縮、調(diào)整壓縮百分比最關(guān)鍵的是它是離線程序下載到本地就能反復(fù)用。我平時(shí)做自媒體和寫前端時(shí)經(jīng)常用到再也不用去網(wǎng)上找在線壓縮工具了。它也帶在線壓縮功能很方便。