<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本篇文章用來為大家提供一個搭建簡易前端腳手架的思路。
先來看一眼實現的效果。
從圖上來看這個腳手架的功能非常的簡單隻有一個建立的命令,其他都是幫助和顯示版本號的。
也就是上圖這句,建立一個新專案,只需要輸入create 專案名便可使用,在建立時執行了一系列的操作,這一塊的思路很簡單,就是將git倉庫中的專案模板拷貝下來再依據使用者的不同操作對複製下來的模板的部分檔案進行修改就可以了,大致思路便介紹到這裡,接下來我們便來詳細的講講如何實現,以及會用到的依賴。
腳手架就是在啟動的時候詢問一些簡單的問題,並且通過使用者回答的結果去渲染對應的模板檔案,我們接下來的流程亦是如此
由於它是一個npm的包,因此我們需要使用npm的初始化命令,隨意新建一個資料夾開啟命令列,輸入npm init
,會出現以下情況。
名稱 | 意思 | 預設值 |
package name | 包的名稱 | 建立資料夾時的名稱 |
version | 版本號 | 1.0.0 |
description | 包的描述 | 建立檔案時的名稱 |
entry point | 入口檔案 | index.js |
test command | 測試命令 | — |
git repository | git倉庫地址 | — |
git倉庫地址 | 關鍵詞,上傳到npm官網時在頁面中展示的關鍵詞 | — |
author | 作者資訊,物件的形式,裡面儲存一些郵箱、作者名、url | — |
license | 執照 | MIT |
這就是輸入初始化命令時會詢問的東西,回答完這些後就會生成一個 package.json
的檔案,這個檔案就是記錄包的資訊。
如果想要了解更多,可檢視如下地址:
package.json詳解
用到如下依賴請安裝。
npm i path npm i chalk@4.1.0 npm i fs-extra npm i inquirer@8.2.4 npm i commander npm i axios npm i download-git-repo
在詢問問題前我們需要先建立一個入口檔案,建立完成後在package.json中新增bin項,並且將入口檔案路徑寫進去
填寫完入口檔案路徑後在入口檔案內隨便輸出一句, 但必須在入口檔案頂層宣告檔案執行方式為node。
宣告程式碼:
#! /usr/bin/env node
寫完後我們需要測試一下我們是否可以正常的存取的我們的腳手架,在本資料夾開啟命令列,輸入 npm link
,該命令會建立一個全域性存取的包的快捷方式,這個是臨時的就是本地測試的時候用的,這個在命令列輸入你的腳手架的名稱可以看到入口檔案輸出的內容。
在完成上一步後我們就要開始與使用者進行互動了,這個時候我們就需要用到一個用於自定義命令列指令的依賴 commander。
引入依賴:
const program = require('commander')
command
命令。.command()
的第一個引數為命令名稱。命令引數可以跟在名稱後面,也可以用.argument()
單獨指定。
引數可為必選的(尖括號表示)、可選的(方括號表示)或變長引數(點號表示,如果使用,只能是最後一個引數)。
例如:
// 建立一個create命令 .command('create <app-name>')
parse
解析。.parse()
的第一個引數是要解析的字串陣列,也可以省略引數而使用process.argv
,這裡我們也是用process.argv用來解析node的引數。
例如:
// 解析使用者執行命令傳入引數 program.parse(process.argv);
option
選項。option()
可以附加選項的簡介。第一個引數可以定義一個短選項名稱(-後面接單個字元)和一個長選項名稱(–後面接一個或多個單詞),使用逗號、空格或|
分隔。第二個引數為該選項的簡介。
例如:
.option('-f, --force', '如果存在的話強行覆蓋')
action
處理常式。用command建立的自定義命令
的處理常式,action攜帶的實參順序就是命令上的引數的順序。
例如:
program.command('create <app-name>') // 這個name 就代表第一個必填引數 options就代表其餘, 如果有第二個就在寫一個,最後一個永遠是剩餘引數 .action((name, options) => { console.log(name) // 列印執行結果 // require("../lib/create")(name, options) })
入口檔案
#! /usr/bin/env node const program = require('commander'); const chalk = require('chalk'); // 定義命令和引數 // create命令 program .command('create <app-name>') .description('create a new project') // -f or --force 為強制建立,如果建立的目錄存在則直接覆蓋 .option('-f, --force', 'overwrite target directory if it exist') .action((name, options) => { // 列印執行結果 console.log('專案名稱', name) }) // 解析使用者執行命令傳入引數 program.parse(process.argv);
這裡我們建立了一個叫 create
的自定義指令,這個命令有著必填的專案名、可以選擇的強制覆蓋的選項 -f
,有著處理常式action
。
我們在action中接收並列印了使用者輸入的專案名稱。
接下來我們再次執行一下自己的腳手架並帶上create命令,我的叫test
test-cli create app
出現如下就說明第一個命令建立成功了
這裡請注意 解析使用者命令引數的操作一定要在最後一行否則什麼都不會出現。
program.parse(process.argv)
到這裡為止我們成功為我們腳手架建立了第一個互動命令,想檢視更多關於 commander
的請點選這裡commander。
在建立了一個基本命令 create
後我們就要開始建立一個模板並在使用者使用該命令時複製並修改我們所建立的模板。
我們在複製模板前需要一個模板,現在的我們隨便建立一個資料夾並取名為template裡面建立一個html。
像這樣建立好後,我們就有了一個模板,但我們依然需要讓模板有一個可被下載、查詢的地方,這裡我選擇的是使用 git
組織倉庫,因為這樣可以直接通過git
提供的介面進行檔案下載,包括選擇不同的模板等。
我們先去 git
的官網中新建一個存放模板的組織倉庫。
點選圖中的位置進入組織,並點選下圖的建立
會進入到付費的位置,沒有大需求就選免費
填寫資訊完基本就算建立成功了
接下來在組織中建立一個儲存庫
這裡我們暫且選擇可見的倉庫,千萬不要選擇私人倉庫,否則git
介面會找不該倉庫
建立好後的倉庫,就直接將模板程式碼提交至也本次建立的倉庫中就可以了,我們在vscode
中進行演示。
先點選推播
如果沒有推播的倉庫則會提示是否新增推播倉庫,我們點選推播遠端倉庫,並從中找到自己的倉庫
擇完成後輸入倉庫名稱,然後會報錯,報錯原因就是因為暫無推播的內容,這個使用,正常的在 vscode
中提交程式碼就行了,然後檢視自己的倉庫,會出現上傳的內容
跟著下列圖操作
點選釋出發行版後就可以了。
我們上傳模板後可以通過 git
提供的介面來完成下載模板的功能,首先我們先去詢問使用者要下載的模板名稱然後在用依賴包來進行下載:
https://api.github.com/orgs/geeksTest/repos 獲取該組織下的所有模板
上傳模板後,我們就可以繼續完成create
命令的後續操作了。
program .command('create <app-name>') .description(chalk.cyan('create a new project')) // -f or --force 為強制建立,如果建立的目錄存在則直接覆蓋 .option('-f, --force', 'overwrite target directory if it exist') .action((name, options) => { // 列印執行結果 require("../lib/create")(name, options) })
建立 create 檔案用來回應使用者的 create 命令。
這裡用到的依賴
// lib/create.js const path = require('path') // fs-extra 是對 fs 模組的擴充套件,支援 promise 語法 const fs = require('fs-extra') // 用於互動式詢問使用者問題 const inquirer = require('inquirer') // 匯出Generator類 const Generator = require('./Generator') //1. 丟擲一個方法用來接收使用者要建立的資料夾(專案)名 和 其他引數 module.exports = async function (name, options) { // 當前命令列選擇的目錄 const cwd = process.cwd(); // 需要建立的目錄地址 const targetAir = path.join(cwd, name) //2 判斷是否存在相同的資料夾(專案)名 // 目錄是否已經存在? if (fs.existsSync(targetAir)) { // 是否為強制建立? if (options.force) { await fs.remove(targetAir) } else { // 詢問使用者是否確定要覆蓋 let { action } = await inquirer.prompt([ { name: 'action', type: 'list', message: 'Target directory already exists Pick an action:', choices: [ { name: 'Overwrite', value: 'overwrite' },{ name: 'Cancel', value: false } ] } ]) // 如果使用者拒絕覆蓋則停止剩餘操作 if (!action) { return; } else if (action === 'overwrite') { // 移除已存在的目錄 console.log(`rnRemoving...`) await fs.remove(targetAir) } } } //3 新建generator類 const generator = new Generator(name, targetAir); generator.create(); }
// lib/Generator.js const { getRepoList, getTagList } = require('./http') const ora = require('ora') const inquirer = require('inquirer') const util = require('util') const downloadGitRepo = require('download-git-repo') // 不支援 Promise const chalk = require('chalk') const path = require('path'); const fs = require("fs-extra"); // 新增載入動畫 async function wrapLoading(fn, message, ...args) { // 使用 ora 初始化,傳入提示資訊 message const spinner = ora(message); // 開始載入動畫 spinner.start(); try { // 執行傳入方法 fn const result = await fn(...args); // 狀態為修改為成功 spinner.succeed(); return result; } catch (error) { // 狀態為修改為失敗 spinner.fail('Request failed, refetch ...'); } } class Generator { constructor (name, targetDir){ // 目錄名稱 this.name = name; // 建立位置 this.targetDir = targetDir; // 對 download-git-repo 進行 promise 化改造 this.downloadGitRepo = util.promisify(downloadGitRepo); } // 獲取使用者選擇的模板 // 1)從遠端拉取模板資料 // 2)使用者選擇自己新下載的模板名稱 // 3)return 使用者選擇的名稱 async getRepo() { // 1)從遠端拉取模板資料 const repoList = await wrapLoading(getRepoList, 'waiting fetch template'); if (!repoList) return; // 過濾我們需要的模板名稱 const repos = repoList.map(item => item.name); // 2)使用者選擇自己新下載的模板名稱 const { repo } = await inquirer.prompt({ name: 'repo', type: 'list', choices: repos, message: 'Please choose a template to create project' }) // 3)return 使用者選擇的名稱 return repo; } // 獲取使用者選擇的版本 // 1)基於 repo 結果,遠端拉取對應的 tag 列表 // 2)自動選擇最新版的 tag async getTag(repo) { // 1)基於 repo 結果,遠端拉取對應的 tag 列表 const tags = await wrapLoading(getTagList, 'waiting fetch tag', repo); if (!tags) return; // 過濾我們需要的 tag 名稱 const tagsList = tags.map(item => item.name); // 2)return 使用者選擇的 tag return tagsList[0] } // 下載遠端模板 // 1)拼接下載地址 // 2)呼叫下載方法 async download(repo, tag){ // 1)拼接下載地址 const requestUrl = `geeksTest/${repo}${tag ? '#'+tag : ''}`; // 2)呼叫下載方法 await wrapLoading( this.downloadGitRepo, // 遠端下載方法 'waiting download template', // 載入提示資訊 requestUrl, // 引數1: 下載地址 path.resolve(process.cwd(), this.targetDir) // 引數2: 建立位置 ) } // 核心建立邏輯 // 1)獲取模板名稱 // 2)獲取 tag 名稱 // 3)下載模板到模板目錄 // 4) 對uniapp模板中部分檔案進行讀寫 // 5) 模板使用提示 async create(){ // 1)獲取模板名稱 const repo = await this.getRepo() // 2) 獲取 tag 名稱 const tag = await this.getTag(repo) // 3)下載模板到模板目錄 await this.download(repo, tag) // 5)模板使用提示 console.log(`rnSuccessfully created project ${chalk.cyan(this.name)}`) console.log(`rn cd ${chalk.cyan(this.name)}`) console.log(`rn 啟動前請務必閱讀 ${chalk.cyan("README.md")} 檔案`) } } module.exports = Generator;
新建一個http.js的檔案用來存放要請求的介面,我們用axios去請求.
依賴安裝
npm i commander
// lib/http.js // 通過 axios 處理請求 const axios = require('axios') axios.interceptors.response.use(res => { return res.data; }) /** * 獲取模板列表 * @returns Promise */ async function getRepoList() { return axios.get('https://api.github.com/orgs/geeksTest/repos') } /** * 獲取版本資訊 * @param {string} repo 模板名稱 * @returns Promise */ async function getTagList(repo) { return axios.get(`https://api.github.com/repos/geeksTest/${repo}/tags`) } module.exports = { getRepoList, getTagList }
最後匯出了兩個方法, 模板列表、模板tag列表。
這個時候的api介面是可以直接在瀏覽器中存取到的,如果不想被人隨意存取讀取資料則可以在git中增加雙因素驗證,然後每次存取api時都會要求帶上git的存取token否則會存取不到,檢視雙因素詳情
完成這一步後我們再去進行test-cli create app
命令,會看到下圖。
會詢問要建立的模板專案,我這裡的遠端組織模板叫做test,大家選擇自己的模板回車,稍等一下就會建立成功,並看到在你使用命令的路徑上多出一個專案名的資料夾,就成功了。
如果有對模板在下載後進行操作的需求可以使用fs
依賴進行操作,到這裡為止我們已經完成了一個簡易的腳手架搭建,感謝大家耐心觀看。
到此這篇關於手把手教你從0搭建前端腳手架詳解的文章就介紹到這了,更多相關搭建前端腳手架內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45