React 產品實現 -任務管理工具「氫」

clipboard.png

原文地址:https://zhuanlan.zhihu.com/p/...,歡迎轉載 :-)html

? 關於

其實對於這個專欄的訂閱用戶感到很是抱歉,已經停更好久了,也沒啥特別的理由就是懶 orz!不對,畫風不能這樣開頭,是這樣的,我以爲我應該用 React 去作點兒什麼,寫文章可以清晰個人思路,讓我和別人有交流,可是並無實際作產品那麼性感,因而我決定要用 React 來作一些產品出來,因而就有了 「氫」 http://origingroup.tech 相信我在這裏對氫的介紹以及實現技術介紹也能對你們有幫助。前端

因此這篇文章我主要想介紹一些作這個東西的思路、想法和一些技術實現以及用到的一些工具。react

? 「氫」 是什麼鬼

這個名字聽着就很奇怪,爲何是個 H 元素做爲名字,對,是這樣的,先無論氫是什麼東西,個人想法是我要用 React 去作一些產品出來那總得取個名字吧,「一些產品」 意味着我得費好多經歷去想產品名稱,好了,不如就按照元素週期表的順序取名字吧,因而「氫」這個名字就這樣定了。 :- ) (我都以爲我是天才了,hhhhhh ☞ )linux

好了,說正經的,其實作氫這個東西早有預謀,好久好久之前我作了一些產品,而後夭折了,eee...... 因而如今我決定把它們撿起來(感受哪裏不對。。)!webpack

其中的一個夭折的是 「一個針對我的的項目管理工具」,本想提供 saas 的服務,結果被我改來改去最後tm很長時間沒空理它不玩了,因而最終被改到如今的 「針對我的的筆記,任務,待辦離線管理工具」 ,因而就有了這個叫氫的傢伙(固然以前給它取的名字不叫這個。。)web

? 那氫提供的核心功能有哪些呢?

  • 提供 workflowy 功能的強大列表,可以將平常的瑣碎的事情,用極致簡單的交互去作增刪改查chrome

  • 核心目標基於任務管理來打造,因此參考了不少任務管理工具的 UX 以及本身的想法-結合 scrum 模型,讓它在任務管理上也能簡單極致(最開始核心目標太多,致使產品幾乎難產,其實最後的氫是一個刪減吧,這也讓我懂得了作產品須要剋制)redux

  • 最後我須要有一個 powerful 的編輯器,由於平時記錄事情的時候總的寫點兒什麼呀,因而任務詳情被打形成了一個 wysiwyg 編輯器,而且支持用 markdown 語法,能夠直接粘貼 markdown 生成樣式,後面還會提供導出 markdown 功能windows

  • 固然得漂亮瀏覽器

好了,說了辣麼多,先上幾個圖,畢竟有句名言叫-meitushuogejb

第一張圖就是我說的 列表 ,具體怎麼好用可能還得本身體驗過才知道,所見即所得,回車就建立一個任務,tab 一下任務就變爲子任務,ctrl + command + up/down

clipboard.png

clipboard.png

clipboard.png

clipboard.png

⚒ 說一說技術實現

如你所見,上面的這個東西是一個 Desktop app ,具體實現方式是:

Electron + React

最開始的時候只是針對 web 版本的,因此技術全是圍繞 React 來,後來決定該爲 Desktop app ,固然第一選擇是 electron,過程當中也是遇到了很多坑的地方,下面分幾個方便來分享一下

  1. React 技術棧的選擇

  2. 項目結構與 Webpack 打包編譯

  3. Electron 相關的使用細節

  4. 如何用 Electron 作 i18n
    React 技術棧選擇

基本的技術使用和我在本專欄中提到的無差,具體爲:

  • ES6 + JSX + Less :做爲基本的語言層選擇,這套路基本仍是很經常使用的了

  • Ant.design: 使用 ant.design 做爲基礎庫,這裏感謝玉伯大大團隊的貢獻

  • Redux + Redux-Saga:如今熟悉了 redux 和 redux-saga 事後很難再改成其餘的方式,由於感受這種配套已經很極致了,在處理數據流轉已經 UI 交互的時候,redux-saga 幾乎是個 magic 的工具

  • draft.js + draft-plugin-js: 在編輯器的選擇上使用了 Facebook 的 draft.js , 若是你願意詳細瞭解其設計和架構的話也會以爲這也是個偉大的項目,同時經過 draft-plugin-js 能夠很快的將編輯器功能組件化,很容易經過 hook 定製本身的編輯器,氫中的編輯器有兩個,第一個是列表項每一個都是一個編輯器實例,任務詳情,定製化的一個富文本編輯器

  • Immutable.js: 不可變數據,這對於列表來講真是過重要了,React 若是不進行優化的在特殊狀況下會有嚴重的性能問題,氫種的列表就是這種特殊狀況,一編輯某一個任務,處理很差會卡,會抖動。所以我在作列表的時候就決定由 immutable 重構了整個項目,同時列表的數據結構也是作了特殊的設計和優化的。(好比一個小問題,上下移動,如何肯定順序呢?)

  • React-intl:氫作了基本的國際化,也就支持英文,固然如今應該有不少語法錯誤還沒檢查,使用的是 React-intl。

  • React-vitualized:這個項目也是爲了作性能優化用的,不過如今的版本由於優化了數據結構不須要了

