本文做者:ivweb qcyhust 原文出处:IVWEB社区 未经赞成,禁止转载css
在一个项目的初始化阶段咱们通常会作什么呢?若是有一个可参考的项目,是否是会复制这个项目,而后修改为新项目?若是是要在项目中增长一个新页面或是新组件,在开始的时候是否是会复制粘贴先前已存在的页面、组件代码。这些初始化时复制粘贴的操做意味着咱们即将着手的项目有大量的结构代码(好比构建脚本,开发脚手架)是存在共性的,在开发过程当中,新建一个页面,新开发一个组件,甚至新写一个路由均可能利用一个相同结构的代码来往里面填写新的内容。那么一个能帮助开发者生成自定制结构文件的小工具就会在这中使用场景下派上用场,它能让开发者的工做焦点回到真正的业务逻辑开发上,同时也能为团队开发体统一份统一的代码规范。html
yeoman是一个能够帮助开发者快速开启一个新项目的工具集。yoeman提出一个yeoman工做流的概念,经过脚手架工具(yo),构建工具(grunt gulp等)和包管理器(npm bower等)的配合使用让开发者专一于业务的解决上而不是其余小事情。在yeoman的官网中能够搜索到不少用于初始化项目的generator,能够用于快速开启项目。同时yeoman也提供给开发者如何定义本身的generator,全部咱们本身开发的generator都做为一个插件能够经过yo工具建立出咱们须要的结构。前端
本身建立的generator能够是很简单的建立几个模板页面,也能够经过和用户交互构建一套量身定制的项目,取决于项目初始化的策略。能够利用yeoman的generator-generator工具来开始构建本身的generator。node
先从一个简单的模板页面入手,建立简单的generator。假设咱们的须要的demo项目目录结构是这样的:git
├───index.html
|───styles/
| └───style.css
├───scripts/
└───main.js
复制代码
以前提到,咱们的generator是一个插件,因此首先须要建立成一个node module包,在yeoman中这个包的名字应该是generator开头的,那么咱们这个generator就叫作generator-demo。每个包的keyword中必须包含yeoman-generator
。files属性要指向项目的模板目录。github
第一步是经过npm init
或是本身手动建立generator的package.json,项目依赖yeoman-generator。也能够利用generator-generator来初始化。web
{
"name": "generator-demo",
"version": "0.1.0",
"description": "",
"files": [
"generators"
],
"keywords": [
"yeoman-generator"
],
"main": "generators/app/index.js",
"dependencies": {
"yeoman-generator": "^1.0.0"
}
}
复制代码
咱们的generator项目目录:npm
├───package.json
└───generators/
└───app/
└───index.js
└───templates/
├───index.html
├───styles/
│ └───style.css
└───scripts/
└───main.js
复制代码
第二步就是往template中填充内容,也就是demo项目的三个基本文件的内容。这里简单提供一个例子: template/index.htmljson
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<title>generator-demo</title>
<link rel="stylesheet" href="styles/style.css">
</head>
<body>
<h1>helle <%= name %></h1>
<script src="scripts/main.js"></script>
</body>
</html>
复制代码
yeoman采用ejs模板语法,能够在模板文件中传入参数。gulp
template/styles/style.css
* {
margin: 0;
padding: 0;
}
复制代码
template/sctipts/main.js
'use strict';
window.onload = function() {
console.log('generator success');
};
复制代码
到这一步后就是扩展generator。yeoman提供了一个基础的generator,它有本身的生命周期和事件,功能强大。能够经过扩展这个基础generator来实现咱们项目的初始化需求。因而第三步就是编辑app/index.js来扩展它:
const Generator = require('yeoman-generator');
const path = require('path');
const fs = require('fs');
const mkdirp = require('mkdirp');
const utils = require('../utils');
const log = utils.log;
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
this.props = {
projectName: 'demo',
name: 'world'
};
}
writing() {
const { projectName, name } = this.props;
const temps = {
'index.html': { name: this.props.name }
};
fs.readdir(this.sourceRoot(), (err, items) => {
for(let item of items) {
if(temps[item]) {
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(projectName, item),
temps[item]
);
} else {
this.fs.copy(
this.templatePath(item),
this.destinationPath(projectName, item)
);
}
}
});
}
end() {
log.info('generator success');
}
};
复制代码
第四步就是运行generator。yoeaman的henerator是一个全局npm module,咱们在本地开发的generator能够经过软链接的方式生成它的全局npm包。在generator-demo的根目录下运行npm link
,它会在本地的全局npm目录下安装咱们新建的generator。
在肯定本地已经安装yo工具(npm install -g yo
)后,在你须要初始化demo项目的地方运行yo demo
,等命令执行完毕,就能够看到新建的项目了。
在扩展基础generator时,咱们能够给实例添加自定义的方法,每个添加进去的方法都会在generator调用的时候被调用,并且一般来说,这些方法是按照顺序调用的。除非是已下划线_开头的私有方法,或是定义在实例上的方法。
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
this.task = () => {
this.log('instance task');
}
}
method1() {
this.log('method 1');
}
method2() {
this.log('method 2');
}
_task() {
this.log('private task');
}
};
// 输出:
// 'method 1'
// 'method 2'
复制代码
每个方法在yeoman中都被认为是一个任务,这些任务都会被run loop调用。yeoman的run loop是一个有优先级的队列系统。采用Grouped-queue来维护yeoman的事件队列。除了自定义的方法外,yeoman有不少特殊的事件方法,按照优先级排序:
initializing - 初始化开始
prompting - 调用this.prompt()与用户产生交互
configuring - 建立配置文件(package.json,config.js等)
default - 方法都不匹配这些优先级时,就会是default优先级(自定义方法会被划入default)
writing - 建立项目文件
conflicts - 文件建立中产生冲突的处理
install - 调用(npm, bower)包install
end - 结束项目初始化 其余自定义方法在configuring和writing按顺序优先级调用。
如今咱们来给generator增长用户交互和package.json,让它能构建出一个更复杂的项目。仍是修改app/index.js,首先增长prompting:
prompting() {
return this.prompt([{
type: 'input',
name: 'projectName',
message: '请输入项目名字',
default: 'default-name'
}, {
type: 'confirm',
name: 'package',
message: '须要package.json文件',
default: true
}, {
type: 'input',
name: 'name',
message: '请输入你的名字',
default: 'world'
}]).then((answers) => {
this.log('create project: ', answers.projectName);
this.log('by: ', answers.name);
this.props = answers;
});
}
复制代码
增长configuring:
configuring() {
const { projectName, name } = this.props;
let packageSettings = {
name: projectName,
version: '0.0.1',
description: 'YOUR DESCRIPTION - Generated by generator-demo',
main: '',
scripts: {},
repository: '',
keywords: [],
author: name,
devDependencies: {},
dependencies: {}
};
this.fs.writeJSON(this.destinationPath(projectName, 'package.json'), packageSettings);
}
复制代码
package.json能够直接建立也能够利用模板文件建立或是将其中的属性抽象到配置文件中,这样方便修改。
yeoman genenrator的功能远不仅本文演示的这些,它还支持异步事件(prompting自己就返回一个promise对象)、install依赖包等等。
一个genenrator也不仅是建立一个模板,它同时支持多种模板的须要,好比咱们有个复杂的项目,files里面能够添加多个generator,主generator负责初始化项目的时候建立项目的主要文件并安装好各类依赖,在项目的开发中,咱们须要增长一个container或是router的话,调用对应的genenrator便可,生成的模板能够将注意力放在内容上,提升开发效率。
腾讯NOW直播前端工程化解决方案feflow正式开源啦~: https://github.com/feflow/feflow 感兴趣的能够star一下哈~