前端腳手架開發指南:提高開發效率的核心操作

前端腳手架通過自動化的方式可以提高開發效率并減少重復工作,而最強大的腳手架并不是現成的那些工具而是屬于你自己團隊量身定制的腳手架!本篇文章將帶你了解腳手架開發的基本技巧,幫助你掌握如何構建適合自己需求的工具,并帶著你一步步走向前端開發的全新高度。

目錄

初始化項目

模板命令操作

倉庫文檔操作

模板下載操作

模板渲染操作?

初始化項目

關于腳手架的基本概念和一些常用工具的講解,上篇文章 地址?已經講解的比較清楚了,本篇文章不再贅述,接下來我們還是初始化一個新的項目進行操作,終端執行 pnpm init 初始化項目:

然后接下來我們執行如下命令安裝腳手架需要的相關插件,不清楚的可以參考我上篇文章講解:

pnpm install commander @inquirer/prompts chalk ini ora -s
pnpm install @types/node typescript nodemon -D

安裝完成一些基礎插件之后,接下來我們需要設置ts模塊然后將ts編譯成js然后在運行項目,終端執行如下命令生成ts配置文件,執行報錯全局cmd安裝一下?npm i -g typescript 即可:

tsc --init

然后我們根據自身情況配置如下內容即可,

{"compilerOptions": {"target": "es6", // 編譯成es6代碼"module": "NodeNext", // 模塊選擇es6"outDir": "bin","moduleResolution": "nodenext", // 模塊解析策略"esModuleInterop": true, // 允許導入非ES模塊"resolveJsonModule": true, // 允許導入json模塊"rootDir": "src", // 根目錄"baseUrl": "./src" // 基礎目錄},"include": ["src"],"exclude": ["node_modules"],
}

然后我們設置編譯打包內容如下所示,執行打包命令組織和執行我們定義的關鍵字就能執行了

但是每次寫完代碼都要重新打包然后再執行一遍,很費時間所以這里我們通過nodemon來設置自動編譯打包執行,nodemon提供了許多實用的命令行選項幫助定制其行為,以下是一些常用的選項:

參數說明
-w 或 --watch指定監視的文件或文件夾
-e 或 --ext指定要監視的文件擴展名
-i 或 --ignore指定要忽略的文件或文件夾
-d 或 --delay設置文件變化后的延遲重啟時間(單位為秒)
--exec執行指定的命令而不是直接啟動 node 命令
-r 或 --require加載一個模塊,通常用于加載環境配置或預處理腳本

根據上面的規則配置了如下script命令,時刻監聽src目錄下所以ts文件,一旦有變化就執行tsc:

"scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "nodemon --watch ./src --ext ts --exec tsc","build": "tsc"
},

效果如下,當我們修改ts文件之后,就會立即熱更新編譯成js文件,效果不錯:

模板命令操作

????????隨著腳手架框架的不斷累積和完善,模板命令也會越來越復雜以適應不同場景下的模板創建,所以我們需要對我們的模板創建命令進行一個抽離和封裝以方便后期簡化操作,這里我們直接在src目錄下新建一個commands文件夾,里面存放封裝commands命令的內容以及命令對應要執行的函數內容,當然這里根據個人喜好配置,博主設置的內容如下所示:

基礎options設置:在基礎的封裝options函數中,這里我將讀取的json文件里面的內容傳遞了進去,對于json文件的讀取,前端tsconfig中已經設置了 "resolveJsonModule": true, // 允許導入JSON模塊 這個配置,我之前還用的好好的,能直接引入pack文件然后讀取使用,后面可能由于ts版本或者其他因素版本的影響導致讀取不了數據了,這里我就抽離了一個工具函數,兩個方法都能讀取json文件的內容:

import fs from 'fs';
import { createRequire } from "module";
import { fileURLToPath } from "url";
import { dirname, join } from "path";const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const require = createRequire(_filename);
const pkg = require(join(_dirname, '../../package.json'));
const loadJSON = (path) => JSON.parse(fs.readFileSync(new URL(path, import.meta.url)).toString());
const Pack = loadJSON('../../package.json');export {Pack,pkg
}

