出发点并不是小程序本身,是想要做一个脚手架(command-line interface),看过 VUE / REACT 脚手架,觉得很厉害,但是并不太知道里面是怎么做成的,所以最近研究了研究,看能不能自己做成一个 简单的脚手架。

之所以以小程序未入点来开发一个 cli, 是因为最近在做小程序的时候有点不方便, 比如每次新建一个 page module, 都要 mkdir x, touch x.json, x.wxml, x.js, x.wxss, 很麻烦。比如,我查找 bug 的时候习惯自己新建一个最小重复 bug 的新项目。如果每次执行一个命令行就能生成对应的项目骨架就好了。小程序本身有很好的开发工具, 比如选中 自动压缩等,开发工具会自己帮你压缩。所以这些其实不需要额外的工作量。

考虑到:如果能够生成一个 脚手架, 每次都只用执行比如下面的步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首次安装此 package
npm install mina-cli --save-dev
 
// 首次 init, 自动生成 项目名称, package.json, gitignore 等
mina-cli init
 
// 首次 你需要的modules
mina-cli create user, logger
 
// 下次如果还需要新建一个module,直接
mina-cli create pageModule
 
// 删除某一个 page module
mina-cli delete pageModule

这只是一个简单的想象,具体的实现过程,我总结了如下。项目地址在这里:mina cli


主要使用的技术

  1. nodejs
  2. commander.js
  3. bluebird
  4. fs-extra
  5. inquirer, chalk

其中 fs-extra 是用来简化 对文件操作的 npm 包。 inquirer 可以用来询问用户的需求,并且得到用户的答复。chalk 美化你的 terminal。最后的效果是:

效果图片


创建步骤

1.创建程序包清单,也就是 package.json ,说明所有的项目依赖。

1
npm init

按照 terminal 的提示一步一步来即可。

2.创建一个入口文件, 比如我们在 bin 目录下,新建一个 script.js 文件, 用于最开始的入口。

1
2
#! /user/bin/env node
console.log('this is an entry');

此时执行 bin/script.js,可以看到相应的 log 输出。当然这不是我们最后需要的效果。因为我们最好能以一个命令行 代替这个命令,让用户对我们的项目目录透明。

3.添加 bin 字段,package.json 中有一个 bin 字段,可以指定相应的命令。

1
2
3
"bin": {
"minaapp": "./bin/script.js"
}

4.关联 npm 包。此时,你执行 minaapp 还是没有效果的,我们需要关联一下:

1
npm link

npm link 的作用是本地安装这个 你自己开发的包。建立关联,简化你的工作流程。

5.输入 minaapp , 就可以运行了。

上面有些地方,比如 #! /user/bin/env node 和 npm link可能不太好理解,这些我在后面有详细的解释。


内容编写

当我们把入口编写好了以后,流程清楚了,就可以编写具体的内容实现。每一个目录结构,或者说内容都要有它存在的理由和方式。我设置的目录结构如下:

项目结构


入口文件

script.js 是我们的入口文件,作用当然是接受用户的输入命令,比如上面我们 提到的 init 和 create, 收到不同的命令解析后,再执行不同的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#! /usr/bin/env node
 
const program = require('commander');
 
// 定义当前版本
program
.version(require('../package').version);
 
// 初始化项目
program
.command('init')
.description('Generate a new project')
.alias('i')
.action(() => {
require('../command/init')()
});
 
// 新建modules
program
.command('create')
.description('Generate new modules')
.alias('c')
.action(() => {
require('../command/create')()
});
 
program.parse(process.argv);
 
if(!program.args.length){
program.help()
}

这里我使用了 tj 的 commander.js 来接受用户的输入。 比如 init 和 create 的时候,去到相应 command 目录下的不同入口, 执行不同的内容。注意这里一定要执行一下 parse, 并且最好当用户没有输入正确命令时,给用户一个 提示信息。这里用下 program.help()


命令分支

入口文件其中一个作用就是内容分发。比如 init 命令,到 command/init 下面去执行, create 命令 到 command/create 中执行。command 目录就是具体的命令目录。 如果以后扩展是不是也很方便, 比如增加一个 del 模块。先看下 init 和 create 都执行什么内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 
var path = require('path');
 
var inquirer = require('inquirer');
var fs = require('../lib/file');
var questions = require('../lib/questions');
 
var config = require('../lib/config');
 
module.exports = function () {
// 清空屏幕并显示 npm 信息
fs.showNpmInfo();
 
// 等待用户输入项目信息
inquirer
.prompt(questions.question1)
.then((args) => {
let dir = path.resolve(process.cwd(), './' + args.appName);
 
// 将 appName 写入配置文件
fs.outPutFile('./lib/config.json', JSON.stringify({
appPath: dir
})).then(flag => {
if(flag) fs.showCreateDirInfo('begin');
});
 
// 保证有用户输入的目录,并开始复制模板进入项目
fs.ensureDir(dir)
.then((flag) => {
return flag == true
? fs.copy('./template', dir)
: Promise.reject(flag);
 
}).then((flag) => {
//...
})
.catch(err => {
console.log('err', err);
});
});
};

