是不是觉得那些会写脚手架的人都是大佬,而自己只会写写页面,调调样式。脚手架太高级了,自己会用就行了。天天都在接触像npm包管理工具,vue-cli,webpack等等。要是自己也能写一个就太棒了,可以减少很多的重复劳动,想想就激动,那么请往下看。

准备工作:

必备项

  • nodejs
  • github 账号
  • npm 账号

优化项

  • commander模块(命令行参数处理模块)
  • co 模块(异步流程控制模块)
  • co-prompt 模块(消息提示模块)
  • chalk 模块(输出字体颜色模块)

常用的node API

点击了解更多 node.js 中文文档

1. path(路径)

path 模块提供了一些实用工具,用于处理文件和目录的路径。 可以使用以下方式访问它:
const path = require(‘path’);

path.resolve([…paths]) 方法会将路径或路径片段的序列解析为绝对路径。

path.resolve('/目录1/目录2', './目录3');
// 返回: '/目录1/目录2/目录3'

path.resolve('/目录1/目录2', '/目录3/目录4/');
// 返回: '/目录3/目录4'

path.resolve('目录1', '目录2/目录3/', '../目录4/文件.gif');
// 如果当前工作目录是 /目录A/目录B,
// 则返回 '/目录A/目录B/目录1/目录2/目录4/文件.gif'

path.dirname(path) 方法会返回 path 的目录名

path.dirname('/目录1/目录2/目录3');
// 返回: '/目录1/目录2'

path.join([…paths]) 方法会将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径。

path.join('/目录1', '目录2', '目录3/目录4', '目录5', '..');
// 返回: '/目录1/目录2/目录3/目录4'

path.join('目录1', {}, '目录2');
// 抛出 'TypeError: Path must be a string. Received {}'

2.fs(文件系统)

fs 模块可用于与文件系统进行交互(以类似于标准 POSIX 函数的方式)。

要使用此模块:

const fs = require(‘fs’);

fs.mkdir(path[, options], callback) 异步地创建目录。

// 创建 `/目录1/目录2/目录3`,不管 `/目录1` 和 `/目录1/目录2` 是否存在。
fs.mkdir('/目录1/目录2/目录3', { recursive: true }, (err) => {
  if (err) throw err;
});

fs.mkdirSync(path[, options]) 同步地创建目录。

// 创建 `/目录1/目录2/目录3`,不管 `/目录1` 和 `/目录1/目录2` 是否存在。
fs.mkdir('/目录1/目录2/目录3', { recursive: true }, (err) => {
  if (err) throw err;
});

fs.open(path[, flags[, mode]], callback) 异步地打开文件。

fs.open('myfile', 'r', (err, fd) => {
  if (err) {
    throw err;
  }
  readMyData(fd);
});

fs.opendir(path[, options], callback) 异步地打开文件。

fs.open('myfile', 'r', (err, fd) => {
  if (err) {
    throw err;
  }
  readMyData(fd);
});

fs.readFile(path[, options], callback) 异步地读取文件的全部内容。。

fs.readFile('文件名', (err, data) => {
  if (err) throw err;
  console.log(data);
});

fs.writeFile(file, data[, options], callback)
当 file 是文件名时,则异步地写入数据到文件(如果文件已存在,则覆盖文件)
当 file 是文件描述符时,则其行为类似于直接调用 fs.write()(建议使用)

const data = new Uint8Array(Buffer.from('Node.js 中文网'));
fs.writeFile('文件.txt', data, (err) => {
  if (err) throw err;
  console.log('文件已被保存');
});

3.process(进程)

process.argv 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数。 第一个元素是 process.execPath。 如果需要访问 argv[0] 的原始值,则参见 process.argv0。 第二个元素是正被执行的 JavaScript 文件的路径。 其余的元素是任何额外的命令行参数。
例如:

// 执行命令
$ node process-args.js 参数1 参数2 参数3

// 打印 process.argv。
process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`);
});
// 输出
/usr/local/bin/node
/Users/mjr/work/node/process-args.js
参数1
参数2
参数3

process.chdir(directory) process.chdir() 方法变更 Node.js 进程的当前工作目录。

console.log(`Starting directory: ${process.cwd()}`);
try {
  process.chdir('/tmp');
  console.log(`New directory: ${process.cwd()}`);
} catch (err) {
  console.error(`chdir: ${err}`);
}

rocess.exit([code]) 方法以退出状态 code 指示 Node.js 同步地终止进程。 如果省略 code,则使用成功代码 0 或 process.exitCode 的值(如果已设置)退出。

// 使用1为失败退出
process.exit(1);

上面的API基本有异步就会有同步,就不列举过多,接下来就是实战生成文件目录和文件内容,直接用以前的ts项目为例。

const fs = require("fs");
const path = require("path");
const basePath = path.resolve(__dirname, "./");

const dirName = process.argv[2];
if (!dirName) {
  console.log("文件夹名称不能为空!");
  console.log("示例:npm run tep ${capPirName}");
  process.exit(0);
}
const capPirName = camelCase(dirName);

//转驼峰命名
function camelCase(string) {
  string = string.substring(0, 1).toUpperCase() + string.substring(1);
  return string.replace(/-([a-z])/g, function(all, letter) {
    return letter.toUpperCase();
  });
}

/**
 * @msg: vue页面模版
 */
const VueTep = `<template>
  <div class="${dirName}-wrap">
    {{pageInfo.name}}
  </div>
