本文首發於 Array_Huang的技術博客——實用至上
,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-xwrwoszw-bz.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏:https://segmentfault.com/blog/array_huang
本文介紹如何在多項目間共用同一套基礎設施,又或是某種層次的框架。javascript
一個完整的網站,不可能只包含一個jQuery,或是某個MVVM框架,其中一定包含了許多解決方案,例如:如何上傳?如何兼容IE?如何跨域?如何使用本地存儲?如何作用戶信息反饋?又或者具體到如何選擇日期?等等等等……這裏面一定包含了UI框架、JS框架、各類小工具庫,不管是第三方的仍是本身團隊研發的。而以上所述的種種,就構成了一套完整的解決方案,也稱基礎設施。css
基礎設施有個重要的特徵,那就是與業務邏輯無關,不管是OA仍是CMS又或是CRM,只要總體產品形態相似,咱們就可使用同一套基礎設施。html
框架這個概念很泛,泛得讓人心生困惑,但抽象出來講,框架就是一套定義代碼在哪裏寫、怎麼寫的規則。不能說咱們要怎麼去用框架,反卻是框架控制咱們怎麼去填代碼。前端
本系列前面的十來篇文章,分開來看是不一樣的點,但若是全部文章合起來,並連同示例項目(Array-Huang/webpack-seed),實際上闡述的就是一套完整的多頁應用框架(或稱架構)。這套框架規定了整個應用的方方面面,舉幾個例子:java
固然,這只是個人框架,我但願大家能夠看懂了,而後根據本身的需求來調整,變成大家的框架。甚至說,我本身在作不一樣類型的項目時,總體架構也都會有很多的變化。jquery
數月前,我找同事要了一個他本身寫的地區選擇器,拉回來一看遍地都是ESLint的報錯(他負責的項目沒有用ESLint,比較隨意),我這人有強迫症的怎麼看得過眼,捲起袖子就開始改,改好也就正常使用了。過了一段時間,來了新需求,同事在他那改好了地區選擇器又發了一份給我,我一看頭都大了,又是滿地報錯,這不是又要我再改一遍嗎?當時我就懵了,只好按着他的思路,對個人版本作了修改。今後,也確立了咱們公司會有兩份外觀功能都一致,可是實現卻不同的地區選擇器。webpack
很坑爹是吧?git
上面說的是組件級的,下面咱們來講架構級別的。github
我在公司主要負責的項目有兩個,在個人不懈努力下,已經作到跟個人腳手架項目Array-Huang/webpack-seed大致上同構了。但維持同構顯然是要付出代價的,我在腳手架項目試驗過的改進,小至改個目錄路徑,大至引入個plugin啊loader啊什麼的,都要分別在公司的兩個項目裏各作一遍,超煩噠(嫌棄臉web
試想只是兩個項目就已經這樣了,若是是三個、四個,甚至六個、七個呢?堪憂啊堪憂啊!
不知道大家有沒有這樣子的經驗:接到新項目時,靈機一動「這不就是個人XX項目嗎?」,而後趕忙搬出XX項目的源碼,而後刪掉業務邏輯,保留可複用的基礎設施。
也許你會說,這不已經比從零開始要好多了嗎?整體上來講,是吧,但還不夠好:
以上這些問題,你每建立一個新項目都要經歷一遍,我問你怕了沒有。
是的沒錯,腳手架自己就算是一整套基礎設施了,但依然有下列問題:
在以前的文章裏,我使用的一直都是Array-Huang/webpack-seed這個腳手架項目做爲示例,而爲了實踐多項目共用基礎設施,我對該項目的架構作了較大幅度的調整,升級爲2.0.0版本。爲免你們看前面的文章時發現示例項目貨不對板,感到困惑,我新開了一個repo來存放調整後的腳手架:Array-Huang/webpack-seed-v2(https://github.com/Array-Huang/webpack-seed-v2
),而且,我在兩個項目的README裏我都註明了相應的內容,你們可不要混淆了哈。
下面就以從Array-Huang/webpack-seed到Array-Huang/webpack-seed-v2的改造過程來介紹如何實現多項目共用基礎設施。
改造思路其實很簡單,就是把預想中多個項目都能用得上的部分從現有項目裏抽離出來。
抽離的說法是針對原項目的,若是單純從文件系統的角度來講,只不過是移動了某些文件和目錄。
移動到哪裏了呢?天然是移動到與項目目錄同級的地方,這樣就方便多個項目引用這個核心了。
若是你跟我同樣,在原項目中定義了大量路徑和alias的話,移動這些文件/目錄就只是個改變量的活了:
選自webpack-seed/webpack-config/base/dir-vars.config.js
:
var path = require('path'); var moduleExports = {}; // 源文件目錄 moduleExports.staticRootDir = path.resolve(__dirname, '../../'); // 項目根目錄 moduleExports.srcRootDir = path.resolve(moduleExports.staticRootDir, './src'); // 項目業務代碼根目錄 moduleExports.vendorDir = path.resolve(moduleExports.staticRootDir, './vendor'); // 存放全部不能用npm管理的第三方庫 moduleExports.dllDir = path.resolve(moduleExports.srcRootDir, './dll'); // 存放由各類不常改變的js/css打包而來的dll moduleExports.pagesDir = path.resolve(moduleExports.srcRootDir, './pages'); // 存放各個頁面獨有的部分,如入口文件、只有該頁面使用到的css、模板文件等 moduleExports.publicDir = path.resolve(moduleExports.srcRootDir, './public-resource'); // 存放各個頁面使用到的公共資源 moduleExports.logicDir = path.resolve(moduleExports.publicDir, './logic'); // 存放公用的業務邏輯 moduleExports.libsDir = path.resolve(moduleExports.publicDir, './libs'); // 與業務邏輯無關的庫均可以放到這裏 moduleExports.configDir = path.resolve(moduleExports.publicDir, './config'); // 存放各類配置文件 moduleExports.componentsDir = path.resolve(moduleExports.publicDir, './components'); // 存放組件,能夠是純HTML,也能夠包含js/css/image等,看本身須要 moduleExports.layoutDir = path.resolve(moduleExports.publicDir, './layout'); // 存放UI佈局,組織各個組件拼起來,因應須要能夠有不一樣的佈局套路 // 生成文件目錄 moduleExports.buildDir = path.resolve(moduleExports.staticRootDir, './build'); // 存放編譯後生成的全部代碼、資源(圖片、字體等,雖然只是簡單的從源目錄遷移過來) module.exports = moduleExports;
選自webpack-seed/webpack-config/resolve.config.js
:
var path = require('path'); var dirVars = require('./base/dir-vars.config.js'); module.exports = { // 模塊別名的配置,爲了使用方便,通常來講全部模塊都是要配置一下別名的 alias: { /* 各類目錄 */ iconfontDir: path.resolve(dirVars.publicDir, 'iconfont/'), configDir: dirVars.configDir, /* vendor */ /* bootstrap 相關 */ metisMenu: path.resolve(dirVars.vendorDir, 'metisMenu/'), /* libs */ withoutJqueryModule: path.resolve(dirVars.libsDir, 'without-jquery.module'), routerModule: path.resolve(dirVars.libsDir, 'router.module'), libs: path.resolve(dirVars.libsDir, 'libs.module'), /* less */ lessDir: path.resolve(dirVars.publicDir, 'less'), /* components */ /* layout */ layout: path.resolve(dirVars.layoutDir, 'layout/html'), 'layout-without-nav': path.resolve(dirVars.layoutDir, 'layout-without-nav/html'), /* logic */ cm: path.resolve(dirVars.logicDir, 'common.module'), cp: path.resolve(dirVars.logicDir, 'common.page'), /* config */ configModule: path.resolve(dirVars.configDir, 'common.config'), bootstrapConfig: path.resolve(dirVars.configDir, 'bootstrap.config'), }, // 當require的模塊找不到時,嘗試添加這些後綴後進行尋找 extentions: ['', 'js'], };
抽離的方法很簡單,那麼關鍵就看究竟是哪些部分能夠抽離、須要抽離了,這一點看我抽離後的成果就比較清晰了:
先來看根目錄:
├─ core # 抽離出來的基礎設施,或稱「核心」 ├─ example-admin-1 # 示例項目1,被抽離後剩下的 ├─ example-admin-2 # 示例項目2,嗯,簡單起見,直接複製了example-admin-1,不過仍是要作一點調整的,好比說配置 ├─ npm-scripts # 沒想到npm-scripts也能公用吧? ├─ vendor # 沒法在npm上找到的第三方庫 ├─ .eslintrc # ESLint的配置文件 ├─ package.json # 全部的npm庫依賴建議都寫到這裏,不建議寫到具體項目的package.json裏
再來看看core
目錄
├─ _webpack.dev.config.js # 整理好公用的開發環境webpack配置,以備繼承 ├─ _webpack.product.config.js # 整理好公用的生產環境webpack配置,以備繼承 ├─ webpack-dll.config.js # 用來編譯Dll文件用的webpack配置文件 ├─ manifest.json # Dll文件的資源目錄 ├─ package.json # 沒有什麼實質內容,我這裏就放了個編譯Dll用的npm script ├─components # 各類UI組件 │ ├─footer │ ├─header │ ├─side-menu │ └─top-nav ├─config # 公共配置,有些是提供給具體項目的配置來繼承的,有些自己就有用(好比說「核心」部分自己須要的配置) ├─dll # 以前的文章裏就說過,我建議把各類第三方庫(包括npm庫也包括非npm庫)都打包成Dll來加速webpack編譯過程,這部分明顯就屬於基礎設施了 ├─iconfont # 字體圖標能不能公用,這點我也是比較猶豫的,看項目實際須要吧,不折騰的話仍是推薦公用 ├─layout # 佈局,既然是同類型項目,佈局確定是基本同樣的 │ ├─layout │ └─layout-without-nav ├─less # 樣式基礎,在我這項目裏就是針對bootstrap的SB-Admin主題作了修改 │ ├─base-dir │ └─components-dir ├─libs # 本身團隊研發的一些公共的方法/庫,又或是針對第三方庫的適配器(好比說對alert庫封裝一層,後面要更換庫的時候就方便了) ├─npm-scripts # 與根目錄下的npm-scripts目錄不同,這裏的不是用來公用的,而是「核心」使用到的script,好比我在這裏就放了編譯dll的npm script └─webpack-config # 公用的webpack配置,尤爲是關係到「核心」部分的配置,好比說各第三方庫的alias。這裏的配置是用來給具體項目來繼承的,老實說我如今繼承的方法也比較複雜,回頭看看有沒有更簡單的方法。 ├─base ├─inherit └─vendor
最後總結一下,是哪些資源被抽離出來了:
上傳上來之後發現圖被壓小了,請到這裏看原圖
https://segmentfault.com/a/1190000006843916
https://segmentfault.com/a/1190000006863968
https://segmentfault.com/a/1190000006871991
https://segmentfault.com/a/1190000006887523
https://segmentfault.com/a/1190000006897458
https://segmentfault.com/a/1190000006907701
https://segmentfault.com/a/1190000006952432
https://segmentfault.com/a/1190000006992218
https://segmentfault.com/a/1190000007030775
https://segmentfault.com/a/1190000007043716
https://segmentfault.com/a/1190000007104372
https://segmentfault.com/a/1190000007126268
https://segmentfault.com/a/1190000007159115
https://segmentfault.com/a/1190000007301770
本文首發於 Array_Huang的技術博客——實用至上
,非經做者贊成,請勿轉載。
原文地址: http://www.javashuo.com/article/p-xwrwoszw-bz.html
若是您對本系列文章感興趣,歡迎關注訂閱這裏:https://segmentfault.com/blog/array_huang