拿到json文件里面的內容數據之后,我們就可以傳遞到基礎配置當中,直接設置腳手架的名稱、描述、作者及版本等相關項目的配置:

import { program } from "commander";const baseOptions = (Pack) => {program.name(Pack.name).version(Pack.version, '-v, --version', '輸出當前版本號').usage('<command>(必填項) [options](可選項)').description(`${Pack.description} (作者:${Pack.author})`).addHelpText("after", `\nRun yyue-cli <command> --help for detailed usage of given command.\n`)
};export default baseOptions;

command命令配置:接下來開始寫command命令配置,這個是大頭,用戶在第一次接觸腳手架使用的使用,都是通過command設置的命令配置,才能了解到腳手架如何使用。因為command命令配置后期可能隨著腳手架的復雜度的升高會導致產生各自各樣的命令,所以這里我們需要對其命令配置抽離出一個對象,然后通過循環遍歷的方式來生成對應的command配置,也很方便,具體實現的代碼如下所示:

import { program } from "commander";// 配置指令命令
const mapActions = {create: {alias: 'c',description: 'create a new project',options: [{ flags: "-f, --force", description: "overwrite target directory if it exists" }],examples: ['yyue-cli create <project-name>'],action: async (name, option) => (await import("../hook/create.js")).default(name, option),},config: {alias: 'conf',description: 'config project variable',options: [{ flags: "-g, --get <k>", description: "get value from path" },{ flags: "-s, --set <k> <v>", description: "set value to path" },{ flags: "-d, --delete <k>", description: "delete value from path" }],examples: ['yyue-cli conf set <k><v>', 'yyue-cli conf get <k>'],action: async (value, option) => (await import("../hook/config.js")).default(value, option),},'*': {alias: '',description: 'command not found',options: [],examples: ['yyue-cli <cmd>'],action: () => console.log('command not found'),}
};const CustomCommand = () => {// 循環創建命令Reflect.ownKeys(mapActions).forEach((key: string) => {const { alias, description, options, action } = mapActions[key];const cmd = program.command(key) // 配置命令名稱.alias(alias) // 配置命令別名.description(description); // 配置命令描述if (key === 'create') {cmd.argument('<project-name>', 'name of the project');} else if(key === 'config') {cmd.argument('<k>', 'key of the variable').argument('<v>', 'value of the variable');} else if (key === '*') {cmd.argument('<cmd>', 'command of the project');}// 配置選項options?.forEach((option) => {cmd.option(option.flags, option.description);});cmd.action(action); // 配置命令執行函數});// 監聽用戶的help事件program.on('--help', () => {console.log("\nExamples:")Reflect.ownKeys(mapActions).forEach((key: string) => {const { examples } = mapActions[key];examples?.forEach((example) => {console.log(`  ${example}`);})});})
};export default CustomCommand;

每個命令的執行都有其對應的action函數來進行執行,這里的action也是大頭,所以說這里我們仍然將其抽離出封裝成一個hook函數,方便后期的維護,增大耦合度才能讓項目的維護更加方便,運行的效果如下所示,感覺還是不錯的哈:

倉庫文檔操作

當我們通過模板的一些創建命令交互式的選擇好我們想要創建的模板之后,我們就需要從倉庫中拉取事先配置好的模板到本地目錄中,因為github有時候由于網絡原因訪問過慢,所以我們可以將模板都配置到國內的 碼云?遠程倉庫當中,該倉庫也配置了 api文檔?方便對倉庫當中的一些項目進行操作,這也方便了我們遠程拉取模板的操作,如下所示我們點擊右上角的申請授權之后,每個接口都會自動添加對于的access_token,后面我們根據自身情況來選擇傳遞對于的參數:

然后我們在untils工具文件夾下封裝一個axios工具函數,這里注意一下當我們對對git服務進行請求的時候,可能會出現如下的問題,導致這個問題的原因就是因為git服務的ssl協議沒有通過驗證,我們可以重新生成正規的SSL證書,當然有可能我們的gitlab就是用的ip地址,這時候也可以通過關閉驗證直接解決,具體直接通過如下代碼所示:

git SSL certificate problem: unable to get local issuer certificate

