实践:开发一个脚手架

前端工程化Example脚手架
案例

# 一、起步

  1. 创建文件夹bin,里面有cli.js,命令行执行文件
  2. 创建文件夹lib,里面有index.js,具体的业务文件
  3. npm init -y,修改binmain里面的文件路径
  4. npm link,然后在命令行中敲一下项目名称,会自动找到bin目录下的js文件执行,没有报错即可。
  5. cli.js里面写下面的代码,看敲一下全局命令是否会正常执行。
// 顶行写,确定执行环境
#! /usr/bin/env node
console.log('begin')
1
2
3

# 二、cli.js文件做什么

执行全局命令行的时候可以找到这个文件,执行里面的代码,这个时候要知道这个代码要完成什么样的工作。

bin下的文件一般不负责具体的业务实现,只负责操作命令行参数和传递参数给入口文件

# 想办法获取命令执行时携带的参数,同时将参数传给后面的业务逻辑。

方法一:通过node自带process.argv可以获取到命令行参数,但是开发效率低

// 顶行写,确定执行环境
#! /usr/bin/env node

//1.如果获取命令行参数,数组
/**
01 当前环境node.exe 所在路径
02 当前正在执行的脚本
03 我们传入的参数
*/
console.log(process.argv)
1
2
3
4
5
6
7
8
9
10

方法二:第三方工具包(commaneder、CAC)

  1. 安装包npm i commander -D
  2. cli.js里面写
#! /usr/bin/env node

let {program} = require('commander')

// 对参数进行解析
program.parse(process.argv)
1
2
3
4
5
6
  1. 这个时候命令行敲02demo2 --help会有输出
02demo2 --help    
# Usage: cli [options]

# Options:
#   -h, --help  display help for command
1
2
3
4
5

# commander

API过一遍

# parse(解析并输出,参数是process.argv)

在最后必须要写,不然无法在命令行输出值

# version(设置版本号,参数是字符串)

#! /usr/bin/env node

let {program} = require('commander')
let {version} = require('../package.json')
// 设置版本号
program
  // 版本号
  .version(version)
  .parse(process.argv)
1
2
3
4
5
6
7
8
9

命令行输出02demo2 --help02demo2 -V可以获得对应结果

# command(设置命令,名称+描述)

#! /usr/bin/env node

let {program} = require('commander')
let {version} = require('../package.json')
// 针对自定义命令的操作
program
  // 命令行名称
  .command('create')
  // 别名
  .alias('crt')
  // 描述
  .description('初始化一个项目模板')
  // 执行回调
  .action(() => {
    console.log('当前命令执行了')
  })

