摘要:前言如何寫出可維護(hù)和可讀性高的代碼,這一直是一個困擾很多人的問題。總結(jié)在開始寫業(yè)務(wù)之前,理應(yīng)先想清楚需求和業(yè)務(wù)邏輯,設(shè)計出合理的數(shù)據(jù)結(jié)構(gòu),對代碼進(jìn)行好的分層,這樣在一定程度上可以寫出可維護(hù)性更高的代碼。
前言
如何寫出可維護(hù)和可讀性高的代碼,這一直是一個困擾很多人的問題。關(guān)于變量如何起名、如何優(yōu)化if else之類的小技巧,這里就不做介紹了,推薦去看《代碼大全2》,千書萬書,都不如一本《代碼大全2》。
工作以來,我一直在寫一些重復(fù)且交互復(fù)雜的頁面,也沒有整理過自己的思路,這篇文章是我工作一年半來在項目中總結(jié)出來的一些經(jīng)驗。
分層對于業(yè)務(wù)代碼來說,大部分的前端應(yīng)用都還是以展示數(shù)據(jù)為主,無非是從接口拿到數(shù)據(jù),進(jìn)行一系列數(shù)據(jù)格式化后,顯示在頁面當(dāng)中。
首先,應(yīng)當(dāng)盡可能的進(jìn)行分層,傳統(tǒng)的mvc分層很適用于前端開發(fā),但對于復(fù)雜頁面來說,隨著業(yè)務(wù)邏輯增加,往往會造成controller臃腫的問題。因此,在此之上,可以將controller其分成formatter、service等等。
下面這是一些分層后簡單的目錄結(jié)構(gòu)。
+ pages + hotelList + components + Header.jsx + formatter + index.js + share + constants.js + utils.js + view.js + controller.js + model.jsService
統(tǒng)一管理所有請求路徑,并且將頁面中涉及到的網(wǎng)絡(luò)請求封裝為class。
// api.js export default { HOTELLIST: "/hotelList", HOTELDETAIL: "/hotelDetail" } // Service.js class Service { fetchHotelList = (params) => { return fetch(HOTELLIST, params); } fetchHotelDetail = (params) => { return fetch(HOTELLIST, params); } } export default new Service
這樣帶來的好處就是,很清楚的知道頁面中涉及了哪些請求,如果使用了TypeScript,后續(xù)某個請求方法名修改了后,在所有調(diào)用的地方也會提示錯誤,非常方便。
formatterformatter層儲存一些格式化數(shù)據(jù)的方法,這些方法接收數(shù)據(jù),返回新的數(shù)據(jù),不應(yīng)該再涉及到其他的邏輯,這樣有利于單元測試。單個format函數(shù)也不應(yīng)該格式化過多數(shù)據(jù),函數(shù)應(yīng)該根據(jù)功能進(jìn)行適當(dāng)拆分,合理復(fù)用。
mvc顧名思義,controller就是mvc中的c,controller應(yīng)該是處理各種副作用操作(網(wǎng)絡(luò)請求、緩存、事件響應(yīng)等等)的地方。
當(dāng)處理一個請求的時候,controller會調(diào)用service里面對應(yīng)的方法,拿到數(shù)據(jù)后再調(diào)用formatter的方法,將格式化后的數(shù)據(jù)存入store中,展示到頁面上。
class Controller { fetchHotelList = () => async (dispatch) => { const params = {} this.showLoading(); try { const res = await Service.fetchHotelList(params) const hotelList = formatHotelList(res.Data && res.Data.HotelList) dispatch({ type: "UPDATE_HOTELLIST", hotelList }) } catch (err) { this.showError(err); } finally { this.hideLoading(); } } }
view則是指react組件,建議盡量用純函數(shù)組件,有了hooks之后,react也會變得更加純粹(實際上有狀態(tài)組件也可以看做一個mvc的結(jié)構(gòu),state是model,render是view,各種handler方法是controller)。
對于react來說,最外層的一般稱作容器組件,我們會在容器組件里面進(jìn)行網(wǎng)絡(luò)請求等副作用的操作。
在這里,容器組件里面的一些邏輯也可以剝離出來放到controller中(react-imvc就是這種做法),這樣就可以給controller賦予生命周期,容器組件只用于純展示。
我們將容器組件的生命周期放到wrapper這個高階組件中,并在里面調(diào)用controller里面封裝的生命周期,這樣我們可以就編寫更加純粹的view,例如:
wrapper.js
// wrapper.js(偽代碼) const Wrapper = (components) => { return class extends Component { constructor(props) { super(props) } componentWillMount() { this.props.pageWillMount && this.props.pageWillMount() } componentDidMount() { this.props.pageDidMount && this.props.pageDidMount() } } componentWillUnmount() { this.props.pageWillLeave && this.props.pageWillLeave() } render() { const { store: state, actions } = this.props return view({state, actions}) } } }
view.js
// view.js function view({ state, actions }) { return ( <>> ) } export default Wrapper(view)
controller.js
// controller.js class Controller { pageDidMount() { this.bindScrollEvent("on") console.log("page did mount") } pageWillLeave() { this.bindScrollEvent("off") console.log("page will leave") } bindScrollEvent(status) { if (status === "on) { this.bindScrollEvent("off"); window.addEventListener("scroll", this.handleScroll); } else if (status === "off") { window.removeEventListener("scroll", this.handleScroll); } } // 滾動事件 handleScroll() { } }其他
對于埋點來說,原本也應(yīng)該放到controller中,但也是可以獨立出來一個tracelog層,至于tracelog層如何實現(xiàn)和調(diào)用,還是看個人愛好,我比較喜歡用發(fā)布訂閱的形式。
如果還涉及到緩存,那我們也可以再分出來一個storage層,這里存放對緩存進(jìn)行增刪查改的各種操作。
對于一些常用的固定不變的值,也可以放到constants.js,通過引入constants來獲取值,這樣便于后續(xù)維護(hù)。
// constants.js export const cityMapping = { "1": "北京", "2": "上海" } export const traceKey = { "loading": "PAGE_LOADING" } // tracelog.js class TraceLog { traceLoading = (params) => { tracelog(traceKey.loading, params); } } export default new TraceLog // storage.js export default class Storage { static get instance() { // } setName(name) { // } getName() { // } }數(shù)據(jù)與交互
不過也不代表著這樣寫就夠了,分層只能夠保證代碼結(jié)構(gòu)上的清晰,真正想寫出好的業(yè)務(wù)代碼,最重要的還是你對業(yè)務(wù)邏輯足夠清晰,頁面上的數(shù)據(jù)流動是怎樣的?數(shù)據(jù)結(jié)構(gòu)怎么設(shè)計更加合理?頁面上有哪些交互?這些交互會帶來哪些影響?
以如下酒店列表頁為例,這個頁面看似簡單,實際上包含了很多復(fù)雜的交互。
上方的是四個篩選項菜單,點開后里面包含了很多子類篩選項,比如篩選里面包括了雙床、大床、三床,價格/星級里面包含了高檔/豪華、¥150-¥300等等。
下方是快捷篩選項,對應(yīng)了部分篩選項菜單里面的子類篩選項。
當(dāng)我們選中篩選里面的雙床后,下方的雙床也會被默認(rèn)選中,反之當(dāng)我們選中下方的雙床后,篩選類別里面的雙床也會被選中,名稱還會回顯到原來的篩選上。
除此之外,我們點擊搜索框后,輸入"雙床",聯(lián)想詞會出現(xiàn)雙床,并表示這是個篩選項,如果用戶選中了這個雙床,我們依然需要篩選項和快捷篩選項默認(rèn)選中。
這三個地方都涉及到了篩選項,并且修改一個,其他兩個地方就要跟著改變,更何況三者的數(shù)據(jù)來自于三個不同的接口數(shù)據(jù),這是多么蛋疼的一件事情!
我借助這個例子來說明,在開始寫頁面之前,一定要對頁面中的隱藏交互和數(shù)據(jù)流動很熟悉,也需要去設(shè)計更加合理的數(shù)據(jù)結(jié)構(gòu)。
對于深層次的列表結(jié)構(gòu),鍵值對會比數(shù)組查詢速度更快,通過key也會更容易和其他數(shù)據(jù)進(jìn)行聯(lián)動,但是卻不能保證順序,有時候可能就需要犧牲空間來換時間。
// 假設(shè)篩選項床型type為1,大床id為1,雙床id為2. const bed = { "1-1": { name: "大床", id: 1, type: 1 }, "1-2": { name: "雙床", id: 2, type: 1 } } const bedSort = ["1-1", "1-2"] // 保證展示順序
當(dāng)我們選中大床的時候,只需要保存"1-1"這個key,再和store中快捷篩選項列表里面的key進(jìn)行mapping(快捷篩選項里面的項也應(yīng)該格式化為{"type-id": filterItem}的鍵值對格式),這樣從時間復(fù)雜度上說,比直接遍歷兩個數(shù)組更高效。
總結(jié)在開始寫業(yè)務(wù)之前,理應(yīng)先想清楚需求和業(yè)務(wù)邏輯,設(shè)計出合理的數(shù)據(jù)結(jié)構(gòu),對代碼進(jìn)行好的分層,這樣在一定程度上可以寫出可維護(hù)性更高的代碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102792.html
摘要:開工,我是如何定義現(xiàn)代切圖的既然所有后臺都有計劃重做,那么統(tǒng)一風(fēng)格那就是必須的了。我們前端部門采用的是自己搭建的。讓我使用,我是不樂意的。我采用提供一個服務(wù),提供靜態(tài)頁面預(yù)覽。沒錯我就是這么直接。 歡迎一起交流 歡迎關(guān)注我的個人公眾號,不定期更新自己的工作心得。 showImg(https://segmentfault.com/img/bVEk23?w=258&h=258); 什么是靜...
摘要:開工,我是如何定義現(xiàn)代切圖的既然所有后臺都有計劃重做,那么統(tǒng)一風(fēng)格那就是必須的了。我們前端部門采用的是自己搭建的。讓我使用,我是不樂意的。我采用提供一個服務(wù),提供靜態(tài)頁面預(yù)覽。沒錯我就是這么直接。 歡迎一起交流 歡迎關(guān)注我的個人公眾號,不定期更新自己的工作心得。 showImg(https://segmentfault.com/img/bVEk23?w=258&h=258); 什么是靜...
摘要:后端開發(fā)的疑惑后端開發(fā)最常面對的一個問題性能高并發(fā)等等。而到了時代,在方面有了前后端分離概念移動后端更是無力渲染天然前后端分離。 先來上一張前端頁面的效果圖(Vue + Vux + Vuex + Vue-Router)。showImg(https://segmentfault.com/img/remote/1460000010207850); 第一次做gif 沒什么經(jīng)驗,太大了。加載...
摘要:如何寫好業(yè)務(wù)代碼在前端工作中有很多業(yè)務(wù)性代碼,如果書寫不規(guī)范,那么對后期的維護(hù)將是非常致命的。代碼配置化在使用編寫代碼的過程中,經(jīng)常用到這樣的情況,根據(jù)情況判斷是否展示對應(yīng)的組件。 如何寫好業(yè)務(wù)代碼? 在前端工作中有很多業(yè)務(wù)性代碼,如果書寫不規(guī)范,那么對后期的維護(hù)將是非常致命的。 判斷配置化 業(yè)務(wù)場景 后端數(shù)據(jù)庫中經(jīng)常會一個字段具備幾個不同的狀態(tài),比如: status: 2 // 各...
閱讀 1274·2019-08-30 12:49
閱讀 3120·2019-08-28 18:14
閱讀 823·2019-08-26 11:38
閱讀 1680·2019-08-23 18:23
閱讀 2825·2019-08-23 17:04
閱讀 504·2019-08-23 16:52
閱讀 4025·2019-08-23 16:43
閱讀 2774·2019-08-23 16:12