如何減少前端代碼的改動
本文轉(zhuǎn)載自微信公眾號「前端GoGoGo」,作者Joel 。轉(zhuǎn)載本文請聯(lián)系前端GoGoGo公眾號。
工作中,大家可能會碰到這樣的情況:
- 接口的返回值變了,要改一堆代碼。
- 升級了一個第三方庫,要改一堆代碼。
- 某個組件的內(nèi)部實現(xiàn)變了,要改一堆代碼。
如何在遇到這種情況的時候,減少前端代碼的改動呢?我分享給大家 2 個技巧:
- 降低代碼間的耦合。
- 業(yè)務(wù)代碼和依賴代碼之間加適配層。
- 下面我們具體來看~
降低代碼間的耦合
耦合指模塊間關(guān)聯(lián)的程度。模塊之間的關(guān)聯(lián)越多,其耦合性越強,那么獨立性也就越差了。
高耦合的代碼,會出現(xiàn)一處改,處處改的情況。高耦合的代碼,模塊之間的聯(lián)系,就像一團亂碼。
解藕,就是避免對模塊內(nèi)部具體實現(xiàn)的依賴。
下面我們來看一些具體的耦合場景,以及對應(yīng)的解藕方案。
耦合 DOM
直接操作 DOM,是耦合的 DOM 的。當 DOM 結(jié)構(gòu)發(fā)生變化時,操作代碼也要跟著變。下面是顯示用戶名的代碼:
HTML:
用戶名:
- 用戶名:<div id="user-name"></div>
JavaScript:
- const userName = ...
- document.querySelector('#user-name').textContent = userName
當顯示用戶名的 id 變成其他時, JavaScript 也要變化。
解藕方法
Vue, React 之列的框架,聲明了 數(shù)據(jù) 和 視圖 的關(guān)系,不會耦合 DOM。
用 Vue:
- <template>
- 用戶名:<div>{{userName}}</div>
- </template>
- <script>
- new Vue({
- ...
- data: {
- userName: ...
- }
- })
- </script>
用 React:
- function App () {
- const [userName, setUserName] = useState(...)
- return (
- <div>
- 用戶名:<div>{userName}</div>
- </div>
- )
- }
耦合其他模塊的方法或?qū)傩?/h3>
以組件為例,當父組件主動調(diào)用子組件方法,會造成耦合。例如,父組件要讓子組件獲得焦點。用 React 實現(xiàn)的代碼如下:
- <button onClick={() => inputRef.focus()}>點我獲得焦點</button>
- // 子組件
- <MyInput ref={inputRef}>
如果子組件獲得焦點的方法簽名發(fā)生了變化,上面的代碼就失效了。同樣的,父組件獲取子組件的內(nèi)部屬性,也會造成耦合的問題。
解藕方法
耦合方法的解決方案1
子組件監(jiān)聽屬性的變化,來響應(yīng)父組件的主動調(diào)用。改寫代碼如下:
- const [controlFocus, setControlFocus] = useState(0);
- return (
- <>
- <button onClick={() => setControlFocus(Date.now())}>點我獲得焦點</button>
- <MyInput controlFocus={controlFocus}>
- </>
- )
耦合方法的解決方案2
用發(fā)布訂閱模式。父組件需要主動調(diào)用子組件方法時,觸發(fā)個自定義事件,子組建監(jiān)聽該自定義事件。
耦合屬性的解決方案1
將耦合的屬性放到父組件來維護,子組件改屬性時,通知父組件。如:
- const [value, setValue] = useState(0);
- return (
- <Comp value={value} onChange={setValue} />
- )
耦合屬性的解決方案2
父組件要獲得子組件的內(nèi)部屬性時,改變某個屬性。子組件監(jiān)聽到該屬性的變化后,執(zhí)行父組件獲取值的回調(diào)函數(shù)。
- const [value, setValue] = useState(0);
- const [controlGetValue, setControlGetValue] = useState(0);
- return (
- <Comp onChange={setValue} controlGetValue={controlGetValue} />
- )
上面代碼中,父組件要獲取子組件內(nèi)部的 value 值,只需改 controlGetValue 的值即可。
業(yè)務(wù)代碼和依賴代碼之間加適配層
如果業(yè)務(wù)代碼中有多處代碼直接調(diào)用了外部依賴,如果依賴項發(fā)生了變化,調(diào)用的地方也要改。比如:接口的請求和響應(yīng)改了;升級的第三方庫的 API 發(fā)生了變化。
解決這個問題,可以在業(yè)務(wù)代碼和依賴代碼之間加適配層。當依賴項改后,只需要改適配層代碼,不需要改業(yè)務(wù)代碼。
注意:增加適配層本身會增加代碼的復(fù)雜度。因此,不要過度的加。一般在滿足以下 2 個條件的情況下加:
- 代碼中有多處代碼直接調(diào)用了外部依賴。
- 外部依賴變動的可能性比較大。
常見的外部依賴有:配置項,接口,第三方庫,全局 API。我們一個個來看。
分類配置項
將配置從邏輯中分離出來,寫成配置文件。如
- export const API_HOST = '/api' // 接口前綴
- export const PAGE_LIMIT = 10 // 分頁時,一頁的條數(shù)
適配接口的請求參數(shù)和響應(yīng)結(jié)果
一般會加個適配層來統(tǒng)一對接口的請求參數(shù)和響應(yīng)。適配層在請求里加 token 之類的,在響應(yīng)里處理通用報錯。如:
- const request = (options) => {
- // 添加請求攔截器
- axios.interceptors.request.use(...)
- // 添加響應(yīng)攔截器
- axios.interceptors.response.use(...)
- return axios
- }
對具體接口做格式化請求參數(shù)和接口的返回。
- export const formatFromServer = res => ...
- export const formatToServer = data => ...
適配第三方庫
如果是第三方組件,可以用個組件代理第三庫的組件。如:
- import { Sortable } from "react-sortablejs"
- function Sort(props) {
- return (
- <Sortable
- {...props}
- ...
- >
- ...
- </Sortable>
- )
- }
如果是一個函數(shù),可以用一個函數(shù)來代理。如:
- import xxx from 'xxx'
- function myXxx(...args) {
- return xxx(...args)
- }
適配全局 API
如果我們大量代碼用了瀏覽器的可能會被廢棄的實驗性的 API。可以做這樣的配置:
- const someAPI = () => {
- if(window.someAPI) {
- return someAPI
- }
- // 不支持的情況。
- return () => ...
- }
總結(jié)
外部變化后,要減少前端代碼的改動,主要靠 降低代碼的耦合 和 增加適配代碼。但也不要過度使用哦~