作為一個開發人員,工作中需要經常進行host
切換,快速在不同環境中進行開發測試,阿里內部有個iHost,用起來簡單順手,可惜並沒有開放,離開後沒有找到一款更好的用的host
切換工具,索性自己寫一個。專案已經開源
程式碼地址 , 歡迎吐槽。
所用到的技術
因為是桌面應用,作為一個前端開發,目前的首選自然是eletron, 前端框架選擇react技術棧。
專案基於 electron-react0-boilerplate 生成,用到技術包括:
- react 16.x
- redux 4.x
- redux-thunk 2.x
- react-router 4.x
- ant-design 3.x
- reselect 3.x
- redux-actions 3.x
- webpack 4.x
- node-sass 4.x
主要邏輯和演示
整個應用的難度其實不大,主要就是生成一些條目,基於條目的啟用狀態對 host
檔案進行讀寫,基本操作如下:
功能設計
- 三個tab頁
- host設定頁:進行host條目的增刪改查
- 當前生效的host: 檢視系統當前生效的host
- 原始host: 啟動應用後會備份一份原始host稿件,用於還原
- host 設定
- 能夠建立一個host條目,編寫對應的host
- 可以對host進行增刪改查
- 同時可以建立一個host分組進行統一管理
- 點選左側的 checkbox,編輯啟用狀態的host設定都會實時改變系統host,不過在編輯時設定了一個 2秒的延遲,避免在不斷的輸入過程中頻繁讀寫檔案
整個應用功能就是這麼簡單,一個關鍵點就是許可權問題,要操作host檔案需要改變/private/etc/host
檔案的和/private/etc
目錄的許可權。所以在應用啟動時會彈出輸入電腦使用者密碼的彈窗進行授權。
記錄幾個主要技術使用
redux-actions和bindActionCreators
基於redux的專案有一個經常被詬病的地方在於action, reducer的繁瑣,模板程式碼太多。redux-actions 是一個不錯的工具用來解決此問題,具體用法請自行學習。不過值得注意的一點是,redux的bindActionCreators
不支援物件巢狀的actionCreator, 例如
const actionsCreators = {
add: function() {
xx
},
change: {
changeTitle: function() {
xx
},
changeDate: function() {
xx
}
}
}
const { add, change } = bindActionCreators(actionsCreator, dispatch)
複製程式碼
這裡 change 中的actionCreators是不會繫結成功的,由於redux-actions
生成的actionCreators是可以巢狀,所以原始的bindActionCreators
和 redux-actions
配合起來很不舒服。專案中稍微改寫了一下 bindActionCreators 使其支援巢狀。
reselect
對於一些基於計算性的資料,同時把原始資料和計算資料儲存在store上不是一個好的方案,每次更新要進行雙重更新,很容易產遺漏, redux
不同於 mobx
, mobx
存在computed
裝飾器用於解決計算屬性問題。 這個時候reselect 就派上了用場,不僅解決了計算性資料問題,它更大的價值在於能夠減少重複計算,每次rerender對於大量資料的計算可以在入參不變的情況下不重複計算。不過問題就是會多出一層selectors
層,可能也會讓某些人抓狂吧,嘎嘎~~
state 持久化儲存
為了每次開啟App的時候保持之前的狀態,整個store層需要儲存下來,因此藉助 electron-json-storage 寫了一個簡單redux 中介軟體:
import storage from 'electron-json-storage';
// 同步 store 到某個檔案的持久化儲存中介軟體, 基於electron-json-storage做儲存
const storageState = store => dispatch => action => {
dispatch(action);
const nextState = store.getState();
const { menuTree, checkedKeys } = nextState.host.menus;
const { systemHost } = nextState.host.systemHost;
const defaultDataPath = storage.getDefaultDataPath();
const dataPath = storage.getDataPath();
console.log('paths:', defaultDataPath, dataPath);
storage.set('HostState-xu', nextState); // 會建立一個HostState-xu.json的檔案,儘可能保證唯一性,不與其他應用衝突
};
export default storageState;
複製程式碼
每一次action
改變都同步保持state
。
electron-react-boilerplate 帶來的坑點
flow 和 eslint
Flow和一些eslint規則會在前期帶來很多困擾和額外的工作,所以可以自行斟酌修改一些規則,不過對於Flow
和eslint
個人還是建議多多使用的,會減少很多後期的小問題。
但是專案中的eslint-import
外掛在結合 webpack
設定alias
的時候會出現問題, 同樣對於測試用例,webpack
的alias
配置也不會生效。
幾個處理關鍵點:
- .eslintrc 需要改成.eslintrc.js
- .eslintrc.js中引用
path
需要require
, 不可以import
- 改成.eslintrc.js 在vscode中會出現
Eslint server
掛掉的問題,主要是因為專案中webpack.config.render.dev.js
中自己進行了環境變數的檢測CheckNodeEnv('development')
, 註釋掉此行程式碼。
electron-react-boilerplate 在不斷地更新,最新版的eslint配置可能已經和我當時不一致了,一些處理經驗也有可能過時了,列出一些參考issue: #620 ,#1321 , #1509
圖片的引用
引用圖片的路徑在打包後總是有問題,官方建議在render程式中通過 require
引入圖片,走一遍 loader
生成 base64
的圖片, 直接通過 path.join
和 __dirname
生成路徑在開發環境沒問題,但是打包後會報錯。
然而,在主程式中要使用圖片同樣存在問題,上訴方案並不生效,目前尚未沒有找到解決方案,已經提了相關issue
貌似electron
的靜態資源路徑在開發和生成環境是會有些問題,這裡還需要進一步研究下,有了解的同學可以指教下。
shell的exec
本想使用 shelljs 做一些命令處理,但是發現其有electron
相容性問題 electron-compatibility
因為需求不復雜,直接用 child_process.exec
進行處理。
一個詭異的打包問題
由於圖片引入問題,我會經常打包驗證,回來發現一個問題,如果上一次的打包出現問題,修改後再次進行打包,發現程式仍然啟動不了,這個時候需要斷網啟動才會成功。尚不瞭解什麼原因。
階段小結
第一次寫一個完整的 electron 專案, 用於解決實際需求,還是挺有意思的,後續專案還要做一些測試,持續整合,自動更新的處理,目前經驗還不足,各位社群的基友,請多指教指教,一起學習!