項目結構與 Webpack 打包編譯

項目的目錄結構設計和 webpack 打包編譯纔是頭痛的問題,當 webpack 趕上 electron,各類環境不一致致使的奇怪 bug 我是不會跟別人說的,☝️我的默默的承受。。。

先上個項目結構的圖

clipboard.png

好長的目錄,其中關鍵的地方是 containers 的設計,項目中 containers 目前的設計是一個 cotainer 包含

  • reducer.js

  • sagas.js

  • index.jsx

  • styles.less

  • components

固然圖中的項目結構中有專門的 reducers 目錄和 sagas 目錄,這是由於以前的設計沒改,後面的 reducer ,style,sagas 都是組件自包含的。

說說 webpack 的打包

感謝這個項目幫我解決了很多坑 chentsulin/electron-react-boilerplate 正常狀況下的 Electron ,React 項目基本就按照這個 boilerplate 來就好了,不過我本身在打包上作了不少自定義的修改,因此纔會出現不少 webpack 的問題。

其中的一個優化點是,不使用 dll ,不管 production 仍是 dev 都會使用 vendors.js ,而這個 vendors 是預先打包壓縮好了的,因此每次打包實際都是隻打包了業務代碼。具體方式是看圖就知道了

經過定義 externals 讓打包過程忽略這些模塊的打包,使用 external 方式引用

clipboard.png

專門打包 vendor 的webpack 配置,必定要注意 libraryTarget

clipboard.png

Electron 相關的使用細節

這裏不想列舉太多細節,說說其中的一個,初始化 loading 加載,由於 electron 每次打開都幾乎是打開一個瀏覽器,在執行大的js 上也會花不少時間,因此會出現1s到2s的停頓,顯得應用很卡。

氫中解決這個問題的方式使用一個專門負責 loading 的 window ,這個頁面極其簡單,很快就能夠加載出來了,而後這個時候打開主要的 window,當這個window 加載完了再顯示出來,再關閉負責 loading 的 window, 下面我直接貼出 main.js 的代碼,有須要的能夠拿走

app.on('ready', createWindow);

function createWindow () {
  locale = app.getLocale();
  landingWindow = new BrowserWindow({
    show: false,
    frame: false,
    width: 490,
    height: 400
  })

  landingWindow.once("show", () => {
    // Create the browser window.
    mainWindow = new BrowserWindow({
      width: 1000, 
      height: 740,
      titleBarStyle: 'hidden',
      icon: `file://${__dirname}/assets/imgs/logo.png`,
      show: false
    })

    mainWindow.once("show", () => {
      landingWindow.hide()
      landingWindow.close()
      landingWindow.removeAllListeners();
      mainWindow.show()
      landingWindow = null
    })

    mainWindow.webContents.on('did-finish-load', () => {
      if (!mainWindow) {
        throw new Error('"mainWindow" is not defined');
      }
      mainWindow.show();
      mainWindow.focus();
    });

     // Emitted when the window is closed.
    mainWindow.on('closed', function () {
      // Dereference the window object, usually you would store windows
      // in an array if your app supports multi windows, this is the time
      // when you should delete the corresponding element.
      mainWindow.removeAllListeners();
      mainWindow = null;
    })

    // and load the index.html of the app.
    mainWindow.loadURL(`file://${__dirname}/app.html`);
    
    const menuBuilder = new MenuBuilder(mainWindow);
    menuBuilder.buildMenu(locale);
  })

  landingWindow.loadURL(`file://${__dirname}/landing.html`)
  landingWindow.once('ready-to-show', () => {
    landingWindow.show()
  })
}

如何用 Electron 作 i18n

在作國際化版本的時候有兩種狀況

main.js 主進程的國際化
window 內 renderer 進程的國際化
在 main.js 能夠經過

locale = app.getLocale();
來獲取當前系統的語言,不過須要注意的是,獲取的地方必定要在 app.on('ready') 的註冊函數中獲取,否則默認會一直取到 「en-US」

在 window 中, 這就真的是前端的天下了

locale = navigator.language
和在 chrome 中無差,能夠如上獲取語言,而後再經過設置到 react-intl 的 provider 中,接下來就是瑣碎的翻譯工做了。。。

最後

氫仍是花了我很多時間的,目前沒打算 open source, 積累到了必定程度應該會開源,最後說一說作這個東西的收穫:

  • 設計和 UX 是很重要的

  • 剋制,在作產品上,在設計上必定要剋制

  • linux 哲學,讓產品只完成一個功能,不要想太多,想太多了什麼都作不成

  • 哪怕仍是個渾身 bug 的東西,也要儘快的推出來,否則你都找不到理由繼續作下去

相關文章
相關標籤/搜索