去年上半年開始接觸了一些JS的后端編程,參與了一個基于node(其實是Meteor)的開源項目,有一些知識上的積累,現在回歸了Java老本行,怕再過段時間就忘了,在這里記錄一下,好記性不如爛筆頭,說不定以后會有用。node大神可以看來消遣,跟我一樣是外行但是需要寫一些node程序的可以看看經驗。這篇文章里會包含以下內容:
- 背景
- 關于Wekan
- 關于Meteor
- 關于publication&subscription
- 關于Jade&Stylus
- 關于JS
背景
第一次知道nodejs是在12年實習時,那時只感覺寫出來的代碼好難看,覺得JS寫服務端性能肯定也不咋滴。直到2016年之前,我對于JS的使用還僅限于在網頁端偶爾用來做一些字段驗證,甚至連JQuery都不怎么會用,老忘記各種selector的語法。
16年開始接觸了一些nodejs的框架,忽然好像發現了個新天地,ES6的語法太美妙,nodejs的「單線程異步IO」的設計也可以最大限度地發揮CPU的性能。后來為了提高工作效率,開始在公司推敏捷,于是找到了一個開源的KANBAN(關于KANBAN可以看這里)工具Wekan,使用一段時間后發現少了兩個很重要的feature:
Wekan設計的理念是像Trello那樣的一個開放的KANBAN平臺,所以默認是開放注冊的,但是有很多用戶會像我這樣把它部署在自己的私服上(像gitlab一樣),所以希望有一個開關,可以控制不開放注冊,而只能邀請注冊。
在卡片中沒有一個可以check的列表,讓任務的管理者去更細粒度地控制這個任務。
于是去issue和PR里找,發現好多人都在等待著兩個feature,于是想,等他們搞不如自己搞。于是開始了第一次真正的node項目的開發。之前做過一些基于Koa和Express的開發,和Meteor還是有很大的區別。
關于Wekan
Wekan是一個開源的KANBAN(看板管理)工具,是一種較簡單(相對于Scrum和XP)的敏捷開發的理念。KANBAN工具比較有名的有Trello等。Wekan和Trello十分相似,具體的可以去查閱Wekan項目的Wiki。Wekan的優勢就是,對于不想在公共平臺上部署自己的項目管理工具的公司,以MIT協議開源的Wekan是一個很好的選擇,如果你也有這方面的需求,不妨試一試Wekan。
Wekan is an open-source kanban board which allows a card-based task and to-do management, similar to tools like WorkFlowy or Trello.
---- Wekan Wiki
從技術上講,Wekan的后端整體和前端的渲染引擎是基于Meteor的,前端的模板和樣式使用了Jade和Stylus,對于這三個東西都是比較陌生的,本文主要講的就是如何快速地融入這種技術棧不熟悉的項目開發中去。
關于Meteor
首先就是去搜索了一下關于這個框架的文章,這樣可以簡單地建立起來對它的一個感性認識。找到了一些簡單的介紹,和一個中文的社區,但是剛剛建立,資料也很少。于是果斷開始閱讀官方文檔,所幸官方文檔十分詳細。
Meteor是一個基于nodejs的全棧開發框架,致力于使開發者快速、高效地構建高響應的富客戶端Web應用。技術棧包括MongoDB、WebSocket、Blaze等。Meteor引入了客戶端DB的概念,使得Meteor開發的應用,可以天然的達到前端的實時響應。
從上圖中可以看到,服務端通過publication把一部分數據發布到前端,前端通過subscription可以直接調用這些數據。對于前端來說,它只和前端數據庫交互,基本上等于本地交互,所以響應很快,而前端數據庫再通過和后端實時同步數據來達到和后端交互的目的。
像聊天應用這種實時性要求比較高的服務,可以直接從這種架構受益,用戶幾乎感受不到延遲,而且服務器上的數據更新也會實時同步到訂閱它的客戶端。
安裝
根據官網的介紹,安裝Meteor只需要在Console中執行curl https://install.meteor.com/ | sh
,然而,由于網絡原因,在中國直接這么做簡直是折磨人,要等到花兒都謝了(除非你有一個強大的穩定的合法的VPN)。
我的做法是首先把網頁https://install.meteor.com/的內容保存到本地,下載其中TARBALL_URL
的包,保存到INSTALL_TMPDIR
中的目錄,然后注釋掉文件中的下載命令(echo "Downloading Meteor distribution"
這一行的后面的一坨),然后執行文件。
Meteor目錄結構
Meteor也遵循「convention over configuration」,它有默認的一套目錄結構,遵循這套規則可以極大地提高開發效率。Meteor項目的文件載入順序如下:
- HTML模板文件總是比其他文件先被載入
- 以main為名字的文件總是最后被載入
- lib目錄下的文件隨后被載入
- 更深目錄中的文件隨后被載入
- 其他文件按照字符順序被載入
還有一些特殊目錄:
- imports
這個文件夾中的文件不會被載入,除非使用import
語句映入到其他文件。 - client
這個文件中的文件不會被服務端的程序載入,相當于在程序中使用if (Meteor.isClient) { ... }
來執行代碼。在production環境這個文件夾下的相關文件會被壓縮。 - server
與client相對應,這里的代碼不會被客戶端程序載入。相當于在if (Meteor.isServer) { ... }
中執行。 - public
項目頂級目錄下public文件夾內的文件可以被任何人引用。
再看Wekan的目錄結構如下,項目的代碼結構和相互間的關系就很清晰了:
lkMacBook-Pro:wekan-setting lk$ tree -dL 2
.
├── client
│ ├── components
│ ├── config
│ └── lib
├── config
├── i18n
├── meta
│ ├── icons
│ ├── screenshots
│ └── t9n-changelog
├── models
├── node_modules
│ ├── fibers
│ ├── node-gyp
│ └── xss
├── public
│ └── fonts
└── server
├── lib
├── notifications
└── publications
21 directories
通過讀文檔和讀代碼,基本上對于現有項目的結構有了個初步認識,看到各個目錄和文件,大致知道其代表的意義,那么接下來就是寫代碼了。對于這種已經有了大量代碼基礎的項目,有個優勢就是「有很多可以參考的代碼」,可以幫助剛接觸的人更容易寫出更好的代碼。
關于publication&subscription
開篇時談到的兩個需求,第一個是先想到的,所以剛開始是拿這個來實驗的。分析這個需求,大約有以下幾個點:
要有限制注冊的功能
用戶注冊時,必須輸入邀請碼,否則無法注冊。要有一個默認的管理員
系統部署好之后,系統是可以任意注冊的,這時需要管理員去注冊一個賬號,然后這個賬號自然地成為管理員,然后他可以設置系統為僅限邀請用戶注冊。要有邀請碼的管理
設計一個管理頁面(Admin Panel),讓管理員可以發送邀請,邀請通過郵件發送,同時要可以配置Email發件地址。
這三點對于服務端的需求很簡單,首先增加兩張表,一張存儲系統的設置models/setting.js,一張存儲邀請碼models/invitationCode.js,然后在用戶的表中添加isAdmin的屬性。只要參照已有的一些表的寫法,肯定不會錯到哪里去,另外寫的過程中再看一看官方文檔中關于「Collection」的一章,很快就可以搞定了。
其中setting和isAdmin的屬性是需要publish給前端使用,結合上邊的一些知識,很容易就知道是在server/publication中稍微改動一些東西就好了。還好Meteor的文檔很完整也很詳細,這部分開發沒有碰到什么比較大的阻力。
publication和subscription之間的通信使用了Meteor的一個自定義的RPC協議(DDP,一種基于WebSocket的自定義協議),對這個東西并沒有作更深的探究,簡單看了一下,貌似WebSocket協議是Meteor自己實現的,并沒有使用更廣為使用的WebSocket組件ws,如果服務器不支持WebSocket,則會使用sockjs實現長輪訓 。
關于Jade&Stylus
Wekan的前端使用了一系列增加開發效率的JS組件,下面是其中涉及到賬戶系統的部分:
# Account system
accounts-password
kenton:accounts-sandstorm
service-configuration
useraccounts:core
useraccounts:unstyled
useraccounts:flow-routing
要修改和賬戶管理相關的東西,則首先要先去看一下這幾個組件(這里花了很多時間,看文檔看的想哭),然后Wekan的UI是用Jade和Stylus寫的,第一次接觸,這酸爽你懂的。幸好網上資料挺多,用習慣了之后覺得這兩個東西真的不是一般的好,強大簡介,關鍵是可以裝逼。
關于JS
隨著最近幾年node商用的越來越多,JS也已經成為成本最低的全棧開發語言,而且ES6出來之后,感覺JS其語言本身已經可以和任何一門其他語言進行媲美了,純個人觀點。
關于縮進
在開始大量寫JS之前,也寫過一段時間底層的c語言,記憶中一個Tab代表4個空格,兩個基本等價。渾然不知,Tab和空格的爭論竟然這么激烈。
一直習慣寫Tab,在提交項目時,想起來run一下eslint吧,結果一大堆的錯誤。對于開源項目來說,代碼樣式的一致性很重要,這里還是乖乖地全部改掉了。
Meteor建議開發者遵循Airbnb style guide ,仔細去翻了翻,一些要求其實是為了提高性能,比如const的大量使用,關鍵是可讀性的提高會直接帶來代碼可維護性的提高。
Wekan的eslint都配置好了,如果懶得配置編輯器的eslint插件,只需要執行meteor npm run lint
,如果結果如下,則可以放心提交了。
lkMacBook-Pro:wekan lk$ meteor npm run lint
> wekan@0.11.1-rc1 lint ~/github/wekan
> eslint .
lkMacBook-Pro:wekan lk$
后續
這里并沒有多高深的技術討論,關于Meteor的底層實現——如DDP的實現——也并沒有作很深的探究,但是做了這個項目后拓寬了一些視野,了解了node作為一個新興崛起的技術的大概和發展現狀。同時出于對WebSocket的好奇也對它做了一些小小的研究,有興趣可以看一看:刨根問底HTTP和WebSocket協議。
對于第二個需求,有了第一個的知識基礎,開發起來流暢了很多,很快就做出來了。