vue-cli

在vue的开发的过程中,经常会使用到vue-cli脚手架工具去生成一个项目。在终端运行命令vue create hello-world后,就会有许多自动的脚本运行。

  • 为什么会这样运行呢?
  • 我们自己是否也能写一个脚手架工具?
    带着这样的疑问,我们先来看看vue-cli。

解读vue-cli

首先我们可以来到vue-cli的安装目录:
mac用户来到路径:/usr/local/lib/node_modules 可以看到(windows可以自行到全局安装的目录下查看)

此时用我们的编辑器 打开@vue文件:

lib内放的是具体各种配置和各种类,对于我们来说,这个目录内的就是所谓的业务逻辑。
bin内放的就是脚本命令的入口,调用lib的入口,入口在package.json内红框定义。我们姑且先放下业务逻辑,来看看这个入口文件。

现在,我们先放下所有的疑虑,我们打开bin/vue.js。我们可以看到以下内容:

看完之后有什么感觉?咦?怎么跟终端内输出的好像?没错,就是这样,这就是我们使用vue-cli的时候具体的命令。我们去终端输入vue

发现了吗,终端内的具体命令全在vue.js内定义过了。

program.command('create <app-name>').description('create a new project powered by vue-cli-service')
program.command('add <plugin> [pluginOptions]').description('install a plugin and invoke its generator in an already created project')
program.command('invoke <plugin> [pluginOptions]')

好了,剩下代码有兴趣的可以自行打开对应目录读下去,可以学习人家优秀的设计思想。对于本文来说,剩下的许多东西都是业务代码了。我们开头的疑问为什么会这样运行呢?已经了解了一个大概了。我们现在来看看我们自己是否也能写一个脚手架工具?,答案是肯定的。开始动手吧。

手写一个自己的脚手架

先来看看可能需要用到哪些npm的包:

  • commander:参数解析
  • inquirer:交互式命令行工具,有他就可以实现命令行的选择功能
  • chalk:输出文本颜色,为了美丽~
    后续可能随着模块的增加,会出现更多需要的包

1. 创建项目

npm init -y # 初始化package.json

2. 创建文件目录

  • 在package.json内添加“bin”
  • bin下的文件没有格式,且第一行必须是#! /usr/bin/env node

3. 链接包到全局

npm link # // 取消链接 npm unlink

有时候可能需要在上面命令后面拼接 --force,mac权限问题记得前面sudo
可以去目录:/usr/local/lib/node_modules 查看,发现我们多了一个,同时在这时候终端输入一下试试,我们在package.json下叫的name叫superman-cli,所以我们的命令就是叫superman-cli:

好了,基础配置初始化的工作全部结束了!

4. 第一个命令

首先安装包commander

npm install commander --save

在目录bin/superman内

#! /usr/bin/env node// console.log(1)
const program = require('commander')program.version(`Version is ${require('../package.json').version}`).description('从0开始 手写脚手架').usage('<command> [options]')program.parse(process.argv)

测试有效!

然后我们在前面Version命令下加入代码:

program.command('create <app-name>').description('create a new project').option('-f, --force', 'Overwrite target directory if it exists').option('-c, --clone', 'Use git clone when fetching remote preset').action((name, cmd) => {console.log('name', name)console.log('cmd', cmd)})

仔细对比我们的代码合终端的输出,我们就可以看到我们写的很多东西都生效了。接下来我们就优化一下.action下的参数,毕竟一大堆也不好处理:

program.command('create <app-name>').description('create a new project').option('-f, --force', 'Overwrite target directory if it exists').option('-c, --clone', 'Use git clone when fetching remote preset').action((name, cmd) => {const options = cleanArgs(cmd)console.log(options)})
function camelize (str) {return str.replace(/-(w)/g, (_, c) => c ? c.toUpperCase() : '')
}
function cleanArgs (cmd) {const args = {}// console.log(cmd)cmd.options.forEach(o => {const key = camelize(o.long.replace(/^--/, ''))// console.log(key)// console.log(cmd[key])// console.log(typeof cmd[key])if (typeof cmd[key] !== 'function' && typeof cmd[key] !== 'undefined') {args[key] = cmd[key]}})return args
}

