利用nodejs写个前端脚手架来玩玩

简介


自己写脚手架是不是感觉有点牛逼的样子,我们大部分的前端,有的天天忙着写页面调样式,有的忙着写业务逻辑,对于脚手架我们可能觉得会用就行了。对于脚手架我们可能天天都在接触,像npm包管理工具,vue-cli,webpack…,但是这些脚手架是怎么做出来的,我们自己能不能做一个出来玩玩,可能就没有考虑过了。其实这些离我们并不遥远,甚至触手可及,只要你愿意。而且写脚手架是一件非常好玩的事情,因为写的一个小工具可以减少很多的重复劳动,想想就激动。好了,废话不多说了,今天就来讲讲如何利用nodejs做一个脚手架。
技术准备:
- nodejs
- commander模块(命令行参数处理模块)
- co 模块(异步流程控制模块)
- co-prompt 模块(消息提示模块)
- chalk 模块(输出字体颜色模块)
- github (远程托管仓库)
如果上面提到的你不熟悉,其实也没什么看看API文档就可以了,通过npm包管理工具将模块下下来,然后进入对应的模块文件夹中去,然后执行

npm docs

浏览器会自动进入模块的托管地址github,在这里README文件中就有他的使用说明。

基本部分构成

首先我们来看看一个脚手架由那些基本部分构成。

1.帮助文档

脚手架离不开命令及参数,那这些参数和命令代表什么我们需要有个说明告诉使用者,就像下图这样:

这里写图片描述

当输入命令的时候出现这样的文档就代表脚手架安装成功或者已经安装了该模块。同时我们可以一目了然的看见有哪些可用的参数和命令已经他们们简介。

2.参数项

通过上面vue的帮助文档我们可以看出其中有一项叫做options,其实就是参数项。参数项一般是用来输出一些信息的,像这里的参数-v 是用来显示版本号,-h 显示帮助文档,当然你也可以定义其他的参数,只要你乐意。

3.命令项

命令项,是用来定义常用的命令。就像上面图中commands中的那样。

实践

下面我们就来开始写一个着手写一个脚手架wyy(构建一vue项目(支持多页面和单页面));

首先我们看看一个脚手架(vue-cli)的目录结构是怎么样的:
这里写图片描述
bin是一般用来存放命令文件的
docs存放文档
lib存放自定义类文件

新建文件夹wyy 安装前面提到的模块

cnpm install co co-prompt chalk commander --save-dev

生成package.json文件

cnpm init

编写入口文件index.js

let program = require('commander');
let package = require('./package.json');
let init = require('./bin/init');
let list = require('./bin/list')
program
    .version(package.version)
    .usage('<command> [options]');
program.command('init (template)')
    .description("创建新新项目")
    .alias('i')
    .action(function(template){
         init(template);
    })
program.command('list')
    .description("显示可使用的模板列表")
    .alias('l')
    .action(function(){
       list();
    })
program.parse(process.argv);
if(program.args.length==0){
    //这里是处理默认没有输入参数或者命令的时候,显示help信息
    program.help();
}

执行node index.js显示

这里写图片描述

这样入口文件就写好了。下面我们开始编写我们定义的两个命令‘init’和‘list’;
为了代码的可读性和可维护性我们把两个命令分别写在不同的文件中,然后通过require引入

list.js:

list这个命令是用来显示当前脚手架有几种模板,已经模板的简介。

let co = require('co');
let prompt = require('co-prompt');
let chalk = require('chalk');
let templates = require("../templates.js");
module.exports = function(){
    for(let key in templates.list){
        let temp = templates.list[key]
        console.log(
        '  ' + chalk.green('★') +
        '  ' + chalk.green(temp.name) +
        ' -' + temp.desc)
    };
    if(!templates.list||templates.list.length==0){
        console.log(chalk.yellow('当前无可用模板'))
    }
}

templates.js里面是保存的模板信息,这里的path属性是模板的托管地址,这里默认是托管在github上的:

module.exports = {
    list:[
       {
        "name":"webpack",
        "path":"vuejs-templates/webpack",
        "desc":"A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction."
      },{
        "name":"webpack-simple",
        "path":"vuejs-templates/webpack-simple",
        "desc":"A simple Webpack + vue-loader setup for quick prototyping."
      },{
        "name":"webpack-multi",
        "path":"wang839305939/vue-cli-multi-page",
        "desc":"支持多页面编译的vue模板"
      }
    ]
}

运行node wyy list显示如下
这里写图片描述

init.js