在执行 minaapp init 的时候,首先接受一个用户的参数,这里用到 inquirer, inquirer 很方便的得到用户的输入结果。question 是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const question1 = [
{
name: 'appName',
type: 'input',
message: 'Enter your App\'s name: ',
validate( value ) {
if (value.length) {
return true;
} else {
return 'Please Enter your App\'s name';
}
}
}
];

用户输入项目名称,然后我利用 args 接受到对应的参数。得到后,再创建项目目录。创建目录的过程就是创建项目结构的时候,这个时候,因为模板内容比较多,我就直接放在了npm 包里,然后直接把目录拷贝过来就可以了。 所以如果有的项目团队有自己的项目结构,可以直接修改 template 里面的目录,执行目录可以直接修改。每次创建出来的目录和 template 中的目录相同。


其他模块

执行 create 目录当然就是 创建 pages 中的内容,这里肯定就比较容易,用下我们自己封装的 fs 文件,然后创建对应的 json,wxml,wxss,js 文件即可。

lib 目录中可以定义你自己的库。比如我定义的 file.js 封装了一层 promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module.exports = {
 
ensureDir(dir) {
return new Promise ((resolve, reject) => {
fs.ensureDir(dir, err => {
if(!err) resolve(true);
 
reject(err);
})
})
},
 
copy(from ,to) {
return new Promise((resolve, reject) => {
fs.copy(from ,to, err => {
if(!err) resolve(true);
 
reject(err);
})
})
},
 
 
//.....
}

大概的过程就是上面这样,这里只是一个简单的流程说明。记录下开发的过程。具体的代码地址可以在sevencai’s mina cli中看到。


几点说明理解

上面创建项目的过程中是一个简单的过程, 中间有几点想解释下:


#! /user/bin/env node

我们刚刚在代码的开头第一行,写了 #! /user/bin/env node ,这是为了指定我们的脚本执行所需要的解释程序。我们这里是利用的 nodejs, 所以使用 node 来作为脚本的解释程序,而#! /usr/bin/env node这样写的目的是使用env来找到node,并使用node来作为程序的解释程序。

那这里的 env 是啥 ? env 中规定了 很多系统的环境变量, 包括安装的一些环境路径等。不同的操作系统, node 的安装路径不一定相同, 但是环境变量都会存在 env 里, 所以我们这里可以用 env 来找到 node, 从而用 node 作为解释程序。 总结下,这里的 env 就是为了让我们的脚本在不同的操作系统能够上都可以被正常的解释和执行。


npm link

npm link 也是我这次新学到的东西。我看到了这样一篇文章:npm命令解析,觉得讲的很清晰明了。下面我把这段文字摘录下来。感谢。

开发Npm模块的时候,有时我们会希望,边开发边试用。但是,常规情况下,使用一个模块,需要将其安装到node_modules目录之中,这对于开发中的模块,显然非常不方便。npm link就能起到这个作用,建立一个符号链接,在全局的node_modules目录之中,生成一个符号链接,指向模块的本地目录。

为了理解npm link,请设想这样一个场景。你开发了一个模块myModule,目录为src/myModule,你自己的项目myProject要用到这个模块,项目目录为src/myProject。每一次,你更新myModule,就要用npm publish命令发布,然后切换到项目目录,使用npm update更新模块。这样显然很不方便,如果我们可以从项目目录建立一个符号链接,直接连到模块目录,就省去了中间步骤,项目可以直接使用最新版的模块

具体的使用方法是:

1.首先,在模块目录(src/myModule)下运行npm link命令。

2.src/myModule$ npm link 上面的命令会在Npm的全局模块目录内,生成一个符号链接文件,该文件的名字就是package.json文件中指定的文件名。

3./path/to/global/node_modules/myModule -> src/myModule 这个时候,已经可以全局调用myModule模块了。但是,如果我们要让这个模块安装在项目内,还要进行下面的步骤。

4.切换到项目目录,再次运行npm link命令,并指定模块名。

5.src/myProject$ npm link myModule 上面命令等同于生成了本地模块的符号链接。

6.src/myProject/node_modules/myModule -> /path/to/global/node_modules/myModule 然后,就可以在你的项目中,加载该模块了。

7.var myModule = require(‘myModule’); 这样一来,myModule的任何变化,都可以直接反映在myProject项目之中。但是,这样也出现了风险,任何在myProject目录中对myModule的修改,都会反映到模块的源码中。

8.如果你的项目不再需要该模块,可以在项目目录内使用npm unlink命令,删除符号链接。

9.src/myProject$ npm unlink myModule


总结