</template>

<script lang="ts" src="./${dirName}.ts"></script>

<style lang="scss" scoped="">
  @import "./${dirName}.scss";
</style>`;

// ts 模版
const tsTep = `import { Component, Vue } from "vue-property-decorator";
import { Getter, Action } from "vuex-class";

@Component({})
export default class ${capPirName} extends Vue {
  @Getter ${dirName}.author
  @Action GET_DATA_ASYN

  private pageInfo: object = {
    name: "${dirName}"
  }

  public created() {
    //
  }
}`;

// scss 模版
const scssTep = `@import "@/assets/css/var";
.${dirName}-wrap {
  width: 100%;
}`;

// interface 模版
const interfaceTep = `// VUEX ${dirName}.State 参数类型
export interface ${capPirName}State {
  ${dirName}Data?: object
}`;

// vuex 模版
const vuexTep = `import { ${capPirName}State } from "../${dirName}.interface";
import { GetterTree, MutationTree, ActionTree } from 'vuex'
const namespace = "${capPirName}";

const state: ${capPirName}State = {
  ${dirName}Data: {}
}

export default {
  name: namespace,
  namespaced: true,
  state
}`;

fs.mkdirSync(`${dirName}`); // mkdir
process.chdir(`${dirName}`); // cd views
fs.writeFileSync(`${dirName}.vue`, VueTep); // vue
fs.writeFileSync(`${dirName}.ts`, tsTep); // ts
fs.writeFileSync(`${dirName}.scss`, scssTep); // scss
fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep); // interface
fs.writeFileSync("module.ts", vuexTep); // vuex

process.exit(0);

复制上面的代码,新建temp.js文件,粘贴上面的代码,在存放的目录下打开命令行工具 执行 node temp.js <文件名>

文件、目录生成没问题了,那要是自己的脚手架有第三方依赖或者还有其他操作怎么办呢?继续往下~

4.child_process(子进程)

创建异步的进程

const exec = require('child_process').exec;

exec("npm i", (err, stdout, stderr) => {
  if (err){
      console.log(err);
      console.warn(new Date(), '命令执行失败');
  } else {
    console.warn(new Date(), '执行成功');
  }
});

创建同步的进程

const execSync = require('child_process').execSync;
execSync("npm i");

到这里的话基本就已经可以自己完成一个脚手架了,可以怎么共享出去呢?npm install 来了。

5.发布到npm registry

npm registry 可以理解成一个包注册管理中心。它管理着全世界的开发者们发布上来的各种插件,同时开发者们可以通过npm install的方式安装所需要的插件。
npm官方registry:http://registry.npmjs.org/
淘宝cnpm:https://registry.npm.taobao.org/

  • 进入要发布到npm的项目根目录,初始化为npm包:
npm init
// 依次按提示填入包名、版本、描述、github地址、关键字、license等

完成之后会生成一个package.json文件,熟悉的东西来了吧,这个就不介绍了。其实这一步应该是开发脚手架项目的第一步,因为我们基本离不开第三方依赖,并且必须有package.json才能安装依赖。

  • 第一次发布包在需要先登录,执行
npm login
// 依次输入自己注册的npm账号、密码,可能需要输入邮箱
  • 登录后就可以发布了
npm publish
// 如果报错可能是包名已经存在

出现 ’+’ 包名 就表示发布成功了
在这里插入图片描述

  • 正常更新一个已经发布的包,版本号+1的情况

先执行

npm version patch  
// 该命令在原来的版本上自动加1,实际上是将package.json文件中的version值修改了。

再执行 npm publish, 需要build的项目还有build一下

  • 更新已经存在的版本,版本号不需要+1的情况
npm unpublish @fx/report@0.5.16
// 先删除版本 再npm publish
  • 删除某个版本,命令同上
  • 删除整个包
npm unpublish 包名 --force

注意:
1、如果发布时报错:‘no_perms Private mode enable, only admin can publish this module:’

表示当前不是原始镜像,可能用的是其他镜像,如淘宝镜像,私有镜像

要切换回原始的npm镜像,命令:npm config set registry https://registry.npmjs.org/,如果用了nrm工具,使用命令:nrm use npm 切换
在这里插入图片描述

2、有些项目需要build后才发布的,直接执行 npm run build可能会报

‘rm’ 不是内部或外部命令,也不是可运行的程序
或批处理文件。
在这里插入图片描述
错误原因:是使用了Linux 下的 rm -rf 命令,换成在git bash执行就可以了。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