教你從零開始搭建一款前端腳手架工具

本文系原創,轉載請附帶作者信息:Jrain Lau
項目地址:https://github.com/jrainlau/s...

前言

在實際的開發過程中,從零開始建立項目的結構是一件讓人頭疼的事情,所以各種各樣的腳手架工具應運而生。筆者使用較多的yoemanexpress-generatorvue-cli便是當中之一。它們功能豐富,但最核心的功能都是能夠快速搭建一個完整的項目的結構,開發者只需要在生成的項目結構的基礎上進行開發即可,非常簡單高效。

作為一個不折騰會死星人,在熟悉了使用方法以后就開始琢磨起它們的原理來了。經過仔細研究文檔和源碼,終于算是摸清了其核心的原理,并且依據這個原理自己搭建了一款叫做SCION的腳手架。

現在讓我們就以SCION為例,從零開始搭建一款屬于我們自己的腳手架工具吧!

核心原理

yoeman搭建項目需要提供yoeman-generatoryoeman-generator本質上就是一個具備完整文件結構的項目樣板,用戶需要手動地把這些generator下載到本地,然后yoeman就會根據這些generator自動生成各種不同的項目。

vue-cli提供了相當豐富的選項和設定功能,但是其本質也是從遠程倉庫把不同的模版拉取到本地,而并非是什么“本地生成”的黑科技。

這樣看來,思路也就有了——首先建立不同的樣板項目,然后腳手架根據用戶的指令引用樣板項目生成實際項目。樣板項目可以內置在腳手架當中,也可以部署在遠程倉庫。為了更廣的適用范圍,SCION采用的是第二種方式。

技術選型

  • node.js:整個腳手架工具的根本組成部分,推薦使用最新的版本。

  • es6:新版本的node.js對于es6的支持度已經非常高,使用es6能夠極大地提升開發效率和開發感受。

  • commander:TJ大神開發的工具,能夠更好地組織和處理命令行的輸入。

  • co:TJ大神開發的異步流程控制工具,用更舒服的方式寫異步代碼。

  • co-prompt:還是TJ大神的作品……傳統的命令行只能單行一次性地輸入所有參數和選項,使用這個工具可以自動提供提示信息,并且分步接收用戶的輸入,體驗類似npm init時的一步一步輸入參數的過程。

整體架構

國際慣例,著手開發之前得先弄明白整體架構,看圖:
圖片描述

首先明白模版的概念。一個模版就是一個項目的樣板,包含項目的完整結構和信息。
模版的信息都存放在一個叫做templates.json的文件當中。
用戶可以通過命令行對templates.json進行添加、刪除、羅列的操作。
通過選擇不同的模版,SCION會自動從遠程倉庫把相應的模板拉取到本地,完成項目的搭建。

最終整個腳手架的文件結構如下:

=================|__ bin|__ scion|__ command|__ add.js|__ delete.js|__ init.js|__ list.js|__ node_modules|__ package.json|__ templates.json

入口文件

首先建立項目,在package.json里面寫入依賴并執行npm install

"dependencies": {"chalk": "^1.1.3","co": "^4.6.0","co-prompt": "^1.0.0","commander": "^2.9.0"}

在根目錄下建立\bin文件夾,在里面建立一個無后綴名的scion文件。這個bin\scion文件是整個腳手架的入口文件,所以我們首先對它進行編寫。

首先是一些初始化的代碼:

#!/usr/bin/env node --harmony
'use strict'// 定義腳手架的文件路徑
process.env.NODE_PATH = __dirname + '/../node_modules/'const program = require('commander')// 定義當前版本
program.version(require('../package').version )// 定義使用方法
program.usage('<command>')

從前文的架構圖中可以知道,腳手架支持用戶輸入4種不同的命令。現在我們來寫處理這4種命令的方法:

program.command('add').description('Add a new template').alias('a').action(() => {require('../command/add')()})program.command('list').description('List all the templates').alias('l').action(() => {require('../command/list')()})program.command('init').description('Generate a new project').alias('i').action(() => {require('../command/init')()})program.command('delete').description('Delete a template').alias('d').action(() => {require('../command/delete')()})

commander的具體使用方法在這里就不展開了,可以直接到官網去看詳細的文檔。
最后別忘了處理參數和提供幫助信息:

program.parse(process.argv)if(!program.args.length){program.help()
}