具体不懂的也可以像我注释的console.log一样,慢慢看就明白了

image

我们第一个命令已经完成一大半了,接下来就是我们这个create命令具体干什么事情。(在这个文件里,我们只管命令,就像vue-cli一样,这也是我们需要学习的地方,模块如何去处理)

// 在上面.action内补充一行代码.action((name, cmd) => {const options = cleanArgs(cmd)console.log(options)require('../lib/create')(name, options)})

同时去lib下创建文件create.js

const path = require('path')
// const fs = require('fs-extra')
async function create (projectName, options) {console.log(projectName, options)const cwd = process.cwd(); // 获取当前命令执行时的工作目录const targetDir = path.join(cwd,projectName); // 目标目录console.log(targetDir)
}module.exports = (...args) => {return create(...args)
}

继续执行superman-cli create hello -f,我们可以得到,force:true,如果新建的话,将来的目录会是/Users/chenjing/hello

接下来我们尝试创建目录hello,不过我们需要考虑几个问题:

  • 是否已经存在目录hello了?(使用fs-extra包)
  • 若存在是要删除覆盖还是停止操作?(这里就需要用到插件inquirer啦,进行选择)
npm install fs-extra --save
npm install inquirer --save

直接上代码:

const path = require('path')
const fsextra = require('fs-extra')
const fs = require('fs')
const Inquirer = require('inquirer')
async function create (projectName, options) {console.log(projectName, options)const cwd = process.cwd(); // 获取当前命令执行时的工作目录const targetDir = path.join(cwd,projectName); // 目标目录console.log(targetDir)if (fsextra.existsSync(targetDir)) {if (options.force) {// 如果强制创建 ,删除已有的await fsextra.remove(targetDir);console.log('删除成功')createDir(projectName)} else {let { action } = await Inquirer.prompt([{name: 'action',type: 'list',message: 'Target directory already exists Pick an action:',choices: [{name:'Overwrite',value:'overwrite'},{name:'Cancel',value:false}]}])if (!action) {console.log('取消操作')return} else if (action === 'overwrite') {console.log(`rnRemoving....`);await fsextra.remove(targetDir)console.log('删除成功')createDir(projectName)}}} else {createDir(projectName)}
}
function createDir (projectName) {fs.mkdir(`./${projectName}`, function (err) {if (err) {console.log('创建失败')} else {console.log('创建成功')}})
}module.exports = (...args) => {return create(...args)
}

看效果,先来的一个空目录

superman-cli create hello
superman-cli create hello // 再次
superman-cli create hello -f // 覆盖

4. 小结

本篇的源码github地址

并不是说我们手写脚手架就到此结束了,只是要完整实现一个功能不是一两篇文章可以搞定的。不过我相信写到这里,动手能力强的一定也能体验一把手动模仿vue-cli的爽了。至于能写出什么牛C的脚手架,真的就是个人需求和业务代码堆加。当然可以发散思维后续还可以做许多许多事情。强烈建议阅读vue-cli。或者其他脚手架的源码,都在目录:/usr/local/lib/node_modules 下面,看源码真的是学习最直接的方法了,甚至copy人家的代码到自己的cli内执行。其乐无穷

作者:超人陈立青
链接:https://www.jianshu.com/p/75de24392de8
来源:简书

