常見的8個前端防御性編程方案
關(guān)于前端防御性編程
我們大多數(shù)情況可能遇到過,后端的由于同時請求人數(shù)過多,或者數(shù)據(jù)量過大,又或者是因?yàn)楫惓?dǎo)致服務(wù)異常,接口請求失敗,然后前端出現(xiàn)白屏或者報錯 還有一種情況,是前端自身寫的代碼存在一些缺陷,整個系統(tǒng)不夠健壯,從而會出現(xiàn)白屏,或者業(yè)務(wù)系統(tǒng)異常,用戶誤操作等 那么,就出現(xiàn)了前端防御性編程
常見的問題和防范
1.最常見的問題:
uncaught TypeError: Cannot read property 'c' of undefined
出現(xiàn)這個問題最根本原因是:
當(dāng)我們初始化一個對象obj為{}時候,obj.a這個時候是undefined.我們打印obj.a可以得到undefined,但是我們打印obj.a.c的時候,就會出現(xiàn)上面的錯誤。js對象中的未初始化屬性值是undefined,從undefined讀取屬性就會導(dǎo)致這個錯誤(同理,null也一樣)
如何避免?
js和ts目前都出現(xiàn)了一個可選鏈概念,例如:
const obj = {};
console.log(obj?.b?.c?.d)
上面的代碼并不會報錯,原因是
?.遇到是空值的時候便會返回undefined.
2.前端接口層面的錯誤機(jī)制捕獲
前端的接口調(diào)用,一般都比較頻繁,我們這時候可以考慮使用單例模式,將所有的axios請求都用一個函數(shù)封裝一層。統(tǒng)一可以在這個函數(shù)中catch捕獲接口調(diào)用時候的未知錯誤,偽代碼如下:
function ajax(url,data,method='get'){
const promise = axios[method](url,data)
return promise.then(res=>{
}).catch(error){
//統(tǒng)一處理錯誤
}
}
那么只要發(fā)生接口調(diào)用的未知錯誤都會在這里被處理了
3.錯誤邊界(Error Boundaries,前端出現(xiàn)未知錯誤時,展示預(yù)先設(shè)定的UI界面)
以React為例
部分 UI 的 JavaScript 錯誤不應(yīng)該導(dǎo)致整個應(yīng)用崩潰,為了解決這個問題,React 16 引入了一個新的概念 —— 錯誤邊界。
錯誤邊界是一種 React 組件,這種組件可以捕獲并打印發(fā)生在其子組件樹任何位置的 JavaScript 錯誤,并且,它會渲染出備用 UI,而不是渲染那些崩潰了的子組件樹。錯誤邊界在渲染期間、生命周期方法和整個組件樹的構(gòu)造函數(shù)中捕獲錯誤。
使用示例:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能夠顯示降級后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同樣可以將錯誤日志上報給服務(wù)器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定義降級后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
注意
錯誤邊界無法捕獲以下場景中產(chǎn)生的錯誤:
事件處理(了解更多) 異步代碼(例如 setTimeout 或 requestAnimationFrame 回調(diào)函數(shù)) 服務(wù)端渲染 它自身拋出來的錯誤(并非它的子組件)
4.前端復(fù)雜異步場景導(dǎo)致的錯誤
這個問題可能遠(yuǎn)不止這么簡單,但是大道至簡,遵循單向數(shù)據(jù)流的方式去改變數(shù)據(jù),例如:
//test.js
export const obj = {
a:1,
b:2
}
//使用obj
import {obj} from './test.js';
obj.a=3;
當(dāng)你頻繁使用這個obj對象時,你無法根據(jù)代碼去知道它的改變順序(即在某個時刻它的值是什么),而且這里面可能存在不少異步的代碼,當(dāng)我們換一種方式,就能知道它的改變順序了,也更方便我們debug
例如:
//test.js
export const obj = {
a:1,
b:2
}
export function setObj (key,value) {
console.log(key,value)
obj[key] = value
}
這樣,我們就做到了
5.前端專注“前端”
對于一些敏感數(shù)據(jù),例如登錄態(tài),鑒權(quán)相關(guān)的。前端應(yīng)該是盡量做無感知的轉(zhuǎn)發(fā)、攜帶(這樣也不會出現(xiàn)安全問題)
6.頁面做到可降級
對于一些剛上新的業(yè)務(wù),或者有存在風(fēng)險的業(yè)務(wù)模塊,或者會調(diào)取不受信任的接口,例如第三方的接口,這個時候就要做一層降級處理,例如接口調(diào)用失敗后,剔除對應(yīng)模塊的展示,讓用戶無感知的使用
7.巧用loading和disabled
用戶操作后,要及時loading和disabled確保不讓用戶進(jìn)行重復(fù),防止業(yè)務(wù)側(cè)出現(xiàn)bug
8.慎用innerHTML
容易出現(xiàn)安全漏洞,例如接口返回了一段JavaScript腳本,那么就會立即執(zhí)行。此時腳本如果是惡意的,那么就會出現(xiàn)不可預(yù)知的后果,特別是電商行業(yè),尤其要注意
