手把手教你搭建属于自己的前端脚手架工具
前面的话以前使用vue-cli,vue init webpack project-name一行命令就可以初始化一个我们自己的项目,小柒觉得好神奇,之后研究啦一下vue-cli,其实就是一个高级版的克隆。然后就决定自己实现一套属于自己的脚手架(xq-cli),目前xq-cli脚手架已经实现了有一段时间了,决定拿出来分享。xq-cli脚手架功能创建项目询问用户:询问用户新建项目的基本信息下...
前面的话
以前使用vue-cli
,vue init webpack project-name
一行命令就可以初始化一个我们自己的项目,小柒觉得好神奇,之后研究啦一下vue-cli
,其实就是一个高级版的克隆。然后就决定自己实现一套属于自己的脚手架(xq-cli
),目前xq-cli
脚手架已经实现了有一段时间了,决定拿出来分享。
xq-cli脚手架功能
创建项目
- 询问用户:询问用户新建项目的基本信息
- 下载模板:根据用户的选择下载对应的模板
- 生成配置文件:根据用户的选择生成对应的配置文件
- 生成新项目:在目录中生成新项目
初始化项目
项目创建好后,我们要对它进行初始化操作:
- 自动创建一个GitHub仓库,将我们的项目推送至仓库
- 安装依赖
启动项目
- 本地启动浏览
- 支持热更新
打包项目
- 打包我们的项目
前置知识
在开始实现时,我们必须要先了解一些工具:
commander:
-
作用:命令行工具,用来编写指令和处理命令行的。
-
用法:
const program = require('commander') program .command('init') .alias('i') .description('初始化') .option('-x, --xxx', 'xxx') // 有参数时使用 .action( () => { // 回调函数 } }) program.parse(process.argv)
vue init
就是这么来滴。
- commander API 介绍
- command - 自定义执行的命令,后面跟上自己定义的命令名称
- alias - 定义更短的命令行(配置别名)(懒人必备)
- description - 描述,它会在 --help里面显示
- option - 定义参数,接受四个参数
- action - 注册一个callback函数(执行命令之后,就会执行这个回调)
- usage:用户使用提示
- parse - 解析命令行,注意这个方法一定要放到最后调用
inquirer
- 作用:这是一个强大的交互式命令行工具,询问用户就靠它啦。
- 用法:
想一下,使用onst inquirer = require('inquirer'); inquirer .prompt([ // 一些交互式的问题 ]) .then(answers => { // 回调函数,answers 就是用户输入的内容,是个对象 });
vue init webpack project-name
时,是不是都要问你几个问题,项目名称、作者、描述等。 - inquirer 功能简介
- input - 输入
- validate - 验证
- list - 列表选项
- comfirm - 提示
- checkbox - 复选框
chalk
- 作用:美化我们滴命令行,一个颜色插件,用来修改命令行输出样式,通过颜色区分 info、error 日志,清晰直观
- 用法:
const chalk = require('chalk'); console.log(chalk.green('success')); console.log(chalk.red('error'));
ora
-
作用:用于显示加载效果,转圈圈滴那种
-
用法:
const ora = require('ora') let loading = ora('downloading template ...') loading.start()
log-symbols
- 作用:控制台彩色日志符号,用来显示√ 或者 × 等图标。(搭配chalk使用)
- 使用:
import symbol from 'log-symbols';
console.log(symbol.error, chalk.red('请输入项目名'));
console.log(symbol.success, chalk.green('配置文件更新完成'));
download-git-repo
-
作用:用来下载远程模板的
-
用法:
import downloadGit from 'download-git-repo'; downloadGit(repository , ProjectName, options ,callback); // repository : 为远程仓库地址 // ProjectName:是存放下载的文件路径,可以直接是文件名,默认是当前目录 // options: 一些选项,比如`{clone: boolean}`表示用http download // 还是 git clone 的形式下载 // callback: 回调函数
准备工作
1、先创建一个文件夹,小柒这里叫xq-cli
。
2、创建.babelrc文件,支持es6语法。
3、在该目录下执行npm init
,生成package.json文件,然后下载上面的依赖。
"dependencies": {
"babel-cli": "^6.26.0",
"babel-env": "^2.4.1",
"chalk": "^3.0.0",
"commander": "^5.0.0",
"download-git-repo": "^1.1.0",
"inquirer": "^7.1.0",
"ora": "^4.0.3",
"log-symbols": "^3.0.0",
"download-git-repo": "^3.0.2",
}
(有了package.josn文件,这样我们就可以发布我们的npm包了)。
4、新建一个bin文件夹,并在bin目录下新建一个无后缀名的cmd文件:
#!/usr/bin/env node
console.log('hello'); // 测试用
这个文件是我们整个脚手架的入口文件
再去package.json文件中配置bin
参数,专门放置用户的自定义命令:
使用npm link
命令将xq-cli
与xqc
软连接至全局,这样就可以全局使用xq-cli
或者xqc
这个命令来启动了。
(这里只是简单的测试一下)。
实际代码为:
#!/usr/bin/env node
require('../dist/main.js'); // 编译后的main.js文件作为入口文件
5、修改 package.json 中的 scripts 参数,指定可执行命令,实时编译脚本,让 node 能够识别并执行,开启watch实时监控。
6、补全目录结构
入口文件main.js
在整个脚手架的入口文件bin/cmd
中,我们引入了require('../dist/main.js');
这个编译后的文件,那我们就从src/main.js
文件开始:
这个文件就是使用commander
工具来定义我们的命令,主要代码:
program
.command(action)
.description(actionMap[action].description)
.alias(actionMap[action].alias)
.action(()=>{
switch (action) {
case 'create':
create(...process.argv.slice(3));
break;
case 'init':
init(program.username, program.token);
break;
case 'dev':
dev(program.port);
break;
case 'build':
build();
break;
default:
break;
}
})
});
// 项目版本
program
.version(require('../package.json').version, '-v --version')
.parse(process.argv);
action就是我们的命令,这里用四个命令,也就是对应脚手架的4个功能:create、init、dev 、build
它们对应的回调函数分别在create.js
、init.js
、dev.js
、build.js
。下面分别来实现这个几个功能。
创建项目(xq-cli create)
项目创建思路:
-
项目创建命令必须输入新建项目名称(必须做一层判断,避免创建的项目名称覆盖已有项目)
// 文件是否存在 let isExist = async(name) => { return new Promise((resolve) => { if(fs.existsSync(name)) { console.log(symbol.error, chalk.red('文件夹名已被占用,请更换名字重新创建')) }else{ resolve(); } }); }
-
询问用户,引导用户输入配置信息
// 询问用户 let promptList = [ { type: 'list', name: 'frame', message: 'please choose this project template', choices: ['vue', 'react'] }, { type: 'input', name: 'description', message: 'Please enter the project description:' }, { type: 'input', name: 'author', message: 'Please enter the author name:' } ]; let prompt = ()=>{ return new Promise((resolve)=>{ // inquirer提供prompt函数来实现询问,其参数为数组,询问将按数组的顺序来 inquirer.prompt(promptList) .then(answer => { resolve(answer); }) }); }
-
下载模板,下载模板比较耗时,可以通过ora提示用户 正在下载模板,下载完毕之后给出提示。
// 项目模块远程下载 let downLoadTemplate = async (ProjectName ,api) => { return new Promise((resolve, reject) => { downloadGit(api, ProjectName, {clone: true}, (err) => { if(err) { reject(err); }else{ resolve(); } }); }); };
-
项目下载完毕后,更新配置文件
// 更新json配置文件 let updateJsonFile = (fileName, obj) => { return new Promise((resolve) => { if(fs.existsSync(fileName)) { // 读出模板下的package.json文件 const data = fs.readFileSync(fileName).toString(); // 转为json对象 let json = JSON.parse(data); // 将用户输入的更新到模板package.json文件 Object.keys(obj).forEach(key => { json[key] = obj[key]; }); // 重写模板下的package.json文件 fs.writeFileSync(fileName, JSON.stringify(json, null, '\t'), 'utf-8'); resolve(); } }); }
上面的工具方法都在
unit.js
代码中,具体调用都在create.js
中,可以到create.js
文件中查看源码。
项目初始化(xq-cli init )
curl -u username:token https://api.github.com/user/repos -d "{\"name\": \"project name \"}"
使用这条命令就可以自动生成仓库,不用手动再去创建了。这里的username 就是你Github的用户名,token在Github上生成:(这里我的是windows系统,不支持单引号,所以要用转义符)
然后会有一个一长串数字字符串就是token了。
初始化项目的思路:
- Git初始化
- 创建Github仓库
- 关联Github仓库
- 更新package.json的repository配置
- 提交代码到GitHub仓库
- 安装依赖
let init = async (username, token)=>{
try {
await loadCmd(`git init`,'git初始化');
if(username === '' || token === '') {
console.log(symbol.warning, chalk.yellow('缺少参数无法创建远程仓库'));
}else {
const projectName = process.cwd().split('\\').slice(-1)[0];
await loadCmd(`curl -u qjk-xiaoqi:a97fc56cbefe4bdc092490067bb1a9727615a583 https://api.github.com/user/repos -d "{\"name\": \"kkk\"}"`, 'Github仓库创建');
// curl -u qjk-xiaoqi:a97fc56cbefe4bdc092490067bb1a9727615a583 https://api.github.com/user/repos -d "{\"name\": \"auto\"}"
await loadCmd(`git remote add origin https://github.com/${username}/${projectName}.git`, '关联远端仓库')
let loading = ora();
loading.start(`package.json更新repository:命令执行中...`);
await updateJsonFile('package.json', {
"repository": {
"type": "git",
"url": `https://github.com/${username}/${projectName}.git`
}
}).then(() => {
loading.succeed(`package.json更新repository: 命令执行完成`);
});
await loadCmd(`git add .`,"执行 git add");
await loadCmd(`git commit -a -m "init"`, '执行git commit')
await loadCmd(`git push --set-upstream origin master`, '执行git push')
}
await loadCmd(`npm install`,"安装依赖");
}catch(err) {
console.log(symbol.error, chalk.red('初始化失败'));
console.log(symbol.error, chalk.red(err));
process.exit(1);
}
}
let loadCmd = async(cmd, text) =>{
let loading = ora();
loading.start(`${text}: 命令执行中...`);
await child.exec(cmd);
loading.succeed(`${text}: 命令执行完成`)
}
相关代码在:init.js
文件中。
运行命令:xq-cli init -u xxx -t xxx
就可以了:
项目启动 (xq-cli dev )
项目启动就要用我们的Webpack啦,将项目的webpack.config.js
配置好。使用open-browser-webpack-plugin
插件来打开了页面, 并且支持热更新。
const config = require('./webpack.config.js');
let dev = (port) => {
// 启动项目后自动打开浏览器
config.plugins.push(new OpenBrowserPlugin({ url: `http://localhost:${port}` }))
// 创建一个小型服务器
new WebpackDevServer(webpack(config), {
contentBase: './public',// 配置http服务器的文件目录
hot: true, // 开启模块热替换
historyApiFallback: true // 开启html5 History API网页
}).listen(port, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
});
}
项目打包(xq-cli build)
这个就比较简单了,直接只用webpack
let build = () => {
// 打包
webpack(config, function(err, stats) {
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}))
console.log( symbol.success, chalk.green(' 打包完成'));
process.exit(1);
})
}
执行命令:xq-cli build
发布npm包
最后就可以发布我们自己的npm包啦,删掉创建的文件,去官网创建一个账号,就可以发布啦。
npm login
登录,npm publish
发布,之后就可以去官网上查看了:
(xq-cli已经被使用了,所以该为xqc)
更多推荐
所有评论(0)