// axios基礎封裝
import axios from 'axios'
import * as https from 'https'const http = axios.create({baseURL: 'https://gitee.com/api/v5',timeout: 5000,httpsAgent: new https.Agent({rejectUnauthorized: false // 拒絕校驗未被授權的證書(SSL證書)})
})// 請求攔截器
http.interceptors.request.use(config => {// 請求攔截器return config
}, error => {return Promise.reject(error)
})// 響應攔截器
http.interceptors.response.use(res => {return res.data},  error => {return Promise.reject(error)}
)export default http

接下來我們就可以創建一個api文件夾,然后可以調用git服務接口來獲取倉庫的一些相關信息:

// 統一管理倉庫的相關接口
import http from '@utils/http.js'const access_token = '你的授權token值'
// 統一管理接口
enum API {REPOS_URL = '/user/repos', // 列出授權用戶的所有倉庫
}
// 列出授權用戶的所有倉庫接口
export const reqGetRepositoriesProjects = (data) =>http.get<any, any>(API.REPOS_URL, {headers: {'Authorization': `Bearer ${access_token}`,},data: { access_token, ...data }})

接口寫完之后我們就可以在command命令行中的action事件函數當中調用改接口,可以看到我們倉庫當中的所以倉庫信息都被打印出來了:

當然gitee申請授權的token是有過期時間的,如果想設置不過期的token需要打開個人中心,然后找到私人令牌,然后給選擇令牌的過期是時間是永不過期即可

當然后面如果配置好模板之后,想把模板設置私有倉庫下載的話,可以設置一下ssh密鑰,執行如下命令在git bash命令行上,生成ssh key:

ssh-keygen -t ed25519 -C "Gitee SSH Key"

輸入命令一直回車即可:?

查看生成的 SSH 公鑰和私鑰,輸出:私鑰文件 id_ed25519;公鑰文件 id_ed25519.pub

ls ~/.ssh/

讀取公鑰文件 ~/.ssh/id_ed25519.pub,輸出密鑰之后,復制到gitee的ssh公鑰配置上:

cat ~/.ssh/id_ed25519.pub

模板下載操作

當我們配置好模板命令、交互選擇以及倉庫文檔等操作之后,我們就可以下載我們的模板了,獲取到項目模板名稱和對應的版本之后,我們就可以直接下載了,上篇文章:?地址?我們下載模板的庫是 git-clone,這里不再過多贅述,這里我們通過gitee的命令獲取gitee上的私有倉庫,并且倉庫的特征關鍵字是template,如下所示:

接下來我們通過上面的一個簡單的示例,把私有倉庫當中的的template-test內容down到本地當中,如下所示:

模板渲染操作?

????????如果用戶想定制下載模板中的內容,這里我們就需要對模板渲染進行操作,拿package.json舉例,用戶可以根據終端交互命令選擇的項目名稱和一些其他操作,根據相對于的詢問生成最終下載的模板的package.json內容,核心原理就是將下載的模板文件依次遍歷根據用戶填寫的信息渲染模板,然后將渲染的模板拷貝到執行目錄下,這里我們需要將模板渲染用到的插件進行安裝,終端執行如下命令操作:

// metalsmith: 遍歷所有文件目錄配置json渲染
pnpm i metalsmith -D

安裝完成之后,這里我把渲染模板文件的功能函數抽離出來,具體的代碼如下所示:

import Metalsmith from 'metalsmith';
import { promisify } from 'util';
import { ejs } from 'consolidate';
import path from 'path';
import fs from 'fs-extra';let { render } = ejs;
render = promisify(render);export const handleTemplateRenders = async (name, metadataData = {}) => {const projectRoot = path.join(process.cwd(), name); // 獲取項目根路徑if (!fs.pathExistsSync(projectRoot)) { // 確保項目目錄存在console.error(`項目目錄不存在: ${projectRoot}`);return;}// 配置元數據const metadata = {name: name,author: 'Your Name',date: new Date().toLocaleDateString(),...metadataData};// 創建一個臨時變量來存儲需要處理的文件let filesToProcess = [];// 創建 Metalsmith 實例await new Promise<void>((resolve, reject) => {Metalsmith(projectRoot).source('.') // 從項目根目錄讀取文件.destination('.') // 輸出到項目根目錄(覆蓋原始文件).clean(false) // 不清除目標目錄,避免刪除其他文件.metadata(metadata) // 設置元數據// 第一個插件:收集需要處理的文件.use((files, metalsmith, done) => {// 過濾需要處理的文件類型filesToProcess = Reflect.ownKeys(files).filter((file: any) => {const fileInfo = files[file];const content = fileInfo.contents.toString();const hasEjsTags = content.includes('<%') && content.includes('%>'); // 檢查文件是否包含 EJS 標簽return hasEjsTags;});done();})// 第二個插件:處理 EJS 模板.use(async (files, metalsmith, done) => {const meta = metalsmith.metadata();try {for (const file of filesToProcess) {const fileInfo = files[file];const originalContent = fileInfo.contents.toString();const renderedContent = await render(originalContent, meta); // 渲染 EJS 模板// 更新文件內容files[file].contents = Buffer.from(renderedContent);}done();} catch (err) {console.error('渲染模板時出錯:', err);done(err);}}).build(err => {if (err) {console.error('Metalsmith 構建失敗:', err);reject(err);} else {resolve();}});});// 驗證渲染結果validateRenderResults(name);
};// 驗證渲染結果的輔助函數
function validateRenderResults(name) {const projectRoot = path.join(process.cwd(), name);const packageJsonPath = path.join(projectRoot, 'package.json');if (fs.pathExistsSync(packageJsonPath)) {try {const content = fs.readFileSync(packageJsonPath, 'utf8');const pkg = JSON.parse(content);console.log('\n=== 渲染結果驗證 ===');console.log('package.json 中的 name:', pkg.name);console.log('package.json 中的 author:', pkg.author);if (pkg.name === '<%= name %>' || pkg.author === '<%= author %>') {console.error('? 渲染失敗: EJS 模板語法未被正確替換');} else {console.log('? 渲染成功: EJS 模板語法已被正確替換');}} catch (err) {console.error('驗證渲染結果時出錯:', err);}}
}

上面代碼封裝的功能函數中,形參name就是項目文件夾,metadataData就是你要渲染的模板的實際數據,這里我在倉庫當中設置一個模板語法的package.json文件,如下所示可以看到我們的項目名稱以及對應的作者名稱都是需要通過用戶輸入來動態渲染的:

這里我們在下面完模板之后,調用一下替換模板語法的函數,這里就會當模板下載之后就會立即遍歷整個文件夾,找到對應的有模板語法的文件,然后進行替換:

實現的效果如下所示,可以看到效果非常好,后期也可以根據自身的項目需求,讓這個模板渲染變得更加復雜以適應不同的項目情況,這些都是可以的:

當然我們還可以通過EJS來實現模板渲染, EJS(Embedded JavaScript)是一個模板引擎,允許在HTML中插入動態內容,可以通過EJS渲染數據并生成最終的HTML頁面,終端執行如下命令安裝插件:?

// ejs: 動態渲染數據并生成最終的HTML頁面
pnpm i ejs -D

EJS允許在HTML模板中嵌入JavaScript代碼,用來動態生成內容,基本案例如下所示:

const ejs = require('ejs');const data = { title: 'Hello World', body: 'This is a test.' };ejs.renderFile('template.ejs', data, (err, str) => {if (err) {console.error(err);} else {console.log(str);  // 渲染后的 HTML 內容}
});

Consolidate.是一個模板引擎的統一接口,它提供了一種統一的方式來使用多種模板引擎(如 EJS、Pug等),通過Consolidate可以輕松地切換不同的模板引擎,終端執行如下命令安裝插件:

// consolidate: 返回渲染函數,統一所有模板引擎
pnpm i consolidate -D

Consolidate.會根據傳入的模板引擎來調用相應的渲染方法,基本用法如下所示:

const consolidate = require('consolidate');
const ejs = consolidate.ejs;ejs.renderFile('template.ejs', { title: 'Hello' }, (err, html) => {if (err) throw err;console.log(html);  // 渲染后的 HTML
});

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

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

相關文章

SpringBoot常用注解詳解

文章目錄 1. 前言2. 核心注解2.1 SpringBootApplication2.2 Configuration2.3 EnableAutoConfiguration2.4 ComponentScan2.5 Bean2.6 Autowired2.7 Qualifier2.8 Primary2.9 Value2.10 PropertySource2.11 ConfigurationProperties2.12 Profile 3. Web開發相關注解3.1 Control…

項目管理進階:全文解讀企業IT系統全生命周期管理與運營平臺建設方案【附全文閱讀】

本文介紹了《企業IT系統全生命周期管理與運營平臺建設方案》的項目內容&#xff0c;包括項目背景、藍圖架構、核心業務流程、系統總體架構、解決方案等。 重點內容&#xff1a; 1. 項目背景&#xff1a;介紹企業IT系統全生命周期管理的重要性。 2. 藍圖架構&#xff1a;描述項目…

記錄一次vue項目頁面內嵌iframe頁面實現跨域上傳和下載附件的功能

功能背景&#xff1a;項目部署在外網&#xff0c;然后其中有一個功能需要上傳下載附件&#xff0c;附件是上傳到華為云對象存儲服務OBS中&#xff08;私有云&#xff09;&#xff0c;所以采用iframe嵌套頁面的方式解決跨域問題。 實現思路&#xff1a; 1、父窗口封裝一個組件專…

rust語言,與c,go語言一樣也是編譯成二進制文件嗎?

是的&#xff0c;Rust 和 C、Go 一樣&#xff0c;默認情況下會將代碼編譯成二進制可執行文件&#xff08;如 ELF、PE、Mach-O 等格式&#xff09;&#xff0c;但它們的編譯過程和運行時特性有所不同&#xff1a; 1. Rust&#xff08;類似 C&#xff0c;直接編譯為機器碼&#x…

后端框架(3):Spring(2)

AOP 概述&#xff1a;AspectOrientedProgramming 面向切面編程&#xff1a;是對面向對象編程的補充延續&#xff0c;面向切面編程思想是將程序中非業務代碼(提交事務&#xff0c;打印日志&#xff0c;權限驗證&#xff0c;統一異常處理) 然后在調用業務代碼時&#xff0c;通過…

Vue3中setup運行時機介紹

在 Vue3 中&#xff0c;直接寫在 <script setup>...</script> 中的代碼運行時機可以分為以下幾個關鍵階段&#xff1a; 一、執行順序層級 #mermaid-svg-bF3p98MiNdLfcoSG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#33…

Ubuntu快速安裝Python3.11及多版本管理

之前文章和大家分享過&#xff0c;將會出一篇專欄&#xff08;從電腦裝ubuntu系統&#xff0c;到安裝ubuntu的常用基礎軟件&#xff1a;jdk、python、node、nginx、maven、supervisor、minio、docker、git、mysql、redis、postgresql、mq、ollama等&#xff09;&#xff0c;目前…

裸金屬服務器和云服務器之間的差別

裸金屬服務器能夠直接在硬件上運行&#xff0c;不需要額外的虛化層&#xff0c;讓每個應用程序或者是服務都能夠在實際的硬件上運行&#xff0c;不需要和其他虛擬服務器來共享資源&#xff1b;而云服務器作為一種虛擬服務器&#xff0c;是通過虛擬化技術為企業提供一個獨立的計…

C++ 中的幾種鎖機制整理

1. 互斥鎖&#xff08;std::mutex&#xff09; ? 簡介 最常用的線程同步工具。保證同一時間只能有一個線程訪問臨界區。 ? 使用方式 #include <mutex>std::mutex mtx;void safeFunction() {std::lock_guard<std::mutex> lock(mtx);// 臨界區代碼 }? 優點 簡…

Graph Representation Learning【圖最短路徑優化/Node2vec/Deepwalk】

文章目錄 Q1&#xff1a;網絡性質&#xff1a;1.數據讀取與鄰接表構建&#xff1a;2.基本特征和連通性&#xff1a; 算法思路&#xff1a;1. 廣度優先搜索&#xff08;BFS&#xff09;標記前驅:2. 回溯生成所有最短路徑: 實驗結果&#xff1a;復雜度分析&#xff1a; Q2&#x…

MATLAB中的概率分布生成:從理論到實踐

MATLAB中的概率分布生成&#xff1a;從理論到實踐 引言 MATLAB作為一款強大的科學計算軟件&#xff0c;在統計分析、數據模擬和概率建模方面提供了豐富的功能。本文將介紹如何使用MATLAB生成各種常見的概率分布&#xff0c;包括均勻分布、正態分布、泊松分布等&#xff0c;并…

經典算法 (A/B) mod C

(A/B) mod C 問題描述 求(A/B)%C&#xff0c;但由于A和B實在太大了&#xff0c;我們只給出A % C&#xff0c;B % C。 (我們保證給定的A必能被B整除&#xff0c;且gcd(B,C) 1)。 輸入描述 輸入一行三個整數&#xff0c;分別是A % C&#xff0c;B % C&#xff0c;C。 輸出…

大數據技術的主要方向及其應用詳解

文章目錄 一、大數據技術概述二、大數據存儲與管理方向1. 分布式文件系統2. NoSQL數據庫3. 數據倉庫技術 三、大數據處理與分析方向1. 批處理技術2. 流處理技術3. 交互式分析4. 圖計算技術 四、大數據機器學習方向1. 分布式機器學習2. 深度學習平臺3. 自動機器學習(AutoML) 五、…

Deeper and Wider Siamese Networks for Real-Time Visual Tracking

現象&#xff1a; the backbone networks used in Siamese trackers are relatively shallow, such as AlexNet , which does not fully take advantage of the capability of modern deep neural networks. direct replacement of backbones with existing powerful archite…

ubuntu22.04卸載vscode

方法 1&#xff1a;通過 Snap 卸載 VSCode 如果你是通過 Snap 安裝的 VSCode&#xff08;Ubuntu 22.04 默認推薦方式&#xff09;&#xff0c;按照以下步驟卸載&#xff1a; 檢查是否通過 Snap 安裝&#xff1a; bash snap list | grep code如果輸出顯示 code&#xff0c;說明…

OpenCV 背景建模詳解:從原理到實戰

在計算機視覺領域&#xff0c;背景建模是一項基礎且重要的技術&#xff0c;它能夠從視頻流中分離出前景目標&#xff0c;廣泛應用于運動目標檢測、視頻監控、人機交互等場景。OpenCV 作為計算機視覺領域最受歡迎的開源庫之一&#xff0c;提供了多種高效的背景建模算法。本文將深…

Android native崩潰問題分析

最近在做NDK項目的時候&#xff0c;出現了啟動應用就崩潰了&#xff0c;崩潰日志如下&#xff1a; 10:41:04.743 A Build fingerprint: samsung/g0qzcx/g0q:13/TP1A.220624.014/S9060ZCU4CWH1:user/release-keys 10:41:04.743 A Revision: 12 10:41:04.743 A ABI: arm64…

【Shell的基本操作】

文章目錄 一、實驗目的二、實驗環境三、實驗內容3.1 Shell變量與腳本基礎3.2 定制終端提示符&#xff08;PS1變量&#xff09;3.3 文件查找與類型確認&#xff08;find命令&#xff09;3.4 管道命令實戰&#xff08;用戶登錄統計&#xff09;3.5 交互式備份壓縮腳本 四、總結4.…

快速選擇算法:優化大數據中的 Top-K 問題

在處理海量數據時&#xff0c;經常會遇到這樣的需求&#xff1a;找出數據中最大的前 K 個數&#xff0c;而不必對整個數據集進行排序。這種場景下&#xff0c;快速選擇算法&#xff08;Quickselect&#xff09;就成了一個非常高效的解決方案。本文將通過一個 C 實現的快速選擇算…

AQS 基本思想與源碼分析

充分了解 AbstractQueuedSynchronizer 對于深入理解并發編程是有益處的&#xff0c;它是用來構建鎖或者其他同步組件的基礎框架&#xff0c;我們常用的同步工具類如 CountDownLatch、Semaphore、ThreadPoolExecutor、ReentrantLock 和 ReentrantReadWriteLock 內部都用到了它。…