文章發表以後,也不免會有一些新的想法。若是新開一篇文章,會顯得散亂;而直接修改正文,那些已經閱讀過本篇文章的讀者又沒法快速定位修改的內容。所以,新增修訂
章節,記錄修訂歷史javascript
CabloyJS最大的亮點是:經過pc=mobile+pad
的模式,把mobile場景的操控體驗
和開發模式
帶⼊pc場景。既顯著減小了代碼開發量,提高了開發效率,⼜保持了用戶操控體驗的⼀致性html
CabloyJS最大的痛點是:經過模塊化的架構設計,能夠快速開發全場景業務前端
場景 | 前端 | 後端 |
---|---|---|
PC:Web | CabloyJS前端 | CabloyJS後端 |
PC:Exe | CabloyJS前端 + Electron | CabloyJS後端 |
Mobile:IOS | CabloyJS前端 + Cordova | CabloyJS後端 |
Mobile:Android | CabloyJS前端 + Cordova | CabloyJS後端 |
微信公共號 | CabloyJS前端 + 微信API | CabloyJS後端 |
企業微信 | CabloyJS前端 + 微信API | CabloyJS後端 |
釘釘 | CabloyJS前端 + 釘釘API | CabloyJS後端 |
Slack | CabloyJS前端 + Slack API | CabloyJS後端 |
小程序:微信、支付寶、百度等 | 小程序框架 | CabloyJS後端 |
CabloyJS是一款頂級NodeJS全棧業務開發框架
既可快速開發,又可靈活定製
爲了實現此理念,CabloyJS內置開發了大量核心模塊,使您能夠在最短的時間內架構一個完整的Web項目。好比,當您新建一個Web項目時,就已經具有完整的用戶登陸與認證系統,也具備驗證碼功能,同時也具有用戶管理
、角色管理
、權限管理
等功能vue
此外,這些內置模塊提供了靈活的定製特性,您也能夠開發全新的模塊來替換內置模塊,從而實現系統的定製化java
Mobile場景
咱們知道,隨着智能機的日益普及,我們開發人員所面對的需求場景與開發場景日益碎片化,如瀏覽器、IOS、Android,還有大量第三方平臺:微信、企業微信、釘釘、Facebook、Slack等等node
隨着智能設備性能愈來愈好,網速愈來愈快,針對如此衆多的開發場景,採用H5開發必將是大勢所趨。只需開發一套代碼,就能夠在以上全部智能設備中運行,不只能夠顯著減小開發量,同時也能夠顯著提高開發效率,對開發團隊和終端用戶均是莫大的福利mysql
PC場景
以上我們說H5開發,只需開發一套代碼,就能夠在全部智能設備中運行。可是還有一個開發場景沒有獲得統一:那就是PC場景
git
因爲屏幕顯示尺寸的不一樣,PC場景
和Mobile場景
有着不一樣的操做風格。有些前端UI框架,採用「自適應」策略,爲PC場景開發的頁面,在Mobile場景下雖然也能查看和使用,但使用體驗每每差強人意github
這也就是爲何有些前端框架老是成對出現的緣由:如Element-UI和Mint-UI,如AntDesign和AntDesign-Mobile
這也就意味着,當咱們同時面對PC場景
和Mobile場景
時,仍然須要開發兩套代碼。在面對許多開發需求時,這些重複的工做量每每是難以接受的:sql
CabloyJS前端採用Framework7框架,目前已同步升級到最新版Framework7 V4。CabloyJS在Framework7的基礎上進行了巧妙的擴展,將PC端的頁面切分爲多個區域,實現了多個Mobile和PAD同時呈如今一個PC端的效果。換句話說,你買了一臺Mac,就相對於買了多臺IPhone和IPad,用多個虛擬的移動設備同時工做,即顯著提高了工做效率,也提供了很是有趣的使用體驗
有圖有真相
也可PC端體驗
也可手機掃描體驗
CabloyJS是模塊化的全棧框架,爲了實現PC = MOBILE + PAD
的風格,內置了兩個模塊:egg-born-module-a-layoutmobile
和egg-born-module-a-layoutpc
。當前端框架加載完畢,會自動判斷當前頁面的寬度(稱爲breakpoint),若是小於800,使用Mobile佈局,若是大於800,使用PC佈局,並且breakpoint數值能夠自定義
此外,這兩個佈局模塊自己也有許多參數能夠自定義,甚至,您也能夠開發本身的佈局模塊,替換掉內置的實現方式
下面分別貼出兩個佈局模塊的默認參數,相信您一看便知他們的用處
egg-born-module-a-layoutmobile
export default { layout: { login: '/a/login/login', loginOnStart: true, toolbar: { tabbar: true, labels: true, bottom: true, }, tabs: [ { name: 'Home', tabLinkActive: true, iconMaterial: 'home', url: '/a/base/menu/list' }, { name: 'Atom', tabLinkActive: false, iconMaterial: 'group_work', url: '/a/base/atom/list' }, { name: 'Mine', tabLinkActive: false, iconMaterial: 'person', url: '/a/user/user/mine' }, ], }, };
egg-born-module-a-layoutpc
export default { layout: { login: '/a/login/login', loginOnStart: true, header: { buttons: [ { name: 'Home', iconMaterial: 'dashboard', url: '/a/base/menu/list', target: '_dashboard' }, { name: 'Atom', iconMaterial: 'group_work', url: '/a/base/atom/list' }, ], mine: { name: 'Mine', iconMaterial: 'person', url: '/a/user/user/mine' }, }, size: { small: 320, top: 60, spacing: 10, }, }, };
NodeJS的蓬勃發展,爲先後端開發帶來了更順暢的體驗,顯著提高了開發效率。但仍有網友質疑NodeJS可否勝任大型Web應用的開發。大型Web應用的特色是隨着業務的增加,須要開發大量的頁面組件。面對這種場景,通常有兩種解決方案:
CabloyJS實現了第三種解決方案:
在CabloyJS中,一切業務開發皆以業務模塊爲單位。好比,咱們要開發一個CMS建站工具,就新建一個業務模塊,如已經實現的模塊egg-born-module-a-cms
。該CMS模塊包含十多個Vue頁面組件,在正式發佈時,就會構建成一個JS包。在運行時,只需異步加載這一個JS包,就能夠訪問CMS模塊中任何一個Vue頁面組件了。
所以,在一個大型的Web系統中,哪怕有數十甚至上百個業務模塊,按CabloyJS的模塊化策略進行代碼組織和開發,既不會出現單一巨大的部署包,也不會出現大量碎片化的JS構建文件。
CabloyJS的模塊化系統還有以下顯著的特色:
也就是說,前面說到的模塊化異步打包策略是已經精心調校好的系統核心特性,咱們只需像平時同樣開發Vue頁面組件,在構建時系統會自動進行模塊級別的打包,同時在運行時進行異步加載
咱們仍然以CMS模塊爲例,經過縮減的代碼直觀的看一下代碼風格,若是想了解進一步的細節,能夠直接查看對應的源碼(下同,再也不贅述)
如何查看源碼:進入項目的node_modules目錄,查看
egg-born-
爲前綴的模塊源碼便可
egg-born-module-a-cms/src/module/a-cms/front/src/routes.js
function load(name) { return require(`./pages/${name}.vue`).default; } export default [ { path: 'config/list', component: load('config/list') }, { path: 'config/site', component: load('config/site') }, { path: 'config/siteBase', component: load('config/siteBase') }, { path: 'config/language', component: load('config/language') }, { path: 'config/languagePreview', component: load('config/languagePreview') }, { path: 'category/list', component: load('category/list') }, { path: 'category/edit', component: load('category/edit') }, { path: 'category/select', component: load('category/select') }, { path: 'article/contentEdit', component: load('article/contentEdit') }, { path: 'article/category', component: load('article/category') }, { path: 'article/list', component: load('article/list') }, { path: 'article/post', component: load('article/post') }, { path: 'tag/select', component: load('tag/select') }, { path: 'block/list', component: load('block/list') }, { path: 'block/item', component: load('block/item') }, ];
能夠看到,在前端頁面路由的定義中,仍然是採用平時的同步加載寫法
關於模塊的異步加載機制是由核心模塊egg-born-front
來完成的,參見源碼egg-born-front/src/base/module.js
每一個業務模塊都是自洽的總體,包含與本模塊業務相關的前端代碼和後端代碼,並且採用先後端分離模式
模塊自洽
既有利於自身的高度內聚
,也有利於整個系統的充分解耦
。業務模塊只須要考慮自身的邏輯實現,容易實現業務的充分沉澱與分享
,達到即插即用
的效果
舉一個例子:若是咱們要開發文件上傳功能,當咱們在網上找到合適的上傳組件以後,在本身的項目中使用時,仍然須要開發大量對接代碼。也就是說,在網上找到的上傳組件沒有實現充分的沉澱,不是自洽的,也就不能實現便利的分享,達到即插即用
的效果
而CabloyJS內置的的文件上傳模塊egg-born-module-a-file
就實現了功能的充分沉澱。爲何呢?由於業務模塊自己就包含前端代碼和後端代碼,可以施展的空間很大,能夠充分細化上傳邏輯
所以,在CabloyJS中要調用文件上傳功能,就會變得極其便捷。以CMS模塊爲例,上傳圖片並取得圖片URL,只需短短20行代碼
egg-born-module-a-cms/src/module/a-cms/front/src/pages/article/contentEdit.vue
... onUpload(mode, atomId) { return new Promise((resolve, reject) => { this.$view.navigate('/a/file/file/upload', { context: { params: { mode, atomId, }, callback: (code, data) => { if (code === 200) { resolve({ text: data.realName, addr: data.downloadUrl }); } if (code === false) { reject(); } }, }, }); }); }, ...
在大型Web項目中,不可避免的要考慮各種資源、各類變量、各個實體之間命名的衝突問題。針對這個問題,不一樣的開發團隊大都會規範各種實體的命名規範。隨着項目的擴充,這種命名規範仍然會變得很龐雜。若是咱們面對的是一個開放的系統,使用的是來自不一樣團隊開發的模塊,所面臨的命名衝突的風險就會愈加嚴重
CabloyJS使用了一個巧妙的設計,一勞永逸解決了命名衝突的隱患。在CabloyJS中,業務模塊採用以下命名規範:
egg-born-module-{providerId}-{moduleName}
providerId
: 開發者Id,強烈建議採用Github的Username,從而確保貢獻到社區的模塊不會衝突moduleName
: 模塊名稱因爲模塊自洽
的設計機制,咱們只須要解決模塊命名的惟一性問題,在進行模塊開發時就不會再被命名衝突的困擾所糾纏了
好比,CMS模塊提供了一個前端頁面路由config/list
。很顯然,如此簡短的路徑,在其餘業務模塊中出現的機率很是高。但在CabloyJS中,如此命名就不會產出衝突。在CMS模塊內部進行頁面跳轉時,能夠直接使用config/list
,這稱之爲相對路徑
引用。可是,若是其餘業務模塊也想跳轉至此頁面就使用/a/cms/config/list
,這稱之爲絕對路徑
引用
再好比,前面的例子咱們要調用上傳文件頁面,就是採用絕對路徑
:/a/file/file/upload
模塊隔離
是業務模塊的核心特性。這是由於,模塊前端和後端有大量實體都須要進行這種隔離。CabloyJS從系統層面完成了這種隔離的機制,從而使得咱們在實際的模塊業務開發時能夠變得輕鬆、便捷。
模塊前端隔離機制
模塊前端的隔離機制由模塊egg-born-front
來完成,實現了以下實體的隔離:
模塊後端隔離機制
模塊後端的隔離機制由模塊egg-born-backend
來完成,實現了以下實體的隔離:
後端Service隔離,不只是解決命名衝突的須要,更是性能提高方面重要的考量。好比有50個業務模塊,每一個模塊有20個Service,這樣全局就有1000個Service。 在EggJS中,這1000個Service須要一次性預加載以便供Controller代碼調用。CabloyJS就在EggJS的基礎上作了隔離處理,若是是模塊A的Controller,只須要預加載模塊A的20個Service,供模塊A的Controller調用。這樣,就實現了一箭雙鵰:不只命名隔離,並且性能提高,從而知足大型Web系統開發的需求
後端Model是CabloyJS實現的訪問數據實體的便捷工具,在Model的定義和使用上,都比Sequelize簡潔、高效與後端Service同樣,後端Model也實現了命名隔離,同時也只能被模塊自身的Controller和Service調用
CabloyJS採用WebPack進行項目的前端構建。因爲CabloyJS項目是由一系列業務模塊組成的,所以,能夠把模塊代碼提早預編譯,從而在構建整個項目的前端時就能夠顯著提高構建速度
經實踐,若是一個項目包含40個業務模塊,若是按照普通的構建模式須要70秒構建完成。而採用預編譯的機制,則只須要20秒便可完成。這對於開發大型Web項目具備顯著的工程意義
CabloyJS中的業務模塊,不只前端代碼能夠構建,後端代碼也能夠用WebPack進行構建。後端代碼在構建時,也能夠指定是否醜化,這種機制能夠知足保護商業代碼
的需求
CabloyJS後端的基礎是EggJS,是如何作到能夠編譯構建的呢?
CabloyJS後端在EggJS的基礎上進行了擴展,每一個業務模塊都有一個入口文件main.js,經過main.js串聯後端全部JS代碼,所以能夠輕鬆實現編譯構建
CabloyJS從2016年啓動開發,主要歷經兩個開發階段:
EggBornJS關注的核心就是實現一套完整的以業務模塊爲核心的全棧開發框架
好比模塊egg-born-front
是框架前端的核心模塊,模塊egg-born-backend
是框架後端的核心模塊,模塊egg-born
是框架的命令行工具,用於建立項目骨架
這也是爲何全部業務模塊都是以egg-born-module-
爲命名前綴的緣由
EggBornJS只是一個基礎的全棧開發框架,若是要進行業務開發,還須要考慮許多與業務相關的支撐特性,如:用戶管理
、角色管理
、權限管理
、菜單管理
、參數設置管理
、表單驗證
、登陸機制
,等等。特別是在先後端分離的場景下,對權限管理
的要求就提高到一個更高的水平
CabloyJS在EggBornJS的基礎上,提供了一套核心業務模塊,從而實現了一系列業務支撐特性,並將這些特性進行有機的組合,造成完整而靈活的上層生態架構,從而支持具體的業務開發進程
換句話說,從實質上看,CabloyJS是一組核心業務模塊的組合,從形式上看,CabloyJS是一組模塊依賴項。且看CabloyJS的package.json文件:
cabloy/package.json
{ "name": "cabloy", "version": "2.1.2", "description": "The Ultimate Javascript Full Stack Framework", ... "author": "zhennann", "license": "ISC", ... "dependencies": { "egg-born-front": "^4.1.0", "egg-born-backend": "^2.1.0", "egg-born-bin": "^1.2.0", "egg-born-scripts": "^1.1.0", "egg-born-module-a-version": "^2.2.2", "egg-born-module-a-authgithub": "^2.0.3", "egg-born-module-a-authsimple": "^2.0.3", "egg-born-module-a-base-sync": "^2.0.10", "egg-born-module-a-baseadmin": "^2.0.3", "egg-born-module-a-cache": "^2.0.3", "egg-born-module-a-captcha": "^2.0.4", "egg-born-module-a-captchasimple": "^2.0.3", "egg-born-module-a-components-sync": "^2.0.5", "egg-born-module-a-event": "^2.0.2", "egg-born-module-a-file": "^2.0.2", "egg-born-module-a-hook": "^2.0.2", "egg-born-module-a-index": "^2.0.2", "egg-born-module-a-instance": "^2.0.2", "egg-born-module-a-layoutmobile": "^2.0.2", "egg-born-module-a-layoutpc": "^2.0.2", "egg-born-module-a-login": "^2.0.2", "egg-born-module-a-mail": "^2.0.2", "egg-born-module-a-markdownstyle": "^2.0.3", "egg-born-module-a-mavoneditor": "^2.0.2", "egg-born-module-a-progress": "^2.0.2", "egg-born-module-a-sequence": "^2.0.2", "egg-born-module-a-settings": "^2.0.2", "egg-born-module-a-status": "^2.0.2", "egg-born-module-a-user": "^2.0.3", "egg-born-module-a-validation": "^2.0.4", "egg-born-module-test-cook": "^2.0.2" } }
相信您經過這些核心模塊的名稱,就已經猜到這些模塊的用處了
根據前面兩階段的分析,咱們就能夠勾勒出框架的總體架構圖
這種架構,讓整個體系變得井井有條,也讓實際的Web項目的源代碼文件組織結構變得很是簡潔直觀。大量的架構細節都封裝在EggBornJS中,而咱們的Web項目只須要引用一個CabloyJS便可,CabloyJS負責引用架構中其餘核心模塊
這種架構,也讓實際的Web項目的升級變得更加容易,具體以下:
1) 刪除現有模塊依賴項 $ rm -rf node_modules 2) 若是有此文件,建議刪除 $ rm -rf package-lock.json 3) 從新安裝全部模塊依賴項 $ npm i
有了EggBornJS,今後可複用的不只僅是組件,還有業務模塊有了CabloyJS,您就能夠快速開發各種業務應用
業務模塊必然要處理數據而且存儲數據,固然也不可避免會出現數據架構的變更,好比新增表、新增字段、刪除字段、調整舊數據,等等
CabloyJS經過巧妙的數據版本控制,可讓業務模塊在不斷的迭代過程當中,無縫的完成模塊升級和數據升級
在數據版本的基礎上,再配合一套開發流程,從而不管是在開發環境仍是生產壞境,都能有順暢的開發與使用體驗
能夠經過package.json指定業務模塊的數據版本,以模塊egg-born-module-test-cook
爲例
egg-born-module-test-cook/package.json
{ "name": "egg-born-module-test-cook", "version": "2.0.2", "eggBornModule": { "fileVersion": 1, "dependencies": { "a-base": "1.0.0" } }, ... }
模塊當前的數據版本fileVersion
爲1
。當這個模塊正式發佈出去以後,爲1
的數據版本就處於封閉狀態。當有新的迭代,須要改變模塊的數據架構時,就須要將fileVersion
遞增爲2
。以此類推,從而完成模塊數據架構的自動無縫升級
當CabloyJS後端服務在啓動時,會自動檢測每一個業務模塊的數據版本,當存在數據版本變動時,就會自動調用業務模塊的升級代碼,從而完成自動升級。仍以模塊egg-born-module-test-cook
爲例,其數據版本升級代碼以下:
egg-born-module-test-cook/backend/src/service/version.js
... async update(options) { if (options.version === 1) { let sql = ` CREATE TABLE testCook ( id int(11) NOT NULL AUTO_INCREMENT, createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted int(11) DEFAULT '0', iid int(11) DEFAULT '0', atomId int(11) DEFAULT '0', cookCount int(11) DEFAULT '0', cookTypeId int(11) DEFAULT '0', PRIMARY KEY (id) ) `; await this.ctx.model.query(sql); sql = ` CREATE TABLE testCookType ( id int(11) NOT NULL AUTO_INCREMENT, createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted int(11) DEFAULT '0', iid int(11) DEFAULT '0', name varchar(255) DEFAULT NULL, PRIMARY KEY (id) ) `; await this.ctx.model.query(sql); sql = ` CREATE VIEW testCookView as select a.*,b.name as cookTypeName from testCook a left join testCookType b on a.cookTypeId=b.id `; await this.ctx.model.query(sql); sql = ` CREATE TABLE testCookPublic ( id int(11) NOT NULL AUTO_INCREMENT, createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted int(11) DEFAULT '0', iid int(11) DEFAULT '0', atomId int(11) DEFAULT '0', PRIMARY KEY (id) ) `; await this.ctx.model.query(sql); } } ...
當數據版本變動時,CabloyJS後端調用方法update
,經過判斷屬性options.version
的值,進行對應版本的數據架構變動
那麼問題來了?在模塊開發階段,若是須要變動數據架構怎麼辦呢?由於模塊尚未正式發佈,因此,不須要鎖定數據版本。也就是說,若是當前數據版本fileVersion
是1
,那麼在正式發佈以前,不論進行多少次數據架構變動,fileVersion
還是1
一方面,咱們確定要修改方法update
,加入架構變動的代碼邏輯,好比添加表、添加字段等等
另外一方面,咱們還要修改當前測試數據庫中的數據架構。由於fileVersion
是沒有變化的,因此當重啓CabloyJS後端服務時,方法update
並不會再次執行
針對這種狀況,首先想到的是手工修改測試數據庫中的數據架構。而CabloyJS提供了更優雅的機制
咱們知道EggJS提供了三個運行環境:測試環境
、開發環境
、生產環境
。CabloyJS在EggJS的基礎上,對這三個運行環境賦予了進一步的意義
{項目目錄}/src/backend/config/config.unittest.js
module.exports = appInfo => { const config = {}; ... // mysql config.mysql = { clients: { // donnot change the name __ebdb: { host: '127.0.0.1', port: '3306', user: 'root', password: '', database: 'sys', // donnot change the name }, }, }; ... return config; };
$ npm run test:backend
因爲咱們將測試環境
的數據庫名稱設爲sys
,那麼CabloyJS就會自動刪除舊的測試數據庫,創建新的數據庫。由於是從新建立數據庫,那麼也就意味着fileVersion
由0
升級爲1
,從而觸發方法update
的執行,進而自動完成數據架構的升級
{項目目錄}/src/backend/config/config.local.js
module.exports = appInfo => { const config = {}; ... // mysql config.mysql = { clients: { // donnot change the name __ebdb: { host: '127.0.0.1', port: '3306', user: 'root', password: '', database: 'sys', // recommended }, }, }; ... return config; };
$ npm run dev:backend
雖然咱們也將開發環境
的數據庫名稱設爲sys
,可是CabloyJS會自動尋找最新建立的測試數據庫,而後一直使用它
{項目目錄}/src/backend/config/config.prod.js
module.exports = appInfo => { const config = {}; ... // mysql config.mysql = { clients: { // donnot change the name __ebdb: { host: '127.0.0.1', port: '3306', user: 'root', password: '', database: '{實際數據庫名}', }, }, }; ... return config; };
$ npm run start:backend
由於生產環境
存儲的都是實際業務數據,因此在生產環境
就要設置實際的數據庫名稱了
根據前面數據版本
和運行環境
的分析,咱們就能夠規劃出一套關於開發流程
的最佳實踐:
npm run test:backend
,用於自動建立一個測試數據庫npm run dev:backend
來啓動項目後端服務,用於調試fileVersion
和方法update
以後,再一次執行npm run test:backend
,從而重建一個新的測試數據庫npm run start:backend
來啓動後端服務CabloyJS經過多實例
的概念來支持多域名站點
的開發。啓動一個服務,能夠支持多個實例運行。實例共享數據表架構,但運行中產生的數據是相互隔離的
這有什麼好處呢?好比您用CabloyJS開發了一款CRM的SAAS服務,那麼只需開發並運行一個服務,就能夠同時服務多個不一樣的客戶。每一個客戶一個實例,用一個單獨的域名進行區分便可。
再好比,要想開發一款基於微信公共號的營銷平臺,提供給不一樣的客戶使用,多實例與多域名
是最天然、最有效的架構設計。
具體信息,請參見
const conn = await app.mysql.beginTransaction(); // 初始化事務 try { await conn.insert(table, row1); // 第一步操做 await conn.update(table, row2); // 第二步操做 await conn.commit(); // 提交事務 } catch (err) { // error, rollback await conn.rollback(); // 必定記得捕獲異常後回滾事務!! throw err; }
CabloyJS在EggJS的基礎上進行了擴展,使得數據庫事務處理
變得更加天然,甚至能夠說是無痛處理
在CabloyJS中,實際的代碼邏輯不用考慮數據庫事務
,若是哪一個後端API路由須要啓用數據庫事務
,直接在API路由上聲明一箇中間件transaction
便可,以模塊egg-born-module-test-cook
爲例
egg-born-module-test-cook/backend/src/routes.js
... { method: 'get', path: 'test/echo/:id', controller: test, action: 'echo', middlewares: 'transaction' }, ...
CabloyJS把用戶系統
與身份認證系統
徹底分離,有以下好處:
好比,用戶A
先經過用戶名/密碼
註冊的身份,之後還能夠添加Github、微信
等認證方式好比,
用戶B
先經過Github
註冊的身份,之後還能夠添加用戶名/密碼
等認證方式
CabloyJS把驗證碼機制抽象了出來,而且提供了一個缺省的驗證碼模塊egg-born-module-a-captchasimple
,您也能夠按統一規範開發本身的驗證碼模塊,而後掛接到系統中
CabloyJS也實現了通用的郵件發送功能,基於成熟的nodemailer
。因爲nodemailer
內置了一個測試服務器,所以,在開發環境中,不須要真實的郵件發送帳號,也能夠進行系統的測試與調試
前面咱們談到CabloyJS中的業務模塊是自洽的,能夠單獨編譯打包,既能夠顯著提高總體項目打包的效率,也能夠知足保護商業代碼
的需求。這裏咱們看看模塊編譯與發佈的基本操做
$ cd /path/to/module 1) 構建前端代碼 $ npm run build:front 2) 構建後端代碼 $ npm run build:backend
後端爲何默認關閉醜化選項呢?答:CabloyJS全部內置的核心模塊都是關閉醜化選項的,這樣便於您直觀的調試整個系統的源代碼,也能夠很容易走進CabloyJS,發現一些更有趣的架構設計
{模塊目錄}/build/config.js
module.exports = { productionSourceMap: true, uglify: false, };
當項目中的模塊代碼穩定後,能夠將模塊公開發布,貢獻到開源社區。也能夠在公司內部創建npm私有倉庫,而後把模塊發佈到私有倉庫,造成公司資產,便於重複使用
$ cd /path/to/module $ npm publish
到目前爲止,實話說,前面談到的概念大多屬於EggBornJS的層面。CabloyJS在EggBornJS的基礎上,開發了大量核心業務模塊,從而支持業務層面的快速開發。下面咱們就介紹一些基本概念
原子是CabloyJS最基本的要素,如文章、公告、請假單,等等
爲何叫原子?在化學反應中,原子是最基本的粒子。在CabloyJS中,經過原子的組合,就能夠實現任何想要的功能,如CMS、OA、CRM、ERP,等等
好比,您所看到的這篇文章就是一個原子
正因爲從各類業務模型
中抽象出來一個通用的原子
概念,於是,CabloyJS爲原子實現了許多通用的特性和功能,從而能夠便利的爲各種實際業務賦能
好比,模塊CMS中的文章能夠發表評論
,能夠點贊
,支持草稿
、搜索
功能。這些都是CabloyJS核心模塊egg-born-module-a-base-sync
提供的通用特性與功能。只要新建一個原子類型,這些原子都會被賦能
這就是
抽象
的力量
全部原子數據都會有一些相同的字段屬性,也會有與業務相關的字段屬性。相同的字段都統一存儲到數據表aAtom
中,與業務相關的字段存儲在具體的業務表
中,aAtom
與業務表
是一對一的關係
這種存儲機制體現了共性
與差別性
的有機統一,有以下好處:
數據權限
增刪改查
等操做星標、標籤、草稿、搜索
等操做關於原子
的更多信息,請參見
角色
是面向業務系統開發最核心的功能之一,CabloyJS提供了既簡潔又靈活的角色體系
CabloyJS的角色體系不一樣於網上流行的RBAC模型
RBAC模型
沒有解決業務開發中資源範圍受權
的問題。好比,Mike
是軟件部的員工,只能查看本身的日誌;Jone
是軟件部經理,能夠查看本部門的日誌;Jimmy
是企業負責人,能夠查看整個企業的日誌
RBAC模型
概念複雜,在實際應用中,又每每引入新的概念(用戶組、部門、崗位等),使得角色體系疊牀架屋
,理解困難,維護繁瑣
涉及到角色體系,每每會有這些概念:用戶
、用戶組
、角色
、部門
、崗位
、受權對象
等等
而CabloyJS設計的角色體系只有用戶
、角色
、受權對象
等概念,概念精簡,層次清晰,靈活高效,既便於理解,又便於維護
部門
從本質上來講,其實就是角色,如:軟件部
、財務部
等等
崗位
從本質上來講,其實也就是角色,如:軟件部經理
、軟件部設計崗
、軟件部開發崗
等等
資源範圍
也是角色。如:Jone
是軟件部經理,能夠查看軟件部
的日誌。其中,軟件部
就是資源範圍
CabloyJS針對各種業務開發的需求,提煉了一套內置角色
,並造成一個規範的角色樹
。實際開發中,可經過對角色樹
的擴充和調整,知足各種角色相關的需求
root
authenticated
organization
名稱 | 說明 |
---|---|
root | 角色根節點,包含全部角色 |
anonymous | 匿名 角色,凡是沒有登陸的用戶自動納入匿名 角色 |
authenticated | 認證 角色 |
template | 模版 角色,可爲模版角色配置一些基礎的、通用的權限 |
registered | 已註冊 角色 |
activated | 已激活 角色 |
superuser | 超級用戶 角色,如用戶root 屬於超級用戶 角色 |
organization | 組織 角色 |
internal | 內部組織 角色,如可添加軟件部 、財務部 等子角色 |
external | 外部組織 角色,可爲合做夥伴提供角色資源 |
CabloyJS是先後端分離的模式,對API接口權限
的控制需求就提高到一個更高的水平。CabloyJS提供了一個很是天然直觀的權限控制方式
好比模塊egg-born-module-a-baseadmin
有一個API接口role/children
,是要查詢某角色的子角色清單。這個API接口只容許管理員用戶訪問,咱們能夠這樣作
咱們把須要受權的對象抽象爲功能
。這樣處理有一個好處:就是一個功能
能夠綁定1個或多個API接口
。當咱們對一個功能
賦予了權限,也就對這一組綁定的API接口
進行了訪問控制
先定義一個功能
:role
egg-born-module-a-baseadmin/backend/src/meta.js
... functions: { role: { title: 'Role Management', }, }, ...
再將功能
與API接口
綁定
egg-born-module-a-baseadmin/backend/src/routes.js
... { method: 'post', path: 'role/children', controller: role, meta: { right: { type: 'function', name: 'role' } } }, ...
名稱 | 說明 |
---|---|
right | 全局中間件right ,默認處於開啓狀態,只需配置參數便可 |
type | function : 判斷功能受權 |
name | 功能的名稱 |
接下來,咱們就須要把功能role
受權給角色superuser
,而管理員用戶歸屬於角色superuser
,也就擁有了訪問API接口role/children
的權限
功能受權
有兩種途徑:
工具
> 功能權限管理
,進行受權配置便可前面談到,針對各種業務數據,CabloyJS抽象出來原子
的概念。對數據訪問
受權,也就是對原子受權
原子受權
主要解決這類問題:誰
能對哪一個範圍內
的原子數據
執行什麼操做
,基本格式以下:
角色 | 原子類型 | 原子指令 | 資源範圍 |
---|---|---|---|
superuser | todo | read | 財務部 |
角色superuser
僅能讀取財務部
的todo
數據
更詳細信息,強烈建議參見
在實際的業務開發中,不免會遇到一些流程需求。好比,CMS中的文章,在做者提交以後,能夠轉入審覈員進行審覈,審覈經過以後方能發佈
當原子數據進入流程時,在不一樣的節點,處於不一樣的狀態(審覈中、已發佈),只能由指定的角色進行節點的操做
CabloyJS經過原子標記
和原子指令
的配合實現了一個簡單的流程機制。也就是說,對於大多數簡單流程場景,不須要複雜的流程引擎
,就能夠在CabloyJS中很輕鬆的實現
更詳細信息,強烈建議參見
前面說到CabloyJS研發經歷了兩個階段:
若是說還有第三階段的話,那就是解決方案
階段。EggBornJS構建了完整的NodeJS全棧開發體系,CabloyJS提供了大量面向業務開發的核心模塊。那麼,在EggBornJS和CabloyJS的基礎上,接下來就能夠針對不一樣的業務場景,研發相應的解決方案
,解決實際的業務問題
CabloyJS是一個單頁面、先後端分離的框架,而有些場景(如博客
、社區
等)更看重SEO、靜態化
CabloyJS針對這類場景,專門開發了一個模塊egg-born-module-a-cms
,提供了一套文章靜態渲染
的機制。CabloyJS自己自然的成爲CMS的後臺管理系統,從而造成動靜結合
的特色,主要特性以下:
具體信息,請參見
CabloyJS以CMS模塊爲基礎,開發了一個社區模塊egg-born-module-cms-sitecommunity
,配置方式與CMS模塊徹底同樣,只需選用不一樣的社區主題
便可輕鬆搭建一個交流社區(論壇)
Atwood定律: 凡是能夠用JavaScript來寫的應用,最終都會用JavaScript來寫
CabloyJS將來規劃的核心之一,就是持續輸出高質量的解決方案
,爲提高廣大研發團隊的開發效率不懈努力
CabloyJS以及全部核心模塊均已開源,歡迎你們加入CabloyJS,發Issue,點Star,提PR,更但願您能開發更多的業務模塊,共建CabloyJS的繁榮生態
最後再來聊聊框架名稱的由來
這個名稱的由來比較簡單,由於有了Egg,因此就有了EggBorn。有一部動畫片叫《天書奇譚》,裏面的萌主就叫「蛋生」,我很喜歡看(不當心暴露了年齡😅)
Cabloy來自藍精靈的魔法咒語,只有拼對了Cabloy這個單詞纔會有神奇的效果。一樣,CabloyJS是有關JS的魔法,基於模塊的組合與生化反應,您將實現您想要的任何東西
親,您也能夠拼對Cabloy吧!這但是神奇的魔法喲!