手寫一個合格的前端腳手架

為什么我們需要一套腳手架

為什么我們需要一套腳手架,它能幫助我們解決哪些痛點問題。

?前端項目配置越來越繁瑣、耗時,重復無意義的工作?項目結構不統一、不規范?前端項目類型繁多,不同項目不同配置,管理成本高?腳手架也可以是一套命令集,不只用來創建項目

那么為什么不用一些開源框架自身的 CLI 工具,需要自己開發呢,這里仁者見仁智者見智,我個人建議就是對于中型團隊以上需要自己維護一套腳手架,因為可控性高,能滿足團隊特定需求的研發。

如何按照開源要求開發一個前端腳手架?

下面是我們常見的前端開源目錄結構

腳手架的設計

思路

?解耦:腳手架與模板分離?腳手架負責構建流程,通過命令行與用戶交互,獲取項目信息?模板負責統一項目結構、工作流程、依賴項管理?腳手架需要檢測模板的版本是否有更新,支持模板的刪除與新建?……

流程圖

代碼講解

目錄結構

配置 Git hook

首先進行開發前的準備工作,來保證你代碼的質量。

Husky + Lint-staged

通過 Git hook 完成 commitlint、ESLint、prettiter 等,具體配置我后面會給源碼,有興趣的可以自己搜索下。

