詳解使用yeoman打造自己的項(xiàng)目腳手架
引言
當(dāng)新建項(xiàng)目的時(shí)候,我們通常需要設(shè)計(jì)目錄結(jié)構(gòu)、配各種配置、處理打包編譯,而且每次新建都要重來(lái)一遍,或把原來(lái)的項(xiàng)目 copy 一份再改改。那能不能自己寫(xiě)個(gè)模板,然后還可以支持個(gè)性化自動(dòng)創(chuàng)建呢?今天我就來(lái)和大家一起分享如何定制一套自己的項(xiàng)目腳手架,提升開(kāi)發(fā)效率。
這里需要引入腳手架的概念,什么是腳手架呢?腳手架如同一個(gè)項(xiàng)目的模板,可以幫我們快速開(kāi)始項(xiàng)目,就像 vue-cli,提供一個(gè)終端的交互界面讓用戶(hù)自定義項(xiàng)目?jī)?nèi)容。
yeoman 介紹
Yeoman 是一個(gè)通用的腳手架系統(tǒng),允許創(chuàng)建任何類(lèi)型的應(yīng)用程序(Web,Java,Python,C#等)。用 yeoman 寫(xiě)腳手架非常簡(jiǎn)單, yeoman 提供了 yeoman-generator 讓我們快速生成一個(gè)腳手架模板,我們的主要工作就是把模板文件寫(xiě)好?,F(xiàn)在我們來(lái)用 yeoman 寫(xiě)一個(gè)生成 javascript 插件的腳手架吧。
腳手架功能:
- 自動(dòng)構(gòu)建編譯和打包
- 支持 es6 語(yǔ)法
- 支持單元測(cè)試
- 支持 jsdoc 生成文檔
- 支持 eslint 語(yǔ)法檢查
- 自動(dòng)生成 changelog
準(zhǔn)備工作
首先需要全局安裝 yo 和 generator-generator
npm install yo -g npm install generator-generator -g
生成腳手架模板
yo generator

在這個(gè)終端界面里輸入項(xiàng)目名、描述等項(xiàng)目信息。注意項(xiàng)目名稱(chēng)要寫(xiě)成generator-xxx的格式,這樣用戶(hù)就可以通過(guò)yo xxx安裝你的腳手架了。
生成的腳手架模板目錄結(jié)構(gòu)如下:
.
├── generators/
│ └── app/
│ ├── index.js
│ └── templates/
│ └── dummyfile.txt
├── .editorconfig
├── .eslintignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.md
├── package.json
└── __tests__/
└── app.js
接下來(lái)我們就在generators/app/index.js里寫(xiě)腳手架的邏輯。
編寫(xiě)自己的腳手架
腳手架所做的事情:
- 接收用戶(hù)輸入
- 根據(jù)用戶(hù)輸入生成模板文件
- 將模板文件拷貝到目標(biāo)目錄(通常是用戶(hù)運(yùn)行腳手架的目錄)
- 安裝依賴(lài)
yeoman 提供了一個(gè)基本生成器,你可以擴(kuò)展它以實(shí)現(xiàn)自己的行為。這個(gè)基礎(chǔ)生成器將幫你減輕大部分工作量。在生成器的 index.js 文件中,以下是擴(kuò)展基本生成器的方法:
var Generator = require("yeoman-generator");
module.exports = class extends Generator {};
yeoman 生命周期函數(shù)執(zhí)行順序如下:
- initializing - 初始化函數(shù)
- prompting - 接收用戶(hù)輸入階段
- configuring - 保存配置信息和文件
- default - 執(zhí)行自定義函數(shù)
- writing - 生成項(xiàng)目目錄結(jié)構(gòu)階段
- conflicts - 統(tǒng)一處理沖突,如要生成的文件已經(jīng)存在是否覆蓋等處理
- install - 安裝依賴(lài)階段
- end - 生成器結(jié)束階段
我們常用的就是 initializing、prompting、default、writing、install 這四種生命周期函數(shù)。看下例子:
"use strict";
const Generator = require("yeoman-generator");
const chalk = require("chalk"); // 讓console.log帶顏色輸出
const yosay = require("yosay");
const mkdirp = require("mkdirp"); // 創(chuàng)建目錄
module.exports = class extends Generator {
initializing() {
this.props = {};
}
// 接受用戶(hù)輸入
prompting() {
// Have Yeoman greet the user.
this.log(
yosay(
`Welcome to the grand ${chalk.red(
"generator-javascript-plugin"
)} generator!`
)
);
const prompts = [
{
type: "confirm",
name: "someAnswer",
message: "Would you like to enable this option?",
default: true
}
];
return this.prompt(prompts).then(props => {
// To access props later use this.props.someAnswer;
this.props = props;
});
}
// 創(chuàng)建項(xiàng)目目錄
default() {
if (path.basename(this.destinationPath()) !== this.props.name) {
this.log(`\nYour generator must be inside a folder named
${this.props.name}\n
I will automatically create this folder.\n`);
mkdirp(this.props.name);
this.destinationRoot(this.destinationPath(this.props.name));
}
}
// 寫(xiě)文件
writing() {
// 將templates目錄的代碼拷貝到目標(biāo)目錄
// templates目錄默認(rèn)路徑是generators/app/templates
this.fs.copy(
this.templatePath("dummyfile.txt"),
this.destinationPath("dummyfile.txt")
);
this._writingPackageJSON();
}
// 以下劃線(xiàn)_開(kāi)頭的是私有方法
_writingPackageJSON() {
// this.fs.copyTpl(from, to, context)
this.fs.copyTpl(
this.templatePath("_package.json"),
this.destinationPath("package.json"),
{
name: this.props.name,
description: this.props.description,
keywords: this.props.keywords.split(","),
author: this.props.author,
email: this.props.email,
repository: this.props.repository,
homepage: this.props.homepage,
license: this.props.license
}
);
}
// 安裝依賴(lài)
install() {
this.installDependencies();
}
};
編寫(xiě)模板代碼
前面我們把一個(gè)腳手架的基本框架都寫(xiě)好了,它可以接受用戶(hù)輸入的內(nèi)容,可以寫(xiě)文件,可以安裝依賴(lài),但接收用戶(hù)輸入的數(shù)據(jù)怎么用?寫(xiě)進(jìn)什么文件?安裝什么依賴(lài)呢?這些都是模板文件做的事情?,F(xiàn)在就開(kāi)始最主要的一部分:編寫(xiě)模板文件。
模板文件是你為用戶(hù)生成的一個(gè)項(xiàng)目 demo,讓用戶(hù)看著這些示例代碼就可以開(kāi)工了,用戶(hù)應(yīng)該只需要專(zhuān)注于業(yè)務(wù)邏輯,而不用管打包構(gòu)建這些事。
首先建好模板目錄:
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .babelrc
├── jsdoc.json
├── README.md
├── package.json
├── build/
└── rollup.js
├── src/
└── index.js
├── test/
└── index.js
我們的模板package.json里已經(jīng)寫(xiě)好這些命令:
"scripts": {
"prebuild": "npm run lint && npm run test && npm run doc",
"build": "node ./build/rollup.js",
"lint": "eslint --ext .js, src",
"test": "mocha --require babel-register --require babel-polyfill --bail",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"doc": "jsdoc -c ./jsdoc.json"
}
npm run lint用 eslint 進(jìn)行語(yǔ)法檢查,在編譯前就避免語(yǔ)法錯(cuò)誤和統(tǒng)一代碼風(fēng)格。npm test運(yùn)行單元測(cè)試npm run doc根據(jù)注釋生成文檔npm run changelog根據(jù)git log生成項(xiàng)目日志,改動(dòng)記錄一目了然npm run prebuild編譯前的語(yǔ)法檢查、 運(yùn)行測(cè)試、生成文檔npm run build編譯打包
我們可以使用<%= name %>這樣的模板語(yǔ)法使用腳手架中的context上下文,無(wú)論是用戶(hù)輸入的數(shù)據(jù),還是程序自己的變量:
{
"name": "<%= name %>",
"description": "<%= description %>",
"version": "1.0.0",
"private": false,
"main": "dist/<%= name %>.umd.js",
"module": "dist/<%= name %>.es.js"
}
詳細(xì)代碼請(qǐng)到github查看。
運(yùn)行測(cè)試用例
為了保證代碼的健壯性,我們必須進(jìn)行單元測(cè)試。其實(shí)我們用generator生成的腳手架代碼中已經(jīng)有測(cè)試代碼示例了,改成自己的邏輯就可以測(cè)試我們的腳手架邏輯了?,F(xiàn)在我們來(lái)測(cè)試下文件是否生成:
'use strict';
const path = require('path');
const assert = require('yeoman-assert');
const helpers = require('yeoman-test');
describe('generator-javascript-plugin:app', () => {
beforeAll(() => {
return helpers
.run(path.join(__dirname, '../generators/app'))
.withPrompts({ someAnswer: true });
});
it('creates files', () => {
assert.file(['build/rollup.js']);
assert.file(['dist']);
assert.file(['src']);
assert.file(['test']);
assert.file(['package.json']);
assert.file(['.babelrc']);
...
});
});
執(zhí)行命令
npm test

運(yùn)行腳手架
到此,我們的腳手架開(kāi)發(fā)完了,接下來(lái)實(shí)際運(yùn)行下看看是否正確。
由于我們的腳手架還是本地開(kāi)發(fā),它尚未作為全局 npm 模塊提供。我們可以使用 npm 創(chuàng)建全局模塊并將其符號(hào)鏈接到本地模塊。在項(xiàng)目根目錄運(yùn)行:
npm link
這樣就可以調(diào)用yo javascript-plugin運(yùn)行腳手架了。你可以在終端看到運(yùn)行過(guò)程。

發(fā)布
寫(xiě)好的腳手架發(fā)布出去才能讓更多的人使用,發(fā)布腳手架和發(fā)布其他 npm 包一樣。如何發(fā)布?
github 地址
generator-javascript-plugin這個(gè)腳手架已經(jīng)發(fā)布到npm上,可以下載或訪問(wèn)源碼。
以上就是詳解使用yeoman打造自己的項(xiàng)目腳手架的詳細(xì)內(nèi)容,更多關(guān)于yeoman項(xiàng)目腳手架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
前端利用jsencrypt.js進(jìn)行RSA加密示例詳解
這篇文章主要為大家介紹了前端利用jsencrypt.js進(jìn)行RSA加密示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)(二)
這篇文章主要介紹了微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2017-05-05
JavaScript生成器函數(shù)Generator?Functions優(yōu)缺點(diǎn)特性詳解
這篇文章主要為大家介紹了JavaScript生成器函數(shù)Generator?Functions的特性及優(yōu)點(diǎn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
JavaScript設(shè)計(jì)模式之單例模式應(yīng)用場(chǎng)景案例詳解
這篇文章主要為大家介紹了JavaScript中單例模式的應(yīng)用場(chǎng)景案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
微信小程序 仿貓眼實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了微信小程序 仿貓眼實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03
微信小程序 開(kāi)發(fā)MAP(地圖)實(shí)例詳解
這篇文章主要介紹了微信小程序 開(kāi)發(fā)MAP(地圖)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
js解決移動(dòng)端滾動(dòng)穿透問(wèn)題方案詳解
這篇文章主要為大家介紹了js解決移動(dòng)端滾動(dòng)穿透問(wèn)題方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07