了解了创建一个 cli 的简单流程,了解了一些常用的 npm 库。知道了 npm link 是干什么用的, 以后创建小程序也更简单方便了。项目整体的功能很简单,我也不准备再多做别的命令解析,因为学习目的已经达到了。下面推荐一些阅读的文章, 感谢:

1.command-line-with-node
2.javascript-command-line-interface-cli-node-js
3.npm命令解析
4.nodejs 中相对路径和绝对路径
5.msh-using-npm-manage-node.js-dependence
6.前端扫盲-之打造一个Node命令行工具

commander.js 制作简易的 MINA CLI 脚手架的更多相关文章

  1. 利用css+原生js制作简易钟表

    利用css+原生js制作简单的钟表.效果如下所示 实现该效果,分三大块:html.javascript.css html部分html部分比较简单,定义一个clock的div,内部有原点.时分秒针.日期 ...

  2. 【带着canvas去流浪(13)】用Three.js制作简易的MARVEL片头动画(下)

    目录 一. 模型的制作 1.1 生成字体模型 1.2 多表面贴图 二. 镜头及动画 三. 大作业总结 示例代码托管在:http://www.github.com/dashnowords/blogs 博 ...

  3. 用js制作简易计算器及猜随机数字游戏

    <!doctype html><html><head> <meta charset="utf-8"> <title>JS ...

  4. 【带着canvas去流浪(12)】用Three.js制作简易的MARVEL片头动画(上)

    目录 一. 大作业说明 二.基本思路 三.视频纹理表面修复--UV映射 3.1 问题描述 3.2 纹理贴图的基本原理-UV映射 3.3 关键示例代码 四.小结 示例代码托管在:http://www.g ...

  5. 使用node.js制作简易爬虫

    最近看了些node.js方面的知识,就像拿它来做些什么.因为自己喜欢摄影,经常上蜂鸟网,所以寻思了一下,干脆做个简单的爬虫来扒论坛的帖子. 直接上代码吧. var sys = require(&quo ...

  6. 原生JS制作简易Tabs组件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. JS制作简易的考试答题管理系统

    答题卡系统: 网站运行效果 代码区域: HTML 代码: <style type="text/css"> body { font-size: 30px; backgro ...

  8. 用JS制作简易选项卡

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 30.0px Consolas; color: #2b7ec3 } p.p2 { margin: 0.0px ...

  9. 用JS制作简易的可切换的年历,类似于选项卡

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 30.0px Consolas; color: #2b7ec3 } p.p2 { margin: 0.0px ...

随机推荐

  1. 随手记录一下 Vue 下来框搜索 select2 封装成vue

    引入布局文件 <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css& ...

  2. linux 上安裝lnmp

    1.確保有一台服務器可以正常運行 2.熟練知道一些基本的命令 3.這裡我以lnmp集成環境為例 https://lnmp.org/install.html 4.安裝大約30分鐘左右 5.安裝完畢,訪問 ...

  3. Java基础--深克隆补充

    深克隆文章很多,这里推荐Java提高篇--对象克隆(复制). 以上文章条理清晰,一目了然,但最近在项目中碰到的实际问题是,所要克隆的对象是加上子类父类共计207个,无论用上述两种方式的哪一种,都要一一 ...

  4. 相对和绝对路径/cd命令/创建和删除目录mkdir/rmdir/rm命令

    2.6 相对和绝对路径 2.7 cd命令 2.8 创建和删除目录mkdir/rmdir 2.9 rm命令 绝对路径:从根开始的路径:文件所在的路径: 相对路径:相对于当前目录而言的路径:上一级或者下一 ...

  5. JSTL SQL标签库 使用

    推荐博客:http://blog.sina.com.cn/s/blog_4f925fc30101820u.html 怕博主把原文删了,所以在这里先保存一下. SQL标签库 JSTL提供了与数据库相关操 ...

  6. lkl风控.随机森林模型测试代码spark1.6

    /** * Created by lkl on 2017/10/9. */ import org.apache.spark.sql.hive.HiveContext import org.apache ...

  7. BarTender数据中的转义符序列知识讲解

    Datamatrix是二维码的一个成员,广泛用于商品的防伪.统筹标识.如果为 Data Matrix 条形码指定的“符号类型”不是 ECC 200,则将会启用“字符集”选项.Data Matrix 也 ...

  8. multiselect2side:jQuery多选列表框插件

    http://blog.csdn.net/rosanu_blog/article/details/8550723 http://www.bkjia.com/jQuery/449193.html < ...

  9. JS去除字符串左右两端的空格

    去除字符串左右两端的空格,在vbscript里面可以轻松地使用 trim.ltrim 或 rtrim,但在js中却没有这3个内置方法,需要手工编写.下面的实现方法是用到了正则表达式,效率不错,并把这三 ...

  10. LED驱动程序分析

    混杂设备 LED驱动程序分析 /******************************* * *杂项设备驱动:miscdevice *majior=10; * * *************** ...