完整的代碼請看這里。
使用node運行這個文件,看到輸出如下,證明入口文件已經編寫完成了。

Usage: scion <command>Commands:add|a      Add a new templatelist|l     List all the templatesinit|i     Generate a new projectdelete|d   Delete a templateOptions:-h, --help     output usage information-V, --version  output the version number

處理用戶輸入

在項目根目錄下建立\command文件夾,專門用來存放命令處理文件。
在根目錄下建立templates.json文件并寫入如下內容,用來存放模版信息:

{"tpl":{}}

添加模板

進入\command并新建add.js文件:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')
const fs = require('fs')module.exports = () => {co(function *() {// 分步接收用戶輸入的參數let tplName = yield prompt('Template name: ')let gitUrl = yield prompt('Git https link: ')let branch = yield prompt('Branch: ')// 避免重復添加if (!config.tpl[tplName]) {config.tpl[tplName] = {}config.tpl[tplName]['url'] = gitUrl.replace(/[\u0000-\u0019]/g, '') // 過濾unicode字符config.tpl[tplName]['branch'] = branch} else {console.log(chalk.red('Template has already existed!'))process.exit()}// 把模板信息寫入templates.jsonfs.writeFile(__dirname + '/../templates.json', JSON.stringify(config), 'utf-8', (err) => {if (err) console.log(err)console.log(chalk.green('New template added!\n'))console.log(chalk.grey('The last template list is: \n'))console.log(config)console.log('\n')process.exit()})})
}

刪除模板

同樣的,在\command文件夾下建立delete.js文件:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')
const fs = require('fs')module.exports = () => {co(function *() {// 接收用戶輸入的參數let tplName = yield prompt('Template name: ')// 刪除對應的模板if (config.tpl[tplName]) {config.tpl[tplName] = undefined} else {console.log(chalk.red('Template does not exist!'))process.exit()}// 寫入template.jsonfs.writeFile(__dirname + '/../templates.json', JSON.stringify(config),     'utf-8', (err) => {if (err) console.log(err)console.log(chalk.green('Template deleted!'))console.log(chalk.grey('The last template list is: \n'))console.log(config)console.log('\n')process.exit()})})
}

羅列模板

建立list.js文件:

'use strict'
const config = require('../templates')module.exports = () => {console.log(config.tpl)process.exit()
}

構建項目

現在來到我們最重要的部分——構建項目。同樣的,在\command目錄下新建一個叫做init.js的文件:

'use strict'
const exec = require('child_process').exec
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')module.exports = () => {co(function *() {// 處理用戶輸入let tplName = yield prompt('Template name: ')let projectName = yield prompt('Project name: ')let gitUrllet branchif (!config.tpl[tplName]) {console.log(chalk.red('\n × Template does not exit!'))process.exit()}gitUrl = config.tpl[tplName].urlbranch = config.tpl[tplName].branch// git命令,遠程拉取項目并自定義項目名let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`console.log(chalk.white('\n Start generating...'))exec(cmdStr, (error, stdout, stderr) => {if (error) {console.log(error)process.exit()}console.log(chalk.green('\n √ Generation completed!'))console.log(`\n cd ${projectName} && npm install \n`)process.exit()})})
}

可以看到,這一部分代碼也非常簡單,關鍵的一句話是

let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`

它的作用正是從遠程倉庫克隆到自定義目錄,并切換到對應的分支。熟悉git命令的同學應該明白,不熟悉的同學是時候補補課啦!

全局使用

為了可以全局使用,我們需要在package.json里面設置一下:

"bin": {"scion": "bin/scion"},

本地調試的時候,在根目錄下執行

npm link

即可把scion命令綁定到全局,以后就可以直接以scion作為命令開頭而無需敲入長長的node scion之類的命令了。

現在我們的腳手架工具已經搭建好了,一起來嘗試一下吧!

使用測試

  • add | a 添加模版命令
    圖片描述

  • init | i 生成項目命令
    圖片描述

  • delete | d 刪除模版命令 和 list | l 羅列模版命令
    圖片描述

大功告成啦!現在我們的整個腳手架工具已經搭建完成了,以后只需要知道模板的git https地址和branch就可以不斷地往SCION上面添加,團隊協作的話只需要分享SCION的templates.json文件就可以了。

后記

看起來并不復雜的東西,實際從零開始搭建也是頗費了一番心思。最大的難題是在開始的時候并不懂得如何像npm init那樣可以一步一步地處理用戶輸入,只懂得一條命令行把所有的參數都帶上,這樣的用戶體驗真的很不好。研究了vue-cliyoeman也沒有找到相應的代碼,只好不斷地google,最后總算找到了一篇文章,可以用coco-prompt這兩個工具實現,再一次膜拜無所不能的TJ大神,也希望能夠有小伙伴告訴我vue-cli它們是怎么實現的。

這個腳手架只具備最基本的功能,還遠遠沒有達到市面上同類產品的高度,在日后再慢慢填補吧,不管怎么說,完成SCION的過程中真的學習到了很多東西。

感謝你的閱讀。我是Jrain,歡迎關注我的專欄,將不定期分享自己的學習體驗,開發心得,搬運墻外的干貨。下次見啦!

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

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

相關文章

微信小程序 --- 頁面跳轉

第一種&#xff1a;wx.navigateTo({}); 跳轉&#xff1a; 注意&#xff1a;這種跳轉回觸發當前頁面的 onHide 方法&#xff0c;將當前頁面隱藏&#xff0c;然后顯示跳轉頁面。所以可以返回&#xff0c;返回的時候觸發 onShow方法進行顯示&#xff1a; &#xff08;項目的底部導…

Java基礎 深拷貝淺拷貝

Java基礎 深拷貝淺拷貝 非基本數據類型 需要new新空間class Student implements Cloneable{private int id;private String name;private Vector course;public Student(){try{Thread.sleep(1000);System.out.println("Student Constructor called.");}catch (Interr…

不安裝運行時運行 .NET 程序

好久沒寫文章了&#xff0c;有些同學問我公眾號是不是廢了&#xff1f;其實并沒有。其實想寫的東西很多很多&#xff0c;主要是最近公司比較忙&#xff0c;以及一些其他個人原因沒有時間來更新文章。這幾天抽空寫了一點點東西&#xff0c;證明公眾號還活著。長久以來的認知&…

一文弄懂分布式和微服務

簡單的說&#xff0c;微服務是架構設計方式&#xff0c;分布式是系統部署方式&#xff0c;兩者概念不同。 微服務 簡單來說微服務就是很小的服務&#xff0c;小到一個服務只對應一個單一的功能&#xff0c;只做一件事。這個服務可以單獨部署運行&#xff0c;服務之間可以通過R…

常見的js算法面試題收集,es6實現

1、js 統計一個字符串出現頻率最高的字母/數字 let str asdfghjklaqwertyuiopiaia; const strChar str > {let string [...str],maxValue ,obj {},max 0;string.forEach(value > {obj[value] obj[value] undefined ? 1 : obj[value] 1if (obj[value] > max)…

PHP面向對象(OOP)----分頁類

PHP面向對象(OOP)----分頁類 同驗證碼類&#xff0c;分頁也是在個人博客&#xff0c;論壇等網站中不可缺少的方式&#xff0c;通過分頁可以在一個界面展示固定條數的數據&#xff0c;而不至于將所有數據全部羅列到一起&#xff0c;實現分頁的原理其實就是對數據庫查詢輸出加了一…

JS 事件練習

QQ拖拽及狀態欄選擇 HTML 1 <!DOCTYPE html>2 <html xmlns"http://www.w3.org/1999/xhtml">3 <head>4 <title>QQ練習</title>5 <link href"css/main.css" rel"stylesheet" />6 <script src&…

Dubbo和Spring Cloud微服務架構對比

微服務架構是互聯網很熱門的話題&#xff0c;是互聯網技術發展的必然結果。它提倡將單一應用程序劃分成一組小的服務&#xff0c;服務之間互相協調、互相配合&#xff0c;為用戶提供最終價值。目錄 微服務主要的優勢 降低復雜度 可獨立部署 容錯 擴展 核心部件 總體架構 Dubbo …

《ABP Framework 極速開發》 - 教程首發

?寫在發布之前強烈建議每一位小伙伴都應該好好看看 ABP Framework 官方文檔&#xff0c;可能有很多的小伙伴跟我剛開始的感覺一樣“一看文檔深似海”&#xff0c;看完文檔之后&#xff0c;想要上手卻找不著頭緒。本套教程寫作的目的之一是為初學者提供一條相對簡潔的快速上手路…

智能家居系統結構標準化

版權申明&#xff1a;本文為博主窗戶(Colin Cai)原創&#xff0c;歡迎轉帖。如要轉貼&#xff0c;必須注明原文網址http://www.cnblogs.com/Colin-Cai/p/8490423.html作者&#xff1a;窗戶QQ&#xff1a;6679072E-mail&#xff1a;6679072qq.com0 引 言 智能家居是指利用先進的…

洛谷 P3391 文藝平衡樹

題目描述 您需要寫一種數據結構&#xff08;可參考題目標題&#xff09;&#xff0c;來維護一個有序數列&#xff0c;其中需要提供以下操作&#xff1a;翻轉一個區間&#xff0c;例如原有序序列是5 4 3 2 1&#xff0c;翻轉區間是[2,4]的話&#xff0c;結果是5 2 3 4 1 --by洛谷…

JSONObject中optString和getString等的區別

2019獨角獸企業重金招聘Python工程師標準>>> 同事在看到我寫的解析數據代碼后&#xff0c;告訴我optString比getString好用&#xff0c;optString不會拋異常&#xff0c;而getString會拋異常&#xff0c;自己是將信將疑&#xff0c;就說&#xff0c;回去后我查查資料…

Lombok插件安裝(IDEA)、配置jar包、使用

點擊進入Lombok官網下載Lombok jar包 使用Lombok可能需要注意的地方 &#xff08;1&#xff09;當你的IDE是Idea時&#xff0c;要注意你的Idea是支持Lombok的&#xff0c;如果不支持請更換高版本嘗試&#xff08;這里采用2018 3.3&#xff09;。 &#xff08;2&#xff09;在使…

Blazor University (40)JavaScript 互操作 —— 傳遞 HTML 元素引用

原文鏈接&#xff1a;https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/passing-html-element-references/傳遞 HTML 元素引用源代碼[1]在編寫 Blazor 應用程序時&#xff0c;不鼓勵對文檔對象模型 (DOM) 進行操作&#xff0c;因為它可能會干…

RabbitMQ+PHP 教程六(RPC)

(using php-amqplib) 前提必讀 本教程假設RabbitMQ是安裝在標準端口上運行&#xff08;5672&#xff09;。如果您使用不同的主機、端口或憑據&#xff0c;則連接設置需要調整。 如果您在本教程中遇到困難&#xff0c;可以通過郵件列表與我們聯系。 開始 在第二個教程中&#xf…

TKMybatis 介紹和使用

目錄 一、什么是 TKMybatis 二、TKMybatis 使用 2.1 Springboot 項目中加入依賴 2.2 使用講解 2.2.1 實體類中使用 2.2.2 dao中使用 2.2.3 Service 層中使用 2.3 實際案例 2.3.1 dao 層使用 2.3.2 service 層使用 一、什么是 TKMybatis TKMybatis 是基于 Mybatis 框…

angularjs的ng-repeat回調

首先html代碼是這樣的&#xff1a; <label>Name des Leiters:</label><select name"leaderID" id"selectLeaderID"><option ng-repeat"manager in managers" value"leader_id{{manager.id}}&leader_name{{manager…

sed和vim練習

1、刪除/etc/grub2.conf文件中所有以空白開頭的行行首的空白字符sed s^[[:space:]]\ /etc/grub2.conf2、刪除/etc/fstab文件中所有以#開頭&#xff0c;后面至少跟一個空白字符的行的行首的#和空白字符sed -n s^#[[:space:]]\p /etc/fstab3、在/root/install.log每一行行首增加#…

WinForm(三)揭開可視化控件的面紗

WinForm所見即所得的UI設計框架&#xff0c;開發效率確實有所提升&#xff0c;同時降低了編程門檻&#xff0c;讓WinForm更普及。拖拖拽拽就能設計出一個界面&#xff0c;那么我們拖拽的這些東西是什么&#xff1f;它們是什么原理&#xff1f;。WinForm我覺得很好的一點是&…

淺談 maxMemory , totalMemory , freeMemory 和 OOM 與 native Heap

作者&#xff1a;林冠宏 / 指尖下的幽靈 掘金&#xff1a;https://juejin.im/user/587f0dfe128fe100570ce2d8 博客&#xff1a;http://www.cnblogs.com/linguanh/ GitHub &#xff1a; https://github.com/af913337456/ 騰訊云專欄&#xff1a; https://cloud.tencent.com/deve…