本来早就想写这篇文章的,由于有其他事情耽搁了(可能还是因为太懒),就拖到了现在,如果再不记下来,估计会抛到九霄云外了。
NodeJs的出现,让前端工程化的理念不断深入,正在向正规军靠近。先是带来了Gulp、webpack等强大的构建工具,随后又出现了vue-cli和create-react-app等完善的脚手架,提供了完整的项目架构,让我们可以更多的关注业务,而不必在项目基础设施上花费大量时间。
但是,这些现成的脚手架未必就能满足我们的业务需求,也未必是最佳实践,这时我们就可以自己来开发一个脚手架。当然,这其实很简单,利用npm上现成的轮子就可以搞定,这里做个记录,仅当备忘,以抛砖引玉。
在上半年的一个项目中需要自定义一个脚手架,来帮助小伙伴们提高开发效率,统一代码输出质量,并解决一些使用上的问题,当然也是为了装装逼。
在使用脚手架方式构建之前,我们遇到了这几个问题:
每个项目创建的时候,需要去Git仓库拉取项目模板或者拷贝之前的项目,这样做有两个问题
所以我们来解决这些问题,思路如下:
当然,除了上述需求,我们还可以再做些额外工作:
首先,我们需要创建一个项目,这里就叫yncms-template-cli, 项目结构如下:
- commands // 此文件夹用于放置自定义命令
- utils
- index.js // 项目入口
- readme.md
为了测试,我们先在index.js放点内容:
#!/usr/bin/env node
// 必须在文件头添加如上内容指定运行环境为node
console.log('hello cli');
对于一般的nodejs项目,我们直接使用node index.js就可以了,但是这里是脚手架,肯定不能这样。我们需要把项目发布到npm,用户进行全局安装,然后就可以直接使用我们自定义的命令,类似yncms-template这样。
所以,我们需要将我们的项目做下改动,首先在packge.json中添加如下内容:
"bin": {
"yncms-template": "index.js"
},
这样就可以将yncms-template定义为一个命令了,但此时仅仅只能在项目中使用,还不能作为全局命令使用,这里我们需要使用npm link将其链接到全局命令,执行成功后在你的全局node_modules目录下可以找到相应文件。然后输入命令测试一下,如果出现如下内容说明第一步已经成功一大半了:
PS E:\WorkSpace\yncms-template-cli> yncms-template
hello cli
但是,目前这个命令只有我们自己电脑可以用,要想其他人也能安装使用,需要将它发布到npm,大致流程如下:
这一步比较简单,不多说,但是请注意如下几点:
package.json中有几个字段需要完善:
发布成功后,需要等待一会儿才可以在npm仓库搜索到。
既然是脚手架,肯定不能只让它输出一段文字吧,我们还需要定义一些命令,用户在命令行输入这些命令和参数,脚手架会做出对应的操作。这里不需要我们自己去解析这些输入的命令和参数,有现成的轮子(commander)可以使用,完全可以满足我们的需要。
安装好commander后,我们将index.js中内容改为如下:
#!/usr/bin/env node
const commander = require('commander');
// 利用commander解析命令行输入,必须写在所有内容最后面
commander.parse(process.argv);
这时,虽然我们没有定义任何命令,但是commander内部给我们定义了一个帮助命令--help(简写-h):
PS E:\WorkSpace\yncms-template-cli> yncms-template -h
Usage: index [options]
Options:
-h, --help output usage information
接下来,我们再创建一个查询版本的命令参数,在index.js增加如下内容:
// 查看版本号
commander.version(require('./package.json').version);
这样,我们在命令行就可以查看版本号了:
PS E:\WorkSpace\yncms-template-cli> yncms-template -V
1.0.10
PS E:\WorkSpace\yncms-template-cli> yncms-template --version
1.0.10
默认参数是大写V,如果需要改成小写,将上面内容做如下改动即可:
// 查看版本号
commander
.version(require('./package.json').version)
.option('-v,--version', '查看版本号');
PS E:\WorkSpace\yncms-template-cli> yncms-template -h
Usage: index [options]
Options:
-V, --version output the version number
-h, --help output usage information
接下来,我们来定义一个init命令,如yncms-template init test。
在index.js中增加如下内容:
commander
.command('init <name>') // 定义init子命令,<name>为必需参数可在action的function中接收,如需设置非必需参数,可使用中括号
.option('-d, --dev', '获取开发版') // 配置参数,简写和全写中使用,分割
.description('创建项目') // 命令描述说明
.action(function (name, option) { // 命令执行操作,参数对应上面的设置的参数
// 我们需要执行的所有操作,都在这里完成
console.log(name);
console.log(option.dev);
});
现在测试一下:
PS E:\WorkSpace\yncms-template-cli> yncms-template init test -d
test
true
commander具体的用法,请自行查看官方文档。
如此,一个自定义命令雏形就算完成了,然还有几件事情要做:
上面,我们定义了init命令,但是并没有达到初始化项目的目的,接下来我们就实现一下。
一般来说,项目模板有两种处理方式:
首先,我们利用download-git-repo封装一个clone方法,用于从git拉取项目。
// utils/clone.js
const download = require('download-git-repo');
const symbols = require('log-symbols'); // 用于输出图标
const ora = require('ora'); // 用于输出loading
const chalk = require('chalk'); // 用于改变文字颜色
module.exports = function (remote, name, option) {
const downSpinner = ora('正在下载模板...').start();
return new Promise((resolve, reject) => {
download(remote, name, option, err => {
if (err) {
downSpinner.fail();
console.log(symbols.error, chalk.red(err));
reject(err);
return;
};
downSpinner.succeed(chalk.green('模板下载成功!'));
resolve();
});
});
};
// commands/init.js
const shell = require('shelljs');
const symbols = require('log-symbols');
const clone = require('../utils/clone.js');
const remote = 'https://gitee.com/letwrong/cli-demo.git';
let branch = 'master';
const initAction = async (name, option) => {
// 0. 检查控制台是否可以运行`git `,
if (!shell.which('git')) {
console.log(symbols.error, '对不起,git命令不可用!');
shell.exit(1);
}
// 1. 验证输入name是否合法
if (fs.existsSync(name)) {
console.log(symbols.warning,`已存在项目文件夹${name}!`);
return;
}
if (name.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)) {
console.log(symbols.error, '项目名称存在非法字符!');
return;
}
// 2. 获取option,确定模板类型(分支)
if (option.dev) branch = 'develop';
// 4. 下载模板
await clone(`direct:${remote}#${branch}`, name, { clone: true });
};
module.exports = initAction;
测试一下,不出意外就可以成功拉取项目了。
这里拉取的项目是和远程仓库关联的,我们需要将其删掉(由于我们项目是svn管理,所以直接把.git文件夹删掉,如果使用git的话,可以git init初始化即可),清理掉一些多余文件:
// commands/init.js
// 5. 清理文件
const deleteDir = ['.git', '.gitignore', 'README.md', 'docs']; // 需要清理的文件
const pwd = shell.pwd();
deleteDir.map(item => shell.rm('-rf', pwd + `/${name}/${item}`));
在上述过程中,我们实现了一个脚手架的基本功能,大致分为三个流程(拉取模板->创建项目->收尾清理),也解决了上面我项目中遇到的第一个问题。接下来,我们就来看下第二个问题如何解决。
解决的思路就是在创建项目的时候,就通过命令行强制要求开发人员输入对应的配置,然后自动写入配置文件,这样就可以有效避免忘记填写的尴尬。当然通过这种方式也可以实现根据用户的输入来动态初始化项目,达到个性化的目的。
这里我们直接使用现成的轮子inquirer就可以搞定,效果和VueCli创建项目一样,支持很多类型,比较强大,也比较简单,具体用法看官方文档就可以了。这里我直接上代码,在第4步(下载模板)前面增加如下:
// init.js
const inquirer = require('inquirer');
// 定义需要询问的问题
const questions = [
{
type: 'input',
message: '请输入模板名称:',
name: 'name',
validate(val) {
if (!val) return '模板名称不能为空!';
if (val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)) return '模板名称包含非法字符,请重新输入';
return true;
}
},
{
type: 'input',
message: '请输入模板关键词(;分割):',
name: 'keywords'
},
{
type: 'input',
message: '请输入模板简介:',
name: 'description'
},
{
type: 'list',
message: '请选择模板类型:',
choices: ['响应式', '桌面端', '移动端'],
name: 'type'
},
{
type: 'list',
message: '请选择模板分类:',
choices: ['整站', '单页', '专题'],
name: 'category'
},
{
type: 'input',
message: '请输入模板风格:',
name: 'style'
},
{
type: 'input',
message: '请输入模板色系:',
name: 'color'
},
{
type: 'input',
message: '请输入您的名字:',
name: 'author'
}
];
// 通过inquirer获取到用户输入的内容
const answers = await inquirer.prompt(questions);
// 将用户的配置打印,确认一下是否正确
console.log('------------------------');
console.log(answers);
let confirm = await inquirer.prompt([
{
type: 'confirm',
message: '确认创建?',
default: 'Y',
name: 'isConfirm'
}
]);
if (!confirm.isConfirm) return false;
获取到用户输入的配置以后,就可以写入配置文件或者做个性化的处理了,这个太简单,我这里就不赘述了。
到这里,一个完全满足需求的脚手架就完成了,但是作为一个有追求的程序员,我们可以在界面和易用性上面再做点什么:
const installSpinner = ora('正在安装依赖...').start();
if (shell.exec('npm install').code !== 0) {
console.log(symbols.warning, chalk.yellow('自动安装失败,请手动安装!'));
installSpinner.fail(); // 安装失败
shell.exit(1);
}
installSpinner.succeed(chalk.green('依赖安装成功!'));
notifier.notify({
title: 'YNCMS-template-cli',
icon: path.join(__dirname, 'coulson.png'),
message: ' 恭喜,项目创建成功!'
});
// 8. 打开编辑器
if (shell.which('code')) shell.exec('code ./');
shell.exit(1);
到这里,会发现开发一个脚手架其实很简单,都是使用现成的轮子就可以搞定,不晓得哪位大牛说过玩NodeJS就是玩轮子。
除了上述方法,我们也可以直接通过大名鼎鼎的Yeoman来创建,不过个人觉得没必要,毕竟这玩意也不难。
一个好的脚手架应该是能够解决工作中遇到的问题,提高开发效率的。
原文:https://segmentfault.com/a/1190000021390776
像我们熟悉的 vue-cli,react-native-cli 等脚手架,只需要输入简单的命令 vue init webpack project,即可快速帮我们生成一个初始项目。在实际工作中,我们可以定制一个属于自己的脚手架,来提高自己的工作效率。
安装生成器npm install express-generator -g,创建名称为APP的应用,在浏览器中使用 localhost:3000访问。注:该模板默认用的是 .jade 文件作为模板渲染 若要使用 ejs 可按照一下方法配置
本文通过一个简单的例子来告诉大家如何使用 Yeoman 快速创建脚手架。要了解更多 yeoman-generator 的开发与使用,可以参考社区里大家写的各类 generator。目前在 npm 上有超过 8000 个 yeoman-generator,也许就会有你的菜。
在前端工程化过程中,为了解决多项目中,相似度高的工作,便诞生许多前端脚手架,这里记录下自己实现一个简易前端脚手架过程的实践。主要是解决多个页面相似内容的复制粘贴问题
vue-cli, create-react-app、react-native-cli 等都是非常优秀的脚手架,通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步配置,有效提升开发体验。尽管这些脚手架非常优秀,但是未必是符合我们的实际应用的
commander在Vue-cli、creat-app(react)中都起到了很大的作用,这种创建脚手架的方式与vue-cli的方式不同,vue-cli则是使用git远程拉取项目再完成初始化,这样一来要比这种更加的方便灵活,每次模板变更不需要再次上传包
一直想建一个自己公司自用的脚手架,可以方便的快速开发。于是开始看vue-cli的源码和一些网上的教程。发现,一款脚手架其实很简单,主要原理就是从远程下载一个模板来新建一个项目。同时提供了一系列的交互来动态的更改模板。
脚手架在前端工作流中负责项目起始阶段创建初始文件。与其他功能模块不同的是,脚手架是一个完全“启下”的模块,它没有任何前置依赖。创建完成项目初始文件之后,脚手架就再无用武之地了。
前端开发都少不了对后台的调用,后台地址配置在哪里,是一个很纠结的问题 为此大家开动脑筋,想了不少办法:
构建库的常见方法有两种:一种是自己手动构建webpack库打包,设置output为 library; 另一种是基于vue-cli3输出库资源包。我们采用第二种vue脚手架的方式构建库。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!