第一次據說腳手架, 是我剛接觸Vue,跟着網上大佬的文章,用Vue-cli從0搭建了一個Vue項目,一步一步配置,而後npm i, npm run dev,打開連接,一個網頁就這麼寫好了,當時對於npm,webpack這些前端工程化一無所知,嘴裏不自覺的吐出了兩個字:'NB'。一年之後,一位新猿在Segmentfault上發出了這樣的提問:css
而後我就裝着知道的樣子就去回答了一下,答案是這樣的:html
粗狂的講,這樣的回答,好像沒什麼毛病。但既然是本着學習的態度,那此次就好好的講一講前端腳手架存在的意義,究竟是個什麼鬼,以及寫一個腳手架到底有多難?因此接下來,文章將圍繞下面幾部分來討論:前端
隨着前端工程化的概念愈來愈深刻人心,腳手架的出現就是爲減小重複性工做而引入的命令行工具,擺脫ctrl + c, ctrl + v,此話zenjiang? 如今新建一個前端項目,已經不是在html頭部引入css,尾部引入js那麼簡單的事了,css都是採用Sass或則Less編寫,在js中引入,而後動態構建注入到html中;除了學習基本的js,css語法和熱門框架,還須要學習構建工具webpack,babel這些怎麼配置,怎麼起前端服務,怎麼熱更新;爲了在編寫過程當中讓編輯器幫咱們查錯以及更加規範,咱們還須要引入ESlint;甚至,有些項目還須要引入單元測試(Jest)。對於一個更入門的人來講,這無疑會讓人望而卻步。而前端腳手架的出現,就讓事情簡單化,一鍵命令,新建一個工程,再執行兩個npm命令,跑起一個項目。在入門時,無需關注配置什麼的,只須要開心的寫代碼;另外,對於不少系統,他們的頁面類似度很是高,因此就能夠基於一套模板來搭建,雖然是不一樣的人開發,但用腳手架來搭建,相同的項目結構與代碼書寫規範,是很利於項目的後期維護的;以上就是爲何腳手架存在的意義, 讓項目從"搭建-開發-部署"更加快速以及規範 (出自於某乎達人)。vue
如今流行的前端腳手架都是基於NodeJs編寫,好比前面提到的Vue-CLI,比較火的create-react-app,還有Dva-CLI和我司本身curie,都是熱門框架react和vue的項目腳手架,其功能都是生成一個通用的目錄結構,並配上構建、編譯、檢查等工程環境。大體流程以下:node
先以create-react-app爲例,網上有不少讀create-react-app源碼的文章,能夠網上搜索一下,推薦一篇。它的代碼很少,讀起來也比較容易,最主要的是create-react-app在某種程度上來說它只作了1,2步的事情(固然4也作了一些),第3步是由react-scripts完成的,固然其還有一個重要做用,就是做爲這個項目的構建編譯工具,因此你在package.json中還能看到下圖靠右這樣的命令(熟悉的nmp start, 有木有):react
因此咱們知道,create-react-app主要解析命令,執行文件的操做,react-scripts主要提供模板與模板所須要的項目工程化配置,如上圖左側所示,咱們能看到其包含了webpack與jest測試的相關配置文件。
而vue-cli的實現與create-react-app稍微有點不同。首先vue-cli新建工程是那種一步一步問答式命令來進行個性化定製的,然後者是一鍵搞定的;vue-cli的項目模板來源於github,支持多種模板(可經過vue list查看),經過git下載的,具體模板配置參照這裏,然後者模板來自於react-scripts目錄下的兩個文件夾(上圖中的template與template-typescript);vue-cli構建的項目其構建編譯是直接依賴於browserify或webpack這樣的構建工具,配置是徹底暴露給使用者的,能夠再次自定義,然後者是基於webpack進行了一層封裝,而後將其暴露爲一種新的構建命令,固然也能夠運行npm run eject將配置暴露出來。dva是一個比較成熟的react解決方案,比較適合中後臺系統,dva-cli與前二者具備較強的類似性,採用roadhog做爲其構建編譯工具,開發人員,無須關心構建配置,只需關心業務代碼的實現(因此大廠的碼農本身不注意的話容易發展成碼畜)。其還有一個擴展功能,就是項目生成後,能夠採用命令添加一個頁面,這樣確實也能減小一部分的ctrl + c, ctrl + v。
綜上,前端腳手架的實質包含兩項,命令式的構建項目(解析命令,拷貝項目到本地),提供項目的配置(構建,編譯,代碼規範檢查)。webpack
從上兩節的描述,大體整理一下即將要實現的功能:git
以上,感受是否是實現特簡單。不是感受,是確實很簡單。流程以下:github
命令解析:web
// 四種模板。對應我git倉庫四個倉庫地址 const tempIndex = { react: 'reactTemplate', // react 模板 vue: 'vueTemplate', // vue 模板 h5: 'h5Template', // h5模板 dva: 'dvaTemplate', // dva模板 }; let projectName; // 存儲目錄 let templateName; // 模板名稱 let inputIndex; // 除了拷貝模板,還支持自定義模板路徑下載,但感受有點畫蛇添足 const program = new commander.Command(packageJson.name) .version('v' + packageJson.version, '-v, --version') .arguments('<templateName>') .arguments('<projectName>') .option('-f, --force', 'force delete the exist director') .option('-d, --directly', 'copy the not specified template') .alias('cp') .description('create-doddle react myProject') .action(function (index,name) { inputIndex = index; // 容許目標項目名和要複製的模板類型名順序顛倒 if (tempIndex[index] || tempIndex[name]) { if (tempIndex[index]) { templateName = tempIndex[index]; projectName = name; } else { templateName = tempIndex[name]; projectName = index; } } if (program.directly) { templateName = index; } }); program.parse(process.argv) // 沒有輸入任何參數,報語法錯誤,並打印help if (program.args.length === 0) { console.log(chalk.red('syntax error')); program.help() } if (templateName) { excute(templateName, projectName, program.force); } else { console.log(`the template ${inputIndex} you want download do not exist`); }
文件拷貝:
async function create(temp, project, force = false) { tempName = temp; projectName = project; forceDel = force; const file = currentPath + projectName; try { // 檢測項目文件夾是否已存在, 若存在,拋出錯誤 const res = await fs.pathExists(file); if (res) { if (forceDel) { console.log(green('force remove the exist directory')); await fs.remove(file); downloadByGit(renameFile, tempName); } else { // 拋出錯誤,並提示可使用-f參數來強制刪除已存在的項目 console.log(chalk.red('Error, In this directory, the project name already exsits !')); console.log(chalk.green('you can use option -f to force delete the directory !')); } return; } // 若不存在,直接從git下載 downloadByGit(renameFile, tempName); } catch (err) { console.error(red(err)); } }
關於chalk,這是一款顏色標記插件,將要打印的文字用不一樣的顏色標記出來,像下面這樣:
git文件下載:
function downloadByGit(callback, template) { console.log(green('start download')); console.log(`git@github.com:closertb/${template}.git`); const result = spawn( 'git', ['clone', `git@github.com:closertb/${template}.git`], { stdio: 'inherit' } ); const error = result.error; if (error) { console.log(red(error)); return; } // 定義回調; callback && callback(); }
主要就這三段代碼,就實現了命令的解析,和從git源端拷貝模板到本地。
稍微對前端工程化了解的就知道,對於項目,想建立npm run start或npm run dev這樣的可執行命令,只須要在package.json的scripts進行定義。而想要建立vue init webpack myapp或dva init myapp這樣的命令又怎樣作呢?廣告以後,立刻揭曉(自娛自樂中,請忽視O(∩~∩)O!)
第一步:在你項目的package.json中填上一個bin屬性,代表它是可執行的, 並配置好可執行命令和入口文件
第二步: 在你的入口文件(我這裏是根目錄的index.js)首行加上一段代碼: #!/usr/bin/env node,告訴操做系統執行這個腳本的時候,調用/usr/bin下的node解釋器;
第三步:登陸你的npm帳號,運行npm publish發佈你的npm包,參考過的連接
到此,一個簡易的腳手架就寫好了。
人老了,總喜歡最後嘮叨兩句,其實不論vue-cli,仍是create-react-app,或則dva,其核心功能(最牛逼的)不是命令的解析或者模板的拷貝,這部分只佔了它腳手架很小的部分,看起來多,是由於它作了不少兼容,好比幫助、錯誤檢測、系統網絡環境檢測、回滾這些操做,但這也不是核心。我的以爲核心仍是是react-scripts或則roadhog這些構建編譯腳本,但歸根接地仍是得對babel和webpack這些庫的深刻理解,送給觀看這篇文章到這裏的你,也勉勵一下本身,繼續加油。
項目源碼地址: create-doddle
npm包地址:create-doddle
首發地址:Denzel Blog