// package.json
"husky": {"hooks": {"pre-commit": "lint-staged","commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged": {"**/*.js": ["eslint --fix","prettier --write","git add"]}

package.json 下的 bin 字段

bin:配置內部命令對應的可執行文件位置,配置命令后,npm 會尋找到對應的可執行文件,然后在 node_modules/.bin 目錄下建立對應的符號鏈接。

由于 node_modules/.bin 會在運行時候加入到系統的環境變量,因此我們可以通過 npm 調用命令來執行腳本。

所有 node_modules/.bin 目錄下的命令都可以通過 npm run [命令] 執行。

所以我們需要在 package.json 配置入口

"bin": {"easy": "bin/easy.js"}

npm link 本地調試

這里介紹下開發腳手架的調試方法。npm link 官網使用介紹。使用方法:

// cd 到你項目的bin目錄(腳本)下
$ npm link

去掉 link 也非常方便:

npm unlink linkname

bin 目錄下的入口文件

#!/usr/bin/env nodeconst program = require('commander'); // 命令行工具
const chalk = require('chalk'); // 命令行輸出美化
const didYouMean = require('didyoumean'); // 簡易的智能匹配引擎
const semver = require('semver'); // npm的語義版本包
const enhanceErrorMessages = require('../lib/util/enhanceErrorMessages.js');
const requiredNodeVersion = require('../package.json').engines.node;didYouMean.threshold = 0.6;function checkNodeVersion(wanted, cliName) {// 檢測node版本是否符合要求范圍if (!semver.satisfies(process.version, wanted)) {console.log(chalk.red('You are using Node ' +process.version +', but this version of ' +cliName +' requires Node ' +wanted +'.\nPlease upgrade your Node version.'));// 退出進程process.exit(1);}
}// 檢測node版本
checkNodeVersion(requiredNodeVersion, '@easy/cli');program.version(require('../package').version, '-v, --version') // 版本.usage('<command> [options]'); // 使用信息// 初始化項目模板
program.command('create <template-name> <project-name>').description('create a new project from a template').action((templateName, projectName, cmd) => {// 輸入參數校驗validateArgsLen(process.argv.length, 5);require('../lib/easy-create')(lowercase(templateName), projectName);});// 添加一個項目模板
program.command('add <template-name> <git-repo-address>').description('add a project template').action((templateName, gitRepoAddress, cmd) => {validateArgsLen(process.argv.length, 5);require('../lib/add-template')(lowercase(templateName), gitRepoAddress);});// 列出支持的項目模板
program.command('list').description('list all available project template').action(cmd => {validateArgsLen(process.argv.length, 3);require('../lib/list-template')();});// 刪除一個項目模板
program.command('delete <template-name>').description('delete a project template').action((templateName, cmd) => {validateArgsLen(process.argv.length, 4);require('../lib/delete-template')(templateName);});// 處理非法命令
program.arguments('<command>').action(cmd => {// 不退出輸出幫助信息program.outputHelp();console.log(`  ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`));console.log();suggestCommands(cmd);
});// 重寫commander某些事件
enhanceErrorMessages('missingArgument', argsName => {return `Missing required argument ${chalk.yellow(`<${argsName}>`)}`;
});program.parse(process.argv); // 把命令行參數傳給commander解析// 輸入easy顯示幫助信息
if (!process.argv.slice(2).length) {program.outputHelp();
}// easy支持的命令
function suggestCommands(cmd) {const avaliableCommands = program.commands.map(cmd => {return cmd._name;});// 簡易智能匹配用戶命令const suggestion = didYouMean(cmd, avaliableCommands);if (suggestion) {console.log(`  ` + chalk.red(`Did you mean ${chalk.yellow(suggestion)}?`));}
}function lowercase(str) {return str.toLocaleLowerCase();
}function validateArgsLen(argvLen, maxArgvLens) {if (argvLen > maxArgvLens) {console.log(chalk.yellow('\n Info: You provided more than argument. the rest are ignored.'));}
}

其他代碼就不貼了我會給出源碼鏈接,下面分享一下幾個有意思的點。建議大家有興趣的跟著敲一遍,有很多小細節需要注意。

發布腳本

// script/release.jsconst { execSync } = require('child_process');
const semver = require('semver');
const inquirer = require('inquirer');const currentVerison = require('../package.json').version;const release = async () => {console.log(`Current easy cli version is ${currentVerison}`);const releaseActions = ['patch', 'minor', 'major'];const versions = {};// 生成預發布版本標示releaseActions.map(r => (versions[r] = semver.inc(currentVerison, r)));const releaseChoices = releaseActions.map(r => ({name: `${r} (${versions[r]})`,value: r}));// 選擇發布方式const { release } = await inquirer.prompt([{name: 'release',message: 'Select a release type',type: 'list',choices: [...releaseChoices]}]);// 優先自定義版本const version = versions[release];// 二次確認發布const { yes } = await inquirer.prompt([{name: 'yes',message: `Confirm releasing ${version}`,type: 'confirm'}]);if (yes) {execSync(`standard-version -r ${release}`, {stdio: 'inherit'});}
};release().catch(err => {console.error(err);process.exit(1);
});

npm version 與 tag

官網關于 npm version 的介紹:

https://docs.npmjs.com/cli/version.html

如果不熟悉 Node 語義化版本可以閱讀:

https://semver.org/lang/zh-CN/
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]'npm [-v | --version]' to print npm version
'npm view <pkg> version' to view a package's published version
'npm ls' to inspect current package/dependency versions

其實我們自己使用 npn publish,最終執行的還是 npm version 下命令。

官網關于 npm-dist-tag 的介紹:

npm-dist-tag[1]

npm install <name>@<tag>
npm install --tag <tag>

npm 也有 tag 的概念,一般情況下我們不會指定 tag,這個時候默認使用的就是 latest 這個 tag,所有的發布與安裝都是最新的正式版本,如果指定 tag 之后,我們可以在這個 tag 上發布一個新的版本,用戶安裝時候也可以指定這個 tag 來進行安裝,你可以簡單理解 tag 類型 git 中的 branch。

常用的一些關于 tag 的命令:

# 查看當前的tag和對應的version。
npm dist-tag ls# 查看my-package發布過的所有版本號。
npm view my-package versions# 給my-package設置tag,對應到版本version。
npm dist-tag add my-package@version tag

如果一不小心把測試版發布成了正式版?發布之前我們是這樣的:

latest: 1.0.0
next: 1.0.0-alpha.0

錯誤的把 1.0.0-alpha.1 直接 npm publish:

latest: 1.0.0-alpha.1
next: 1.0.0-alpha.0

解決方法:

# 把原來的1.0.0設置成最新的正式版
$ npm dist-tag add my-package@1.0.0 latest# 把1.0.0-alpha.1更新到最新的測試版
$ npm dist-tag add my-package@1.0.0-alpha.1 next

npm publish 一個包

1.創建一個 npm 賬戶2.cd 到你需要發布的 repo 倉庫下, 記得切換到 npm 源(或者公司內網自建源)3.npm login,需要輸入用戶名、密碼、郵箱4.npm publish

集成 CI(Travis CI)自動發布

每次手動發布太 low 了,要是可以自動發布就好了。

Travis CI 提供的是持續集成服務(Continuous Integration,簡稱 CI)。它綁定 GitHub/GitLab 等上面的項目,只要有新的代碼,就會自動抓取。然后,提供一個運行環境,執行測試,完成構建,還能部署到服務器。

持續集成指的是只要代碼有變更,就自動運行構建和測試,反饋運行結果。確保符合預期以后,再將新代碼“集成”到主干。

簡單理解就是:它的作用是自動幫你做好從代碼測試到發布的一系列流程,配合版本控制使用的話可以設置成每一次 push 都自動進行一次集成,保證代碼的正確性。

注意現在 GitHub 也出了集成工具,感興趣的可以去體驗下。

如果你的項目是在 GitHub 并且是開源的,推薦使用這個?org[2]

使用 GitHub 進行登錄 Travis CI,完成一些授權工作,Travis CI 才能監聽到你的 GitHub 項目代碼的變化。

Travis CI 要求你項目的根目錄必須有一個配置文件 .travis.yml 文件,這是一個配置文件,指定 travis 的行為,該文件還必須保存在 GitHub 的倉庫。一旦有新的 push,travis 就會找到這個文件進行執行。

關于 travis 更多使用推薦閱讀官網[3],這里主要講下利用?travis 自動發布包到 npm[4],Continuous Integration environments[5]

下面是一個 .travis.yml 配置文件:

language: node_js
node_js:- '8'
cache:directories:- node_modules
install:- npm install
script:- npm run lint
deploy:provider: npmemail: "$NPM_EMAIL"api_key: "$AUTH_TOKEN"skip_cleanup: trueon:branch: master
# after_success:

然后在你的 travis 上選擇需要開啟 CI 的項目。

配置對應環境變量到該倉庫下如:

環境變量名格式必須為“大寫字母_大寫字母”格式。

token 生成也非常簡單,官網[6]介紹,可以直接在你的 npm 賬戶下的 tokens 頁面手動生成或者通過 npm 命令行生成。

# 切換到npm源下, 登陸npm
npm login# 生成token, npm可以指定生成token的權限(只讀或者可讀可寫)
npm token create

然后配置一些腳本來執行 npm version,這樣當你包版本有更新后 push 到 GitHub repo,就會觸發 travis 自動發包到 npm。

DEMO

源碼鏈接[7]

推薦閱讀

若川知乎高贊:有哪些必看的 JS庫?
我在阿里招前端,我該怎么幫你?(現在還可以加模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer
如何準備阿里P6/P7前端面試--項目經歷準備篇
大廠面試官常問的亮點,該如何做出?
如何從初級到專家(P4-P7)打破成長瓶頸和有效突破
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?

末尾

你好,我是若川,江湖人稱菜如若川,歷時一年只寫了一個學習源碼整體架構系列~(點擊藍字了解我)

  1. 關注若川視野,回復"pdf" 領取優質前端書籍pdf,回復"1",可加群長期交流學習

  2. 我的博客地址:https://lxchuan12.gitee.io?歡迎收藏

  3. 覺得文章不錯,可以點個在看呀^_^另外歡迎留言交流~

精選前端好文,伴你不斷成長

若川原創文章精選!可點擊

小提醒:若川視野公眾號面試、源碼等文章合集在菜單欄中間【源碼精選】按鈕,歡迎點擊閱讀,也可以星標我的公眾號,便于查找

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276233.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276233.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276233.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第一篇cnblog!

本人才疏學淺&#xff0c;終于通過了cnblog的審核&#xff0c;興奮之余&#xff0c;發表感言——不容易啊&#xff01;在我的博聞里面&#xff0c;隨筆類當然就是技術類的比較多的&#xff0c;特別是實例類的。理論類的大部分放在文章板塊&#xff0c;本人e文特別好(哈哈&#…

解決error 1045: Access denied for user: 'root@localhost' (Using password: YES)

轉載連接&#xff1a;http://jianfw2009.blog.163.com/blog/static/13431366020111016112459158/ 1、先停止mysql服務2、在mysql的目錄下找到my.ini&#xff0c;在[mysqld]后面加上skip-grant-tables3、啟動mysql服務,打開Command Line Client以空密碼登錄4、退出mysql,并停止服…

fillcolor是什么意思_fill是什么意思

1. (使)裝滿;(使)注滿;(使)充滿If you fill a container or area, or if it fills, an amount of something enters it that is enough to make it full.e.g.Fill a saucepan with water and bring to a slow boil...往平底鍋里加滿水,小火煮沸。e.g.She made sandwiches, fill…

利用JMeter進行壓力測試(1)(轉)

轉自&#xff1a;http://www.cnblogs.com/game-over/archive/2010/01/08/1642685.html壓力測試以軟件響應速度為測試目標&#xff0c;尤其是在較短時間內大量并發用戶的同時訪問時&#xff0c;軟件的性能和抗壓能力。 JMeter是一款開源的壓力測試工具&#xff0c;目前最新Rele…

MyISAM InnoDB 區別

轉載鏈接&#xff1a;http://www.php100.com/html/webkaifa/database/Mysql/2011/0326/7789.html MyISAM 和 InnoDB 講解 InnoDB和MyISAM是許多人在使用MySQL時最常用的兩個表類型&#xff0c;這兩個表類型各有優劣&#xff0c;視具體應用而定。基本的差別為&#xff1a;MyISAM…

Git 內部原理圖解——對象、分支以及如何從零開始建倉庫

我們中的許多人每天都在使用 git&#xff0c;但是有多少人知道它的內部是怎么運作的呢&#xff1f;例如我們使用 git commit 時發生了什么&#xff1f;提交&#xff08;commit&#xff09;與提交之間保存的是什么&#xff1f;兩次提交之間難道只是文件的差異&#xff08;dif…

wpsmac和pc版的區別_Mac版WPS Office和微軟Office 2019哪個更好?

眾所周知&#xff0c; macOS系統生態下&#xff0c;有許多界面精美、功能強大、體驗出色的軟件&#xff0c;但提到辦公套件&#xff0c;人們首先想到的還是微軟 Office 套件&#xff0c;其中的Word、 Excel 以及PPT&#xff0c;但用戶體驗并不如意。但現在&#xff0c;蘋果用戶…

A tutorial video for MindManager for free

MindManager 2016 for Windows Essential Training 本人學習的時候使用的是MindManager 2018版本的&#xff0c;和2016版本差異不大。 轉載于:https://www.cnblogs.com/kelamoyujuzhen/p/10253278.html

Google, 請不要離開我們!

雖然我是.net陣營, 力挺Silverlight, 但是我真心希望谷歌留在中國, 如果她能夠靠談判求的言論自由的權利, 那將對中國的擁有自由信仰的一族產生重大的影響. 谷歌離開了中國, 不是她想拋棄中國市場, 而是中國決策者背叛了人性. 在此留下 Google 2010年1月14日的logo, 智慧的幽默…

高級php面試題及部分答案

轉載鏈接&#xff1a;http://www.2cto.com/kf/201304/201112.html 一. 基本知識點 1.1 HTTP協議中幾個狀態碼的含義:503 500 401 403 404 200 301 302。。。 200 : 請求成功&#xff0c;請求的數據隨之返回。 301 : 永久性重定向。 302 : 暫時行重定向。 401 : 當前請求需要用…

iec104點號_IEC104報文流程(有常用類型標識解釋)

參數地址圍類別97版基地址2002版基地址遙信1H------400H1H------4000H遙測701H------900H4001H------5000H遙控B01------B806001H------6100H設點B81H------C00H6201H------6400H電度C01H------C80H6401H------6600H遙測和遙信個數不設置上限&#xff0c;可以沒有上限限制&…

本周ASP.NET英文技術文章推薦[04/08 - 04/14](附贈自彈超級瑪麗主題曲)

摘要 本期共有6篇文章&#xff1a; ASP.NET編譯問題的公開Hotfix補丁 期待下個版本AjaxPro 的發布 在ASP.NET 2.0中使用MultiView控件實現多頁面表單 數據綁定的技巧&#xff1a;嵌套Eval語句 在ASP.NET 2.0中訪問并更新數據&#xff1a;使用數據源控件以編程方式訪問數據 AD…

一個離開某門戶網站人員自爆黑幕

去年&#xff0c;我已在星星發表了一個關于免費發短信的各類軟件的黑幕所在。而事實上的SMS&#xff08;即短信&#xff09;的黑幕遠不止于此&#xff0c;今天&#xff0c;我終于有空坐下來&#xff0c;把其中的一些讓你感覺平常卻實際觸目驚心的事情告訴你們&#xff0c;讓你們…

28歲自學3年前端成功轉行的勵志故事

為什么轉行因為混得不好。在成為程序員之前&#xff0c;我干過很多工作。由于學歷的問題&#xff08;高中&#xff09;&#xff0c;我的工作基本上都是體力活。包括但不限于&#xff1a;工廠普工、銷售&#xff08;沒有干銷售的才能&#xff09;、搬運工、擺地攤等&#xff0c;…

css中!important的作用

轉載鏈接&#xff1a;http://www.cnblogs.com/guoguo-15/archive/2011/08/24/2151859.html css中!important的作用 {*rule !important}這個css規則當今在網頁制作的時候的普及已經非常流行了&#xff0c;以前我對它的理解就停留在‘瀏覽器是否識別階段’ 而沒有真正去研究過&am…

word2vec應用場景_word2vec的使用參數解釋和應用場景

" > corpus.txt因為這些數據雖然去除了其他標簽的數據&#xff0c;但是卻把保留下來了&#xff0c;所以后來作者在分詞程序中去除了這個標簽我在這個[網頁](http://www.jb51.net/article/65497.htm)上找到了一個python去標簽的簡單代碼。但是沒有實驗過&#xff0c;不知…

usb 驅動

usb 驅動學習總結&#xff1a; usb 采用分層的拓撲結構&#xff0c;金字塔型&#xff0c;最多是7層。usb 是主從結構&#xff0c;主和主或者從和從之間不能交換數據。理論上一個usb主控制器最多可接127個設備&#xff0c;協議規定每個usb設備具有一個7bit的地址&#xff0c;范圍…

讓Spring架構減化事務配置(轉)

讓Spring架構減化事務配置(轉) 注&#xff1a;原文章曾發表在it168Spring顛覆了以前的編程模式&#xff0c;引入了IOC等全新的概念&#xff0c;廣受大家的喜愛。目前大多數j2ee項目都已經采用Spring框架。Spring最大的問題是太多的配置文件&#xff0c;使得你不僅需要維護程序代…

面試字節跳動后的2點總結,建議收藏!

首先我來辟個謠&#xff1a;隨便打開一個招聘網站&#xff0c;你會發現前端工程師的崗位需求依舊龐大&#xff0c;大廠人才奇缺&#xff0c;就業薪資起點高&#xff0c;無行業限制。&#xff08;數據來源&#xff1a;職友集&#xff09;前端開發的行業大環境行業升級&#xff0…

phpexcel中文教程-設置表格字體顏色背景樣式、數據格式、對齊方式、添加圖片、批注、文字塊、合并拆分單元格、單元格密碼保護

轉載連接&#xff1a;http://www.cnblogs.com/huangcong/p/3687665.html phpexcel中文教程-設置表格字體顏色背景樣式、數據格式、對齊方式、添加圖片、批注、文字塊、合并拆分單元格、單元格密碼保護 首先到phpexcel官網上下載最新的phpexcel類&#xff0c;下周解壓縮一個cla…