搭建一个通用的脚手架
在16年年底的時(shí)候,同事聊起腳手架。由于公司業(yè)務(wù)的多樣性,前端的靈活性,讓我們不得不思考更通用的腳手架。而不是伴隨著前端技術(shù)的發(fā)展,不斷的把時(shí)間花在配置上。于是chef-cli誕生了。 18年年初,把過(guò)往一年的東西整理和總結(jié)下,重新增強(qiáng)了原有的腳手架project-next-cli, 不單單滿足我們團(tuán)隊(duì)的需求,也可以滿足其他人的需求。
<!--more-->
project-next-cli
面向的目標(biāo)用戶:
- 公司業(yè)務(wù)雜,但有一定的積累
- 愛(ài)折騰的同學(xué)和團(tuán)隊(duì)
- 借助github大量開(kāi)發(fā)模板開(kāi)發(fā)
發(fā)展
前端這幾年(13年-15年)處于高速發(fā)展,主要表現(xiàn):
備注:以下發(fā)展過(guò)程出現(xiàn),請(qǐng)不要糾結(jié)出現(xiàn)順序 [捂臉]
- 庫(kù)/框架:jQuery, backbone, angular,react,vue
- 模塊化:commonjs, AMD(CMD), UMD, es module
- 任務(wù)管理器:npm scripts, grunt, gulp
- 模塊打包工具: r.js, webpack, rollup, browserify
- css預(yù)處理器:Sass, Less, Stylus, Postcss
- 靜態(tài)檢查器:flow/typescript
- 測(cè)試工具:mocha,jasmine,jest,ava
- 代碼檢測(cè)工具:eslint,jslint
開(kāi)發(fā)
當(dāng)我們真實(shí)開(kāi)發(fā)中,會(huì)遇到各種各樣的業(yè)務(wù)需求(場(chǎng)景),根據(jù)需求和場(chǎng)景選用不同的技術(shù)棧,由于技術(shù)的進(jìn)步和不同瀏覽器運(yùn)行時(shí)的限制,不得不配置對(duì)應(yīng)的環(huán)境等,導(dǎo)致我們從而滿足業(yè)務(wù)需求。
畫(huà)了一張圖來(lái)表示,業(yè)務(wù),配置(環(huán)境),技術(shù)之間的關(guān)系
前端配置工程師
于是明見(jiàn)流傳了一個(gè)新的職業(yè),前端配置工程師 O(∩_∩)O~
社區(qū)現(xiàn)狀
專一的腳手架
社區(qū)中存在著大量的專一型框架,主要針對(duì)一個(gè)目標(biāo)任務(wù)做定制。比如下列腳手架
vue-cli提供利用vue開(kāi)發(fā)webpack, 以及 遠(yuǎn)程克隆生成文件等 pwa等模板,本文腳手架參考了vue-cli的實(shí)現(xiàn)。
dva-cli 針對(duì)dva開(kāi)發(fā)使用的腳手架
think-cli 針對(duì) thinkjs項(xiàng)目創(chuàng)建項(xiàng)目
通用腳手架
yeoman是一款強(qiáng)壯的且有一系列工具的通用型腳手架,但yeoman發(fā)布指定package名稱,和用其開(kāi)發(fā)工具。具體可點(diǎn)擊這里查看yeoman添加生成器規(guī)則
開(kāi)發(fā)初衷和目標(biāo)
由于公司形態(tài)決定了,業(yè)務(wù)類型多樣,前端技術(shù)發(fā)展迭代,為了跟進(jìn)社區(qū)發(fā)展,更好的完成下列目標(biāo)而誕生。
- 完成業(yè)務(wù):專心,穩(wěn)定,快速
- 團(tuán)隊(duì)規(guī)范:代碼規(guī)范,測(cè)試流程,發(fā)布流程
- 沉淀:專人做專事,持續(xù)穩(wěn)定的迭代更新,跟進(jìn)時(shí)代
- 效益:少加班,少造輪子,完成kpi,做更有意義的事兒
實(shí)現(xiàn)準(zhǔn)備
依托于Github,根據(jù)Github API來(lái)實(shí)現(xiàn),如下:
實(shí)現(xiàn)邏輯
根據(jù)github api獲取到項(xiàng)目列表和版本號(hào)之后,根據(jù)輸入的名稱,選擇對(duì)應(yīng)的版本下載到本地私有倉(cāng)庫(kù),生成到執(zhí)行目錄下。核心流程圖如下:。
總體設(shè)計(jì)
- 使用Node進(jìn)行腳手架開(kāi)發(fā),版本選擇 >=6.0.0
- 選用async/await開(kāi)發(fā),解決異步回調(diào)問(wèn)題
- 使用babel編譯
- 使用ESLint規(guī)范代碼
遵守單一職責(zé)原則,每個(gè)文件為一個(gè)單獨(dú)模塊,解決獨(dú)立的問(wèn)題。可以自由組合,從而實(shí)現(xiàn)復(fù)用。以下是最終的目錄結(jié)構(gòu):
├── LICENSE ├── README.md ├── bin │?? └── project ├── package.json ├── src │?? ├── clear.js │?? ├── config.js │?? ├── helper │?? │?? ├── metalAsk.js │?? │?? ├── metalsimth.js │?? │?? └── render.js │?? ├── index.js │?? ├── init.js │?? ├── install.js │?? ├── list.js │?? ├── project.js │?? ├── search.js │?? ├── uninstall.js │?? ├── update.js │?? └── utils │?? ├── betterRequire.js │?? ├── check.js │?? ├── copy.js │?? ├── defs.js │?? ├── git.js │?? ├── loading.js │?? └── rc.js └── yarn.lock配置和主框架
使用babel-preset-env保證版本兼容
{"presets": [["env", {"targets": {"node": "6.0.0"}}]] }使用eslint管理代碼
- eslint demo
使用husky檢測(cè)提交
使用husky, 來(lái)定義git-hooks, 規(guī)范git代碼提交流程,這里只做 commit校驗(yàn)
在package.json配置如下:
"husky": {"hooks": {"pre-commit": "npm run lint"} }入口
統(tǒng)一配置和入口,分發(fā)到不同單一文件,執(zhí)行輸出。核心代碼
function registerAction(command, type, typeMap) {command.command(type).description(typeMap[type].desc).alias(typeMap[type].alias).action(async () => {try {if (type === 'help') {help();} else if (type === 'config') {await project('config', ...process.argv.slice(3));} else {await project(type);}} catch (e) {console.log(e);help();}});return command; }本地配置讀和寫(xiě)
配置用來(lái)獲取腳手架的基本設(shè)置, 如registry, type等基本信息。
本地配置文件, 格式是 .ini
若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示
下面每個(gè)命令的實(shí)現(xiàn)邏輯。
下載
獲取項(xiàng)目列表
- https://api.github.com/orgs/p...
獲取tag列表
若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示
請(qǐng)求代碼
- request
下載代碼
- download-git-repo
生成項(xiàng)目
若中間每一步 數(shù)據(jù)為空/文件不存在/生成目錄已重復(fù) 則給予提示
其中模板引擎編譯實(shí)現(xiàn)核心代碼如下:
// metalsmith邏輯 function metal(answers, tmpBuildDir) {return new Promise((resolve, reject) => {metalsmith.metadata(answers).source('./').destination(tmpBuildDir).clean(false).use(render()).build((err) => {if (err) {reject(err);return;}resolve(true);});}); } // metalsmith render中間件實(shí)現(xiàn) function render() {return function _render(files, metalsmith, next) {const meta = metalsmith.metadata();/* eslint-disable */Object.keys(files).forEach(function(file){const str = files[file].contents.toString();consolidate.swig.render(str, meta, (err, res) => {if (err) {return next(err);}files[file].contents = new Buffer(res);next();});})} }升級(jí)/降級(jí)版本
若中間每一步 數(shù)據(jù)為空/文件不存在 則給予提示
搜索
搜索遠(yuǎn)程的github倉(cāng)庫(kù)有哪些項(xiàng)目列表
若中間每一步 數(shù)據(jù)為空 則給予提示
總結(jié)
以上是這款通用腳手架產(chǎn)生的背景,針對(duì)用戶以及具體實(shí)現(xiàn),該腳手架目前還有一些可以優(yōu)化的地方:
硬廣:如果您覺(jué)得project-next-cli好用,歡迎star,也歡迎fork一塊維護(hù)。
總結(jié)
以上是生活随笔為你收集整理的搭建一个通用的脚手架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: find_in_set()和in()比较
- 下一篇: 力扣(LeetCode)78