program.parse(process.argv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这个时候命令行02demo2 create,会输出console的结果

如果多个命令怎么办? 每次都挨个写很费事,最好弄一个对象进行遍历

#! /usr/bin/env node

let {program} = require('commander')
let {version} = require('../package.json')

// 1 提前将需要自定义的命令配置存放起来(create config)
let actionMap = {
  create: {
    alias: 'crt',
    description: '初始化模板项目',
    // 实例代码,用数组存放,尖括号里面是参数,
    examples: ['02demo2 create <projectname>']
  },
  config: {
    alias: 'cfg',
    description: '初始化项目配置',
    examples: [
      '02demo2 config set <k> <v>',
      '02demo2 config get <k>'
    ]
  }
}

// 2 遍历存放自定义命令的数据结构进行()
Reflect.ownKeys(actionMap).forEach((aname) => {
  program
    // 设置命令名称
    .command(aname)
    // 设置别名
    .alias(actionMap[aname].alias)
    // 设置描述
    .description(actionMap[aname].description)
    // 示例并不在这里,需要加到help后面
})


program.on('--help', () => {
  // 在敲help的时候会在后面加上
  console.log('Examples: ')
  Reflect.ownKeys(actionMap).forEach((aname) => {
    actionMap[aname].examples.forEach((item) => {
      // 为了缩进
      console.log(' ' + item)
    })
  })
})

program.version(version).parse(process.argv)
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
42
43
44
45
46
47
48

这个时候命令行敲02demo2 --help会看到后面的配置

02demo2 --help
# Usage: cli [options] [command]

# Options:
#   -V, --version   output the version number
#   -h, --help      display help for command

# Commands:
#   create|crt      初始化模板项目
#   config|cfg      初始化项目配置
#   help [command]  display help for command
# Examples:
#  02demo2 create <projectname>
#  02demo2 config set <k> <v>
#  02demo2 config get <k>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这样做的好处就是,如果输入一个没有配置的命令,不会直接报错,会告诉你参考help

02demo2 abc   
# error: unknown command 'abc'. See 'cli --help'.
1
2

上面说的传递参数是什么?

在执行的时候把参数传递给入口文件,让入口文件接收并处理

// cli.js
let mainFn = require('..') //找到了lib下面的index.js导入

Reflect.ownKeys(actionMap).forEach((aname) => {
  program
    .command(aname)
    .alias(actionMap[aname].alias)
    .description(actionMap[aname].description)
    .action(() => {
      // process.argv.slice(3)是参数的第三个以后的,真正的命令后面跟着的参数
      mainFn(aname, process.argv.slice(3))
    })
    
})

// index.js
module.exports = (aname, args) => {
  console.log(aname, args)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

命令行写02demo2 crt 1 2 3,可以看到输出

02demo2 crt 1 2 3
# create [ '1', '2', '3' ]
1
2

多个命令不应该都在index.js中执行

所以我们要给不同的命令进行分发

//index.js
module.exports = (aname, args) => {
  // 引入模块之后是函数,直接调用并且将参数展开传递
  require('./' + aname)(...args)
}

// create.js
module.exports = (proname) => {
  console.log('创建' + proname)
}

// config.js
module.exports = (proname) => {
  console.log('配置' + proname)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

命令行输入可以看到不同的操作

02demo2 crt p1  
# 创建p1
02demo2 config p2
# 配置p2
1
2
3
4

# 三、进入create.js完成创建逻辑

# 如何下载远端仓库模板

这个是个人github仓库,不能短时间内访问,否则会把账户封掉 (opens new window)

# 目标

  1. 选择目标仓库模板列表 [create-nm, create-vue]
  2. 用户通过交互问题选择目标模板: create-nm
  3. 获取目标仓库模板的列表: [v0.1.0, v0.1.1]
  4. 选择指定的版本
  5. 下载之前处理缓存目录,判断缓存中是否存在 create-nm ,如果存在则直接返回路径进行使用
  6. 如果缓存中不存在,则直接下载,然后返回缓存目录
  7. 判断当前缓存目录里的模板文件中是否存在 que.js 文件,如果存在则证明南要渲染,如果不存在则证明不需要渲染
  8. 如果不需要渲染,我们就直接将文件拷贝到当前项目根下即可

# 思路

  1. 如何下载远端仓库模板
    • 通过官方API 可以获取到某个用户名下的所有仓库列表
    • 我们要从这一坨信息中取出 name
    • 由于这个下载过程是需要消耗时间的, 所以我们需要一些 loading 的展示
    • 当我们拉回这些列表之后,我们还需要提供命令行交互,问问题
  2. 想要通过 API 拉取数据: axios
  3. 想要做 loading 效果: ora
  4. 想要做交互 inquirer
  5. 想要命令行终端里的字体五颜六色: chalk

# 实践

# 1. 下载npm i ora inquirer chalk axios -D
# 2. 获取模板列表
let axios = require('axios')
module.exports = async (proname) => {
  // 1 获取模板列表
  // axios的data就是我们要的数据,是个人的仓库内容,里面的name就是仓库名称列表
  let  { data } = await axios.get('https://api.github.com/users/a1burning/repos')
  let repos = data.map(item => item.name)
  console.log(repos)
}
1
2
3
4
5
6
7
8
# 3. 测试五颜六色,在bin的同级目录下建立chalk/test.js
const { requiredOption } = require("commander");

const chalk = require('chalk')

console.log(chalk.red('我要变红了'))
console.log(chalk.green('我要变绿了'))
console.log(chalk.bgGreen('我背景是绿的'))
console.log(chalk.bgBlue('我背景是蓝的的'))
console.log(chalk.bgBlueBright('我背景是浅蓝的'))
1
2
3
4
5
6
7
8
9

命令行写node chalk/test.js可以看到效果

# 4. 测试问答式交互,在bin的同级目录下建立inquirer/test.js
let inquirer = require('inquirer')
let chalk = require('chalk')

async function fn () {
  let {key} = await inquirer.prompt({
    type: 'input', 
    message: '请输入密码',
    name: 'key',
    default: chalk.red(1233), 
  })
  console.log(key)

  // 使用 caz zce-cli 交互里的一些默认信息都是 zce 
}
fn()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 5. 给代码添加loading
let axios = require('axios')
let inquirer = require('inquirer')
let ora = require('ora')
const url = 'https://api.github.com/users/a1burning/repos'

// 工具方法: 设置loaing动画
const addLoaing = async function (fn) {
  let spinner = ora('拉取开始')
  spinner.start()
  try{
    let ret = await fn()
    spinner.succeed('拉取成功')
    return ret 
  }catch (err) {
    console.log(err)
    spinner.fail('拉取失败')
  }
}

// 工具方法: 获取仓库列表
const fetchRepoList = async function () {
  let {data} = await axios.get(url)
  let repos = data.map(item => item.name)
  return repos
}


module.exports = async (proname) => {
  // 1 获取模板列表
  let repos = await addLoaing(fetchRepoList)
  console.log(repos)

  // 2 交互问题
  let { tmpname } = await inquirer.prompt({
    type: 'list',
    name: 'tmpname',
    message: '请选择目标仓库标题',
    choices: repos
  })
  console.log(tmpname)
}
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

loading成功之后会看到这个,选择仓库标题

image

# 6. 添加选择tag的部分
let axios = require('axios')
let inquirer = require('inquirer')
let ora = require('ora')
const url = 'https://api.github.com/users/a1burning/repos'
const addLoaing = async function (fn) {
  let spinner = ora('拉取开始')
  spinner.start()
  try{
    let ret = await fn()
    spinner.succeed('拉取成功')
    return ret 
  }catch (err) {
    console.log(err)
    spinner.fail('拉取失败')
  }
}

// 工具方法: 获取仓库列表
const fetchRepoList = async function () {
  let {data} = await axios.get(url)
  let repos = data.map(item => item.name)
  return repos
}

// 工具方法: 获取 tags 列表
const fetchTagList = async function () {
  let {data} = await axios.get('https://api.github.com/repos/zcegg/create-nm/tags')
  let repos = data.map(item => item.name)
  return repos
}


module.exports = async (proname) => {
  // 1 获取模板列表
  let repos = await addLoaing(fetchRepoList)
  console.log(repos)

  // 2 交互问题
  let { tmpname } = await inquirer.prompt({
    type: 'list',
    name: 'tmpname',
    message: '请选择目标仓库标题',
    choices: repos
  })
  console.log(tmpname)

  // 3 拉取 tags, 如果有内容 tags就是一个有值的数组,如果没有tags 那它就是一个空数组
  let tags = await addLoaing(fetchTagList)
  console.log(tags)

  // 4 依据当前拉取回来的 tags 分支进行处理 ([v1,v2......] [])
  if(tags.length) {
    // 当代码运行到这里就说明存在多个 tags 
    let {tagv} = await inquirer.prompt({
      type: 'list', 
      name: 'tagv',
      message: "请选择目标版本",
      choices: tags
    })
    console.log(tagv)
  } else {
    // 当代码运行到这里就说明当前仓库是不存在多个 tag版本
    let {isDownload} = await inquirer.prompt({
      type: 'confirm', 
      name: 'isDownload',
      message: "当前不存在多个tag是否直接下载"
    })
    console.log(isDownload)// 布尔值
    if (isDownload) {
      console.log('选择yes直接执行下载操作')
    } else {
      return
    }
  }
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

命令行写02demo2 crt p1,执行

02demo2 crt a
# √ 拉取成功
# [ 'csf-gulp-build', 'demofiles', 'generator-csfdemo', 'lottieDemo' ]
# ? 请选择目标仓库标题 csf-gulp-build
# csf-gulp-build
# √ 拉取成功
# [ 'v0.1.1', 'v0.1.0' ]
# ? 请选择目标版本 v0.1.1
# v0.1.1
1
2
3
4
5
6
7
8
9
# 7. 修改上面tags的部分,改成项目名称选择的,要用到柯里化

因为改动点二的地方要把tags的项目名称传进来,不能写成死的。那么就需要调用fetchTagList的时候把参数传进来,但是怎么把参数传进来呢?

不能直接调用fetchTagList传参,就只能包装一下addLoading,让其返回一个函数。这个就用到了函数的高级用法 —— 柯里化

// 改动点一:调用的时候把要执行的函数名传参,并且返回一个函数,在返回的函数中调用把参数传递进去,然后里面就会把参数传到之前要执行的函数中。
const addLoaing = function (fn) {
  return async function (...args) {
    let spinner = ora('拉取开始')
    spinner.start()
    try{
      // 这边调用的时候拿到参数
      let ret = await fn(...args)
      spinner.succeed('拉取成功')
      return ret 
    }catch (err) {
      console.log(err)
      spinner.fail('拉取失败')
    }
  }
}

// 改动点二:把项目名称弄成传参进来的
const fetchTagList = async function (reponame) {
  let {data} = await axios.get(`https://api.github.com/repos/zcegg/${reponame}/tags`)
  let repos = data.map(item => item.name)
  return repos
}


module.exports = async (proname) => {
  // 改动点三:这里不需要参数传递直接调用不用传参
  let repos = await addLoaing(fetchRepoList)()
  console.log(repos)
  let { tmpname } = await inquirer.prompt({
    type: 'list',
    name: 'tmpname',
    message: '请选择目标仓库标题',
    choices: repos
  })
  console.log(tmpname)

  // 改动点四:这里调用addLoading并且把fetchTagList的参数名传递进去,返回一个函数,调用函数把项目名称传递进去,在函数内部实现的是fetchTagList(tmpname)
  let tags = await addLoaing(fetchTagList)(tmpname)
  console.log(tags)
  // 剩下的忽略
  ......
}
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
42
43
# 8. 完成 git 仓库下载操作
  • 安装npm i download-git-repo -D
  • 添加缓存
    • 好处一:下载的东西里面有语言标记我们可以进行渲染,渲染之后再放到项目目录下
    • 好处二:下一次下载的时候相同的东西没有必要再去远程下载
  • 因为这个插件不支持async-await所以需要转化,所以需要进行转化
// 可以让不支持async-await的变得支持
let { promisify } = require('util')
let downloadFn= require('download-git-repo')
// 转化之后可以使用了
downloadFn = promisify(downloadFn)
1
2
3
4
5

小知识

如何定义临时目录

// 一堆信息
console.log(process.env)
// 什么操作系统
console.log(process.platform) // win32
// 使用 USERPROFILE 获取管理员路径目录
console.log(process.env['USERPROFILE']) // C:\Users\韵七七

if (process.platform === 'win32') {
 console.log(process.env['USERPROFILE'])
} else {
 console.log(process.env['HOME'])
}
// 上面的等同于
console.log(process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'])
// 当前目录下的临时文件目录
console.log(`${process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME']}/.tmp`)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

判断文件是否存在

let fs = require('fs')
let path = require('path')

console.log(__dirname) // E:\professer\lagou\02-01-study-materials\02demo2

let a = fs.existsSync(path.join(__dirname, 'test.js1'))

console.log(a) // false
1
2
3
4
5
6
7
8
  • 定义工具方法
const path = require('path')
let fs = require('fs')

// 工具方法: 自定义函数完成 git 仓库下载操作
const downloadRepo = async function (repo, tag) {
  // 1 定义缓存目录
  let cacheDir = `${process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME']}/.tmp`
  // 2 处理 download-git-repo 导出的函数的调用规则 downloadFn(zcegg/create-nm#tagv)
  let api = `a1burning/${repo}`
  // 如果有tag就添加
  if (tag) {
    api += `#${tag}`
  }
  // 3 自定义一个模板下载后的输出目录
  let dest = path.resolve(cacheDir, repo)
  let flag = fs.existsSync(dest)
  // 4 执行下载操作
  if (!flag) {
    let spinner = ora('开始下载')
    spinner.start()
    await downloadFn(api, dest)
    spinner.succeed('模板下载成功')
  }else{
     console.log("已经下载完毕,从缓存中获取")
  }
  // 5 将设置好的缓存目录返回
  return dest
}

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
  • 执行操作
module.exports = async (proname) => {
  ... ...
  if(tags.length) {
    let {tagv} = await inquirer.prompt(...)
    // 修改点一
    // 依据选择的模板名称和仓库版本号完成具体的下载操作
    let dest = await downloadRepo(tmpname, tagv)
    console.log(dest)
  } else {
    let {isDownload} = await inquirer.prompt(...)
    if (isDownload) {
      console.log('选择yes直接执行下载操作')
      // 修改点二
      // 依据选择的模板名称完成具体的下载操作
      let dest = await downloadRepo(tmpname)
      console.log(dest)
    } else {
      return
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 命令行运行02demo2 create p1,下载可以看到目录C:\Users\韵七七\.tmp下面有项目文件下载下来,再次运行可以看到下载的时候从本地缓存获取。

需要考虑的事情:

  • 代码优化
  • 在缓存文件的目录下面,应该用版本再加一层
# 9. 将临时目录的文件进行渲染到项目目录中
  • 安装包npm i ncp -D :将当前的资源拷贝到目录下的第三方包
  • 二种情况
    • 一种是项目初始化过程中需要用户动态提供数据
    • 一种是项目中不需要渲染动态数据,直接拷贝即可
  • 如何判断需不需要渲染动态数据,可以用que.js来判断,如果有就说明需要渲染,如果没有就不需要渲染

9.1 不需要渲染数据

let ncp = require('ncp')

if (fs.existsSync(path.join(dest, 'que.js'))) {
    console.log('当前是需要渲染数据的')
  } else {
    // 不需要渲染时直接将缓存里的内容拷贝至当前项目下
    console.log('当前是不需要渲染数据')
    ncp(dest, proname)
  }
1
2
3
4
5
6
7
8
9

9.2 渲染数据之给模板传递数据

  • 安装第三方包npm i metalsmith consolidate ejs -D

let Metalsmith = require('metalsmith')
// 多模板集合,里面有ejs可以导出来使用
let {render} = require('consolidate').ejs

if (fs.existsSync(path.join(dest, 'que.js'))) {
    console.log('当前是需要渲染数据的')
    // 安装第三方包 metalsmith 
    // 安装第三方包 consolidate
    // 当前是需要渲染数据,里面可以递归拿到所有文件
    await new Promise((resolve, reject) => {
      // 数据渲染过程,传参数不传会报错,传当前目录后面也用不到
      Metalsmith(__dirname)
        // 资源目录,会把东西遍历一遍
        .source(dest)
        // 输出目录,
        .destination(path.resolve(proname))
        // 中间如何处理
        // done 这个use的事情做完了就去下一个use
        .use((files, metal, done) => {
          console.log(files)
          done()
        })
        .use((files, metal, done) => {
          done()
        })
        // 构建
        .build((err) => {
          // 错误处理
          if (err) {
            reject()
          } else {
            resolve()
          }
        })
    })
  } else {
    // 不需要渲染时直接将缓存里的内容拷贝至当前项目下
    console.log('当前是不需要渲染数据')
    ncp(dest, proname)
  }
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

我们可以看到输出的文件结构(敲黑板,记住这里,下面要用

'package-lock.json': {
  contents: <Buffer 7b 0a 20 20 22 6e 61 6d 65 22 3a 20 22 68 65 6c 6c 6f 31 22 2c 0a 20 20 22 76 65 72 73 69 6f 6e 22 3a 20 22 30 2e 31 2e 30 22 2c 0a 20 20 22 6c 6f 63 ... 482073 more bytes>,
  mode: '0666',
  stats: Stats {
    dev: 2992857361,
    mode: 33206,
    nlink: 1,
    uid: 0,
    gid: 0,
    rdev: 0,
    blksize: 4096,
    ino: 11821949021890680,
    size: 482123,
    blocks: 944,
    atimeMs: 1600577005804,
    mtimeMs: 1600293756000,
    ctimeMs: 1600577005839.1682,
    birthtimeMs: 1600577005835.1768,
    atime: 2020-09-20T04:43:25.804Z,
    mtime: 2020-09-16T22:02:36.000Z,
    ctime: 2020-09-20T04:43:25.839Z,
    birthtime: 2020-09-20T04:43:25.835Z
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

9.3 渲染数据之把数据替换模板文件

if (fs.existsSync(path.join(dest, 'que.js'))) {
    console.log('当前是需要渲染数据的')
    await new Promise((resolve, reject) => {
      Metalsmith(__dirname)
        .source(dest)
        .destination(path.resolve(proname))
        // 中间如何处理
        /**
         * files 就是需要渲染的模板目录下的所有类型的文件
         * metal 当前use定义的变量,下一个use要使用,需要用这个容器来保存
         * done 这个use的事情做完了就去下一个use
         */
        // 渲染拿到数据
        .use(async (files, metal, done) => {
          // 加载当前存放问题的模块 que.js
          let quesArr = require(path.join(dest, 'que.js'))
          // 依据问题数据,来自定义交互的问题,传入数组即可
          let answers = await inquirer.prompt(quesArr)
          // 当前 answers 是传递过来的参数,我们需要在下一个 use 中进行使用
          // 利用 metal.metadata() 来保存所有的数据,交给下一个use 进行命名用即可
          let meta = metal.metadata()
          Object.assign(meta, answers)

          // 这步操作完成之后,那么 que.js 文件就没有用了,不需要拷贝至项目的目录
          // 我们删除的是读取到内存中的键值,如果删了只是删的是键,文件还在,只是文件不会被拷贝
          delete files['que.js']
          done()
        })
        // 找对应文件进行拷贝
        .use((files, metal, done) => {
          // 获取上一个 use 中拿到的用户填写的数据
          let data = metal.metadata()
          /**
           * 找到那些需要渲染数据的具体文件(.js or .json),找到之后将它们里的内容的Buffer形式转为字符串方式
           * 转为字符串之后,接下来就可以针对于字符串进行替换实现渲染 
           */
          Reflect.ownKeys(files).forEach(async (file) => {
            // 文件名
            if (file.includes('js') || file.includes('json')) {
              // 把文件名获取出来转换成字符串
              let content = files[file].contents.toString()
              // 如果里面有ejs的特殊字符,
              if (content.includes("<%")) {
                // 需要用ejs把数据进行替换
                content = await render(content, data)
                // 把字符串转换成buffer之后放入文件中
                files[file].contents = Buffer.from(content)
              }
            }
          })
          done()
        })
        // 构建
        .build((err) => {
          // 错误处理
          if (err) {
            reject()
          } else {
            resolve()
          }
        })
    })
  } else {
    console.log('当前是不需要渲染数据')
    ncp(dest, proname)
  }
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

命令行输入可以看到本地有一个新的项目p3文件夹产生

02demo2 create p3
# √ 拉取成功
# [ 'create-nm', 'create-vue' ]
# ? 请选择目标仓库标题 create-vue
# create-vue
# √ 拉取成功
# []
# ? 当前不存在多个tag是否直接下载 Yes
# true
# 选择yes直接执行下载操作
# 已经下载完毕,从缓存中获取
# C:\Users\韵七七\.tmp\create-vue
# 当前是需要渲染数据的
# ? 是否为私有仓库? Yes
# ? author? csf
# ? description 
# ? license? MIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 10. 包名的一些配置选项(留下作业)
  • consimconfig可以在本地生成一个配置文件,可以弄成动态配置,用户可以手动改配置
  • 如果手动配置觉得low,就用yyl config的get set,通过文件读写动态修改内容
更新时间: 2021-12-18 20:56