vue 使用fs_模仿vue-cli,手写一个脚手架相关推荐

  1. vue @click 赋值_vue 手写一个时间选择器

    vue 手写一个时间选择器 最近研究了 DatePicker 的实现原理后做了一个 vue 的 DatePicker 组件,今天带大家一步一步实现 DatePicker 的 vue 组件. 原理 Da ...

  2. 深入Vue底层,手写一个vuex

    深入底层,手把手教你写一个Vuex 1. Vuex是什么?什么场景下使用? 2. Vuex的基本使用 3. 手写一个vuex 1. Vuex是什么?什么场景下使用? Vuex是vue的一个插件,叫做状 ...

  3. 基于vue手写一个分屏器,通过鼠标控制屏幕宽度。

    基于vue手写一个分屏器,通过鼠标控制屏幕宽度. 先来看看实现效果: QQ录屏20220403095856 下面是实现代码: <template><section class=&qu ...

  4. vue手写一个计算器

    计算器大家都不陌生 有计算器机器 有手机计算器 网页计算器! 那么好 今天我来给大家手写一个计算器 啥都不说上操作 请听题:vue手写计算器 一个个小方块拼成一个计算器 绿色比较好 可以缓解视力哦 i ...

  5. vue中手写一个放大镜功能

    vue中手写一个放大镜功能 有的时候需要对图片进行放大,类似于电商的商品放大功能,于是在这个想法上写了一个放大镜的功能,并且在放大镜的基础上新添加了一些小功能,下面开始吧! 放大镜是封装的组件的形式, ...

  6. Vue手写一个日历组件

    工作中遇到一个需求是根据日历查看某一天/某一周/某一月的睡眠报告,但是找了好多日历组件都不是很符合需求,只好自己手写一个日历组件,顺便记录一下. 先来看看设计图是什么样式, 跟其他日历有点不一样,这个 ...

  7. 手写一个自己的 cli 并发布到 npm 上

    手写一个自己的 cli 并发布到 npm 上 简介:大家平时肯定用过 vue-cli 或者 create-react-app,只需要敲简单的命令行,就可以生成一个完整的项目,非常好用.由于本人所在公司 ...

  8. 用 Node.js 手写一个 DNS 服务器

    DNS 是实现域名到 IP 转换的网络协议,当访问网页的时候,浏览器首先会通过 DNS 协议把域名转换为 IP,然后再向这个 IP 发送 HTTP 请求. DNS 是我们整天在用的协议,不知道大家是否 ...

  9. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

最新文章

  1. TensorFlow 2.0发布在即,高级API变化抢先看
  2. 用友致远协同OA办公软件可以给外驻领导带来什么好处?
  3. 设置联想电脑双屏显示(备用待查)
  4. Unity使用协程技术制作倒计时器
  5. 推荐 6 个不错的JavaScript动画库
  6. mr InputFormat类
  7. unity资源面数规范
  8. 小学计算机集体备课,信息技术集体备课总结
  9. 【Ansys Workbench】—响应面优化操作步骤
  10. windows 搭建kms服务器激活_自建KMS激活服务器的技巧
  11. 2021概率论与数理统计辅导讲义-余丙森
  12. 小白的python学习实录 基础篇(八)面向对象
  13. vnc-4.0-winsrc版本之winvnc工程分析_源码研究
  14. 基于DRM框架的HDMI热插拔流程分析
  15. tomcat6\webapps\manager does not exist or is not a readable directory
  16. java拼图_java拼图
  17. 快速提取Word文档中的所有图片
  18. 自然语言处理(七):AG_NEWS新闻分类任务(TORCHTEXT)
  19. 大白话说期权——除了买涨买跌,我们还能怎么交易?二元期权又是什么鬼?
  20. 固态硬盘的S.M.A.R.T详解

热门文章

  1. Python基础教程:类的特殊成员及高级特性
  2. Python 序列类型支持拆包操作
  3. python中的内部函数和闭包函数
  4. Python中sort与sorted函数
  5. rediscluster全局数据_redis迁移,从redis cluster集群迁移数据到另外一个redis cluster集群...
  6. 计算机ftp无法找到启动路径,filezilla出现路径错误导致无法启动怎么办?filezilla无法启动的解决方法...
  7. linux ubuntu pkg-config工具的使用(源代码编译库接口查询工具)
  8. 【深度学习的数学】2×3×1层带sigmoid激活函数的神经网络感知机对三角形平面的分类训练预测(绘制出模型结果三维图展示效果)(梯度下降法+最小二乘法+激活函数sigmoid+误差反向传播法)
  9. CSDN Markdown 博客如何设置插入代码背景颜色(设置成黑色)?
  10. python 使用raise语句主动抛出异常(Exception)、将异常抛出给上一级