業(yè)務(wù)前端的本質(zhì)--數(shù)據(jù)維護(hù)
共 4795字,需瀏覽 10分鐘
·
2024-07-26 09:10
Vue/React 將前端開發(fā)從 jQuery 命令式的編程風(fēng)格帶到了聲明式的編程風(fēng)格,開發(fā)者只需要描述界面應(yīng)該是什么樣子,Vue/React 就會根據(jù)數(shù)據(jù)的變化自動更新界面。
因此對于業(yè)務(wù)頁面只需要關(guān)心數(shù)據(jù)有什么以及引起數(shù)據(jù)的變化有什么。
數(shù)據(jù)
數(shù)據(jù)主要有兩大類,ui 相關(guān)和非 ui 相關(guān)。
ui 相關(guān)
前端本質(zhì)上就是將數(shù)據(jù)可視化,因此定義的變量中一部分就是供頁面展示使用的,在 Vue 中會把這些數(shù)據(jù)定義在 data 中變?yōu)轫憫?yīng)式,在 React 中會調(diào)用 SetState 來更新這些變量以便更新視圖。
前端自閉環(huán)
一些變量僅在前端記錄進(jìn)行 ui 的更新,后端不會感知到。
比如頁面的 loading 態(tài):
點擊態(tài),是否打開展示更多:
來自后端
頁面數(shù)據(jù)是存在數(shù)據(jù)庫中,后端會把這些數(shù)據(jù)給前端,供前端展示,這類數(shù)據(jù)又分為兩種:
-
將數(shù)據(jù)直接賦值給某個前端變量進(jìn)行展示,比如昵稱、標(biāo)題等。 -
將數(shù)據(jù)轉(zhuǎn)換后再進(jìn)行展示,比如錢相關(guān)字段因為精度問題,后端存儲的是分,給到前端以后需要轉(zhuǎn)換成元進(jìn)行展示。
來自底層
設(shè)備信息:通過屏幕寬高來設(shè)置彈窗的寬高。
localStorage:一些模塊可能一天只需要展示一次,前端將標(biāo)志存到 localStorage 中自行進(jìn)行判斷。
非 ui 相關(guān)
這些變量和 ui 無關(guān)也不會和頁面后端交互,舉幾個例子:
前端自閉環(huán)
請求鎖:一些提交請求,為了防止用戶多次提交,可以在接口請求前設(shè)置一個標(biāo)志位,類似于下邊這樣。
// 用于保存請求狀態(tài)的標(biāo)志位
let isSubmitting = false;
// 模擬一個異步請求
function sendRequest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("請求成功");
}, 2000); // 模擬2秒的請求時間
});
}
// 處理按鈕點擊事件
function handleSubmit() {
// 檢查標(biāo)志位
if (isSubmitting) {
console.log("請求正在進(jìn)行中,請稍后...");
return;
}
// 設(shè)置標(biāo)志位
isSubmitting = true;
console.log("開始請求...");
// 發(fā)送請求
sendRequest().then(response => {
console.log(response);
}).catch(error => {
console.error(error);
}).finally(() => {
// 請求完成后重置標(biāo)志位
isSubmitting = false;
console.log("請求完成,可以再次提交");
});
}
埋點數(shù)據(jù):模塊曝光或者用戶點擊的時候進(jìn)行埋點,相關(guān)數(shù)據(jù)會提前存到一個對象中。
定時器引用:頁面中創(chuàng)建定時器后用一個變量保存定時器實例,用戶可能離開頁面的時候還未執(zhí)行到定時器,因此需要在離開頁面的時候進(jìn)行清除。
Page({
data: {
// 其他數(shù)據(jù)
},
// 用于保存定時器實例的變量
timer: null,
// 頁面加載時創(chuàng)建定時器
onLoad: function() {
this.createTimer();
},
// 創(chuàng)建定時器的方法
createTimer: function() {
this.timer = setTimeout(() => {
console.log('定時器執(zhí)行中...');
}, 5000); // 5秒后執(zhí)行
},
// 頁面卸載時清除定時器
onUnload: function() {
this.clearTimer();
},
// 清除定時器的方法
clearTimer: function() {
if (this.timer) {
clearTimeout(this.timer);
console.log('頁面即將卸載,定時器已清除');
}
},
// 其他頁面方法和事件處理函數(shù)
});
來自后端
埋點數(shù)據(jù):模塊曝光或者用戶點擊的時候進(jìn)行埋點,一些數(shù)據(jù)會由后端給到。
來自底層
localStorage:比如存儲用戶的點擊次數(shù),進(jìn)行相應(yīng)的限頻。
引起數(shù)據(jù)的變化
數(shù)據(jù)變化的根源就是用戶操作,用戶的操作可能直接引起數(shù)據(jù)變化,也可能觸發(fā)某些全局事件或者定時器,又觸發(fā)新一輪的頁面數(shù)據(jù)變化。
用戶操作
大部分的數(shù)據(jù)變化都是由于用戶的操作,比如點擊、滑動。
根據(jù)點擊的位置不同,可能觸發(fā)不同的動作。比如去請求后端接口拿數(shù)據(jù)、進(jìn)入新頁面、離開當(dāng)前頁面,在小程序中會觸發(fā) onHide 、onShow 生命周期,在這些周期中會做一些動作更新數(shù)據(jù)。
還有經(jīng)常遇到的表單逆向操作,當(dāng)用戶依次填了 A 項、B 項、C 項,由于 B、C 依賴于 A 項的選擇,當(dāng)用戶再修改 A 項的時候需要清空 B、C 之前的選擇。
監(jiān)聽數(shù)據(jù)變化
在 Vue 中通過 watch 監(jiān)聽變量,在 React 中通過 useEffect 監(jiān)聽變量。一般情況監(jiān)聽的是組件的 prop,當(dāng)父組件變化時,子組件進(jìn)行相應(yīng)的更新。
定時器
定時器時間結(jié)束后,會觸發(fā)定時器注冊的回調(diào)函數(shù)。
常用于頁面上的倒計時的更新。也用于解決 ui 更新的時序問題,直接給 setTimeout 事件設(shè)置為 0,讓回調(diào)函數(shù)到下一個宏任務(wù)周期去執(zhí)行。
全局事件
主要用于跨模塊之間的通信,常用的比如 eventbus、vuex、redux 等。
常見的比如全局的登錄事件,各個頁面需要監(jiān)聽登錄成功才去觸發(fā)后續(xù)的業(yè)務(wù)邏輯。
關(guān)聯(lián)
理想狀態(tài),用戶動作 => 更新數(shù)據(jù) => 頁面自動更新。
但實際上,當(dāng)數(shù)據(jù)變化的時候,由于全局事件、定時器的存在,還會繼續(xù)觸發(fā)新一輪的數(shù)據(jù)更新。
此外,數(shù)據(jù)變化每次也不止變更一個數(shù)據(jù),數(shù)據(jù)之間又會有相應(yīng)的聯(lián)動關(guān)系。
這也是為什么框架都在倡導(dǎo)單一數(shù)據(jù)流的原因,全局事件第一個人用起來會很方便,但在一個上百人的前端項目中,后續(xù)頁面繼續(xù)迭代或者重構(gòu)的時候,漏改或者影響面評估錯誤的風(fēng)險也會增高。
當(dāng)增加一個數(shù)據(jù)變量的時候也要考慮清楚,是否有必要新增,因為每增一個都會增加頁面的內(nèi)部復(fù)雜度。當(dāng)然有時候也不是變量越少越好,當(dāng)各個地方共用一個變量,也意味著這個變量賦予了多重含義,有悖「單一職責(zé)」。
總
業(yè)務(wù)前端看起來簡單,就是維護(hù)一些數(shù)據(jù)。但當(dāng)頁面數(shù)據(jù)變量越來越多,交互越來越多,數(shù)據(jù)更新會變得錯綜復(fù)雜,后續(xù)迭代的心智負(fù)擔(dān)會越來越重。
此時能做的就是明確當(dāng)前數(shù)據(jù)(ui/非 ui 數(shù)據(jù))有什么,引起數(shù)據(jù)的變化有什么(用戶動作、數(shù)據(jù)之間的關(guān)聯(lián)等),這些理清之后出現(xiàn) bug 的概率也會極大降低。
最根本的還是降低函數(shù)和函數(shù)之間、模塊與模塊之間的依賴關(guān)系,也就是常說的高內(nèi)聚、低耦合,保證后續(xù)改動的影響面足夠小且明確。
最終看到的頁面不再是頁面,而是數(shù)據(jù)的變化和流動。