init命令是用来创建新项目的,其实说白了就是将对应的模板下载下来,然后改成你自己项目的名字。废话不多说,直接上代码:

let co = require('co');
let prompt = require('co-prompt');
let chalk = require('chalk');
let templates = require("../templates");
let download = require('download-git-repo');
let ora = require('ora');
let fs  = require('fs');
let util  = require('./util.js')
let temps = {};
let temps2 = {};
module.exports = function(name) {
    getTemps(templates);
    co(generator(name));
}

function getTemps(templates) {
    for(let key in templates.list) {
        let item = templates.list[key]
        temps[key] = item.name;
        temps2[item.name] = item.path;
    };
}

let generator = function *(name) {
        let tempName  = name;
        let path = temps2[name];
        if(!name) {
           console.log('    可用模板列表:')
           for(let key in temps) {
                let tempName = temps[key]
                console.log(
                    '     ' + chalk.green(key) +
                    ' : ' + chalk.green(tempName))
                };
               tempName = yield prompt("    请选择模板类型:")
               path = temps2[temps[tempName]];
        }
        if(temps[tempName] || temps2[tempName]) {
                console.log('    ----------------------------------------')
                let projectName = yield prompt("    请输入项目名称(demo):")
                if(!projectName) {
                    projectName = "demo"
                }
                console.log('    ----------------------------------------')
                downloadTemplates(path,projectName);

        } else {
                console.log(chalk.red(`   ✘模版[${tempName}]不存在`))
                process.exit(0);
        }
    }

function downloadTemplates(path,projectName) {
        let spanner = ora("   正在构建,客官請稍等......");
        spanner.start();
        if(fs.existsSync('download')){
            //刪除原文件
            util.rmdirSync('download');
        }
        download(path,__dirname+'/download', function(err) {            
                if(err) {
                    spanner.stop();
                    console.log('    ','----------------------------------------')
                    console.log('    ',chalk('x构建失败'), err);
                    process.exit(0);
                }
                startBuildProject(spanner,projectName)
        })

    }

function startBuildProject(spanner,projectName){
    let targetPath = process.cwd()+"/"+projectName
    util.copyDirSync(__dirname+'/download',targetPath)
    console.log('    ','----------------------------------------')
    console.log('    ',chalk.green('★'),chalk.green('项目构建成功'));
    spanner.stop();
    process.exit(0);
}

执行node wyy.js init webpack:

这里写图片描述

如果init 后面不加模板类型,会提示选择模板类型的:

这里写图片描述

这样一个简单的脚手架就基本成型了。但是大家可以发现我们每次执行命令都是 node wyy.js 后面加上命令,这样感觉很难看,而且我们还必须在脚手架入口文件所在目录下执行才有效,不然会提示文件路径找不到。这怎么解决呢?

全局安装

我们在安装npm版的时候可以全局安装

npm install xxxx -g

这样安装完之后我们的命令就可以在任意地方使用了,如果我们上面的脚手架用全局安装的话不是就可以解决这个问题了吗?

其实在package.json中有个配饰项叫bin,我们只要在这里配置上我们的入口文件就行了。

"bin": {
    "wyy": "bin/wyy"
  },

然后将原来入口文件的wyy.js文件类型去掉直接叫wyy,在文件内容的头上加上:

#!/usr/bin/env node

最后我们在教授叫更目录下执行

npm link

执行成功后我们就可以在任意地方使用我们的命令了,比如:

wyy --help
wyy list
wyy init

这样这个脚手架有像模像样了。

在执行npm link的时候可能你会遇见各种错误,使得npm link 执行不成功。那怎么解决呢?

其实npm link执行成功后 你可以理解成他就是将你这个脚手架工程复制到了npm 系统安装目录的node_modules下了,然后在npm系统安装目录下生成了两个启动文件一个是在linux写的shell文件一个是.bat的启动文件。文件内容就是:

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\node_modules\wyy\bin\wyy" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\node_modules\wyy\bin\wyy" %*
)

我们在自动这个原理后,我们可以直接将我们的项目拷贝到npm的全局安装目录的node_modules下,然后在npm目录下手动创建两个启动脚本,然后将启动脚本的名字换成我们要启动的项目名字,将脚本内容中的路径换成我们项目的入口文件地址,然后就OK了。

总结:

还是那句话,写脚手架其实离我们并不遥远,甚至触手可及,只要你愿意。程序员就是需要不断的作,如果只是一成不变,天天重复的敲着同样的代码,那多无聊,久来久去曾经的那一点点激情早就被消磨没了。

猜你喜欢

转载自blog.csdn.net/wang839305939/article/details/78276668