1. 脚手架设计和框架搭建
通用组件库
commander
import { program, Command } from 'commander'
export function commandTest(commandName, version) {
program
.name(commandName)
.usage(`<command> [options]`)
.version(version)
.option('-d, --debug', '是否开启调试模式', false)
.option('-e, --envName <char>', '环境变量')
.createHelp()
program
.command('clone <source> [destination]')
.description('克隆')
.action((source, destination) => {
console.log('clone...', source, destination)
})
// 脚手架参数,监听所有命令劫持,当该命令没有注册时触发
program
.arguments('<cmd> [options]')
.description('test command')
.action((cmd, env) => {
console.log(cmd, env)
})
// 注册子命令
const service = new Command('service');
service.command('start [port]')
.description('启动服务')
.action((port) => {
console.log('start service ', port)
})
program.addCommand(service)
program.parse(process.argv)
}
- 命令更换,实现多个脚手架串行互相调用
// 当添加第二个参数后,组合成新的命令:vue-cli-install
program
.command('install [name]', 'install package', {
// 更换为vue-install命令
executableFile: 'vue-install',
// 默认命令
isDefault: true,
// 隐藏
hidden: true,
})
.alias('i')
- 自定义help信息
// help info
program.helpInformation()
program.helpInformation = () => ''
program.on('--help', () => {
console.log('your help info')
})
// 解析debug参数
program.on('option:debug', () => {
console.log('开启了debug')
})
- 注册子命令
const service = new Command('service');
service.command('start [port]')
.description('启动服务')
.action((port) => {
console.log('start service ', port)
})
program.addCommand(service)
- 监听未知命令
program.on('command:*', function(obj) {
console.log(obj)
})
console.log('可用命令', program.commands.map(cmd => cmd.name()))
url-jion
用于拼接url
npmlog 打印日志
'use strict';
// const log = require('npmlog')
import log from 'npmlog'
// 日志级别
log.level = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : 'info'
log.addLevel("success", 2000, {fg: "green", bold: true})
log.addLevel("error", 5000, {fg: "red", bold: true})
// 前缀
log.heading = 'vue-cli'
log.headingStyle = { fg: 'white', bg: 'black' }
export default {
success(msg) {
log.success(msg)
},
error(msg) {
log.error(msg)
},
info(msg) {
log.info(msg)
},
debug(msg) {
log.verbose(msg)
},
notice(msg) {
log.notice(msg)
},
};
fs-extra 文件操作
semver 版本比对
import semver from 'semver'
if (!semver.gte(currentVersion, lowestVersion)) {
throw new Error(`vue cli 需要安装 v${lowestVersion}以上版本的node.js`)
}
colors 打印颜色的文本
log.error(colors.red(e.message))
user-home 快速拿到主目录
import userHome from "user-home"
console.log(userHome)
dotenv 获取环境变量
import dotenv from 'dotenv'
const env = dotenv.config({
path: path.resolve(userHome, ".env")
})
console.log(env)
console.log(process.env.CLI_HOME)
root-check root检查和角色降级
import rootCheck from 'root-check'
rootCheck()
minimist
parse argument options
脚手架简介
脚手架本质上是一个操作系统的客户端,通过命令行执行,如:
vue create vue-test-app
上面的命令有三个部分组成:
- 主命令
- command:create
- param:参数
vue create vue-test-app --force --> --force true
vue create vue-test-app --force -r https://taobao.xxx
--force
为option
-r
也是option,为简称
vue
命令:
$ ll vue
lrwxr-xr-x 1 zhx admin 39B 1 16 2022 vue -> ../lib/node_modules/@vue/cli/bin/vue.js
是一个超链接,执行了../lib/node_modules/@vue/cli/bin/vue.js, 该目录为node的全局依赖,所有通过npm i -g
全局安装的依赖都会在这里。如:
ll ../lib/node_modules/
total 0
drwxr-xr-x 3 zhx admin 96B 3 2 2022 @jh4j-cloud
drwxr-xr-x 4 zhx admin 128B 1 16 2022 @vue
drwxr-xr-x 9 zhx admin 288B 4 1 2022 lerna
drwxr-xr-x 10 zhx admin 320B 1 15 2022 nrm
drwxr-xr-x 21 zhx admin 672B 2 20 2022 pm2
drwxr-xr-x 7 zhx admin 224B 7 27 2022 pnpm
- 在终端输入
vue create vue-test-app
- 终端解析
vue
命令 - 在环境变量中找到vue命令
- 终端根据link找到实际文件
vue.js
- 终端利用node执行
vue.js
- vue.js解析command和param
- 执行命令
脚手架实现原理
- 为什么通过npm全局安装了
@vue/cli
后,node下的bin目录就多了一个可执行文件链接到 项目的bin/vue.js
上?
观察项目的package.json
"bin": {
"vue": "bin/vue.js"
}
- 执行
vue
命令的时候,vue指向了js文件,为什么可以直接执行?
查看vue.js的第一行:
#!/usr/bin/env node # 标识在环境变量中寻找node
#!/xxx/node # 表示使用node命令执行该文件
到usr/bin/env环境变量中寻找node,然后通过node执行该文件
创建软链接
ln -s /xxx/xxx.js vue
unlink /xx/xx.js
脚手架本身是一个可执行文件 js, 就是操作系统的客户端,依赖node命令执行的一个客户端
脚手架开发流程
- 创建npm项目
mkdir lc-cli
cd lc-cli
npm init -y
- package.json
"bin": {
"lc-cli": "bin/index.js"
},
- touch bin/index.js
#!/usr/bin/env node
lc-cli
env: node\r: No such file or directory
vim设置换行符::set ff=unix
本地调试项目 npm link
# 将当前项目链接到到全局node_modules下作为一个库文件,并解析bin配置创建可执行文件
$ npm link
# e.g
$ npm link
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN @vue-lc-cli/cli@0.0.0 No repository field.
removed 1 package and audited 1 package in 2.497s
found 0 vulnerabilities
/opt/homebrew/bin/vue-cli -> /opt/homebrew/lib/node_modules/@vue-lc-cli/cli/bin/index.js
/opt/homebrew/lib/node_modules/@vue-lc-cli/cli -> /Users/zhx/study/web架构/cli/vue-lc-cli/core/cli
# 将当前项目中node_modules下的库文件链接到node全局node_modules下的库文件
npm link xx
# 解除链接 全局node库链接到当前项项目
npm unlink
# 取消连接 当前项目链接到全局node库
npm unlink xx
# 删除本地安装
npm remove -g xx
lerna
lerna create
创建包
lerna create core
lerna add
安装依赖
# 给每个模块安装依赖
lerna add vue
# 指定模块
lerna add vue --scope=@vue-lowcode/core
lerna link
将所有相互依赖的包符号链接在一起,一般使用bootstrap命令
lerna link
lerna bootstrap
将本地包链接在一起并安装剩余的包依赖项
lerna bootstrap
lerna exec
# 在每个包下执行命令
lerna exec -- rm -rf node_modules
lerna exec -- rm -rf node_modules --scope=@vue-lowcode/core
安装依赖
check
require加载资源类型原理
- js 寻找module.exports/exports
- json JSON.parse()
require("xxx.txt") 对于非js、非json文件默认当成js文件解析
通过api获取npm包的信息
这里研究的是私有仓库,是通过 Verdaccio 搭建的。
- 命令
npm view @someScope/packageName versions --json
- 接口
https://private.registry.com/@someScope/packageName
es module
node支持es module的方式
- 通过webpack,webpack使用commonjs语法
webpack.config.js
module.exports = {
entry: "",
output: {
path: '',
filename: ''
},
target: 'node'
}
webpack的构建环境target默认为'web',可以修改为node
babel为了兼容较低版本的node环境,如:低版本的node不认识 Promise,await等es7引入的语法
npm i -D babel-loader @babel/core @babel/preset-env
# 防止一个报错,regenerator not defined
npm i -D @babel/plugin-transform-runtime
# babel-runtime-corejs3
npm i -D @babel/babel-runtime-corejs3
在webpack.config.js中的module属性,引入loader
{
module: {
rules: [{
// js结尾的文件
test: /\.js$/,
exclude: /(node_modules|disct)/,
use: {
loader: 'babel-loader',
options: {
// 转换,直接把babel的配置文件省略在这里配置
presets: ['@babel/preset-env'],
plugins: [
['@babel/plugin-transform-runtime'],
{
corejs: 3,
regenerator: true,
useESModules: true,
helpers: true
}
]
}
}
}]
}
}
- type="module"