关于cli:文盘Rust起手式CLI程序-京东云技术团队

技术的学习从不会到会的过程是最有意思的,也是领会最多的。一旦纯熟了,常识变成了常识,可能就失去了记录学习过程的最佳时机。 在我看来学习一门计算机语言和学习人类语言有很多共通之处。咱们学习人类语言是从单个的词开始,而后是简略句子,通过一直的与别人交互练习把握语法和语言习惯。当纯熟到肯定水平就能够表达思想。计算的语言也差不多,相熟关键词,根本逻辑,规范库,写利用。只是沟通的对象是机器而已。 既然是学就不能在开始搞的太难。学习原本就是个艰辛的差事。上来就干特地简单的事件往往会保持不上来。天下难事必做于易,从简入繁,从易到难,方为邪道。 先聊聊最简略的CLI(Command Line Interface)程序。其实咱们每学习一门语言的 hello world 程序就是CLI,只是没那么多交互而已。 做命令行程序最繁琐的事件是解决交互。交互大体分两种。一种是咱们最相熟shell下的交互模式,每次一个命令,配合参数实现一次解决返回一组后果。这种模式解决起来比拟容易Rust也有相当优良的第三方lib (clap)。第二种是畛域交互,就像我是应用MySql或者redis的客户端程序。这种程序能够玩儿的货色就比拟多了像如何实现交互,如何来做子命令的提醒。这些货色 clap 并没有提供,须要咱们本人来实现。 interactcli-rs是我在工作过程中做的一个交互模式命令行脚手架。实现了一些罕用性能。 上面咱们来看看如何通过几个步骤疾速的实现一个性能绝对齐全的CLI程序。和做饭一样,可能疾速取得成就感的形式是找半成品间接下锅炒一盘:)。 上面咱们具体看看,如何通过interactcli-rs实现一个功能齐全的命令行程序 来点感性认识先把我的项目clone下来运行个例子 clone 我的项目 git clone https://github.com/jiashiwen/interactcli-rs.gitcd interactcli-rs命令行模式 cargo run requestsample baidu交互模式 cargo run -- -iinteract-rs> requestsample baidu运行下面的命令是通过http来申请百度 四步做个CLI首先咱们先来看看框架的目录构造 .├── examples├── log├── logs└── src ├── cmd ├── commons ├── configure ├── interact ├── logger └── requestcmd目录是咱们做本人性能时要动的次要目录,上面咱们一步一步的实现requestsample命令。 定义命令 cmd 模块用于定义命令以及相干子命令,requestsample.rs 中定义了拜访百度的命令 use clap::Command;pub fn new_requestsample_cmd() -> Command<'static> {clap::Command::new("requestsample").about("requestsample").subcommand(get_baidu_cmd())}pub fn get_baidu_cmd() -> Command<'static> {clap::Command::new("baidu").about("request www.baidu.com")}new\_requestsample\_cmd 函数定义了命令 "requestsample",get\_baidu\_cmd 函数定义了 requestsample 的子命令 baidu ...

September 7, 2023 · 2 min · jiezi

关于cli:从0构建cli脚手架

前言脚手架的作用缩小重复性的工作,不须要复制其余我的项目再删除无关代码,或者从零创立一个我的项目和文件。能够依据交互动静生成我的项目构造和配置文件。多人合作更为不便,不须要把文件传来传去。本文我的项目残缺代码请戳https://github.com/xiumubai/xiumu-cli 实现性能xiumu init <projectName>依据近程模板,初始化一个我的项目(vue、react)xiumu --version 查看以后版本号xiumu -h 帮忙命令第三方库仓库用处commander命令行工具download-git-repo用来下载近程模板inquirer交互式命令行工具ora显示loading动画我的项目初始化创立一个空我的项目(xiumu-cli),应用 npm init 进行初始化。 装置依赖yarn add commander inquirer download-git-repo 增加bin命令在package.json文件中增加 { "bin": { "xiumu": "./index.js" },}这样咱们就能够应用xiumu这个命令了。 在index.js文件头部中增加#!/usr/bin/env node用来指定运行环境。 增加command命令接下来须要增加init命令,这样就能够应用xiumu init <projectName>进行我的项目的初始化。 commander是 node.js 命令行解决方案。能够应用它进行命令交互。 index.js #!/usr/bin/env nodeconst { Command } = require('commander');const program = new Command();class Init { start() { program .version(require('./package.json').version) .option('-v, --version', '查看以后版本'); program .command('init <projectName>') .description('create a new project') .action((projectName, options) => { console.log('projectName': projectName) }); program.parse(process.argv); }}new Init().start();运行node index.js init test,在日志中失去 ...

May 8, 2023 · 2 min · jiezi

关于cli:AWS-CLI入门教程亲测

背景因为公司有用到S3,所以整顿了一个S3的简略入门教程。当然,入门之后有其余更高级的用法需要,就靠本人去查文档了。入门的教程能让你疾速上手,不至于翻阅一堆文档,容易被劝退。这里次要是介绍如何用cli去操作S3。 官网文档https://docs.aws.amazon.com/cli/latest/userguide/cli-services... 装置客户端https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/gettin... curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"unzip awscliv2.zipsudo ./aws/install什么是 Amazon S3就是国内的对象存储,更粗犷的比喻,能够了解成一个云盘(只是一个有接口的云盘)。下边是官网术语: Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,提供行业当先的可扩展性、数据可用性、安全性和性能。各种规模和行业的客户能够为简直任何应用案例存储和爱护任意数量的数据,例如数据湖、云原生应用程序和挪动应用程序。借助高老本效益的存储类和易于应用的治理性能,您能够优化老本、组织数据并配置精密调整过的访问控制,从而满足特定的业务、组织和合规性要求。 配置aws configureAWS Access Key ID [None]: xxxAWS Secret Access Key [None]: xxxDefault region name [None]: cn-northwest-1Default output format [None]: json列出对象#列出所有bucketaws s3 ls#列出单个bucketaws s3 ls s3://bucket-name/#列出bucket中的某个对象aws s3 ls s3://bucket-name/example/同步对象s3 sync 将更新与指标地位中同名文件的大小或批改工夫不同的任何文件。 #格局$ aws s3 sync <source> <target> [--options]上面的示例显示了如何应用 --delete 选项。 // Delete local file$ rm ./MyFile1.txt// Attempt sync without --delete option - nothing happens$ aws s3 sync . s3://my-bucket/path// Sync with deletion - object is deleted from bucket$ aws s3 sync . s3://my-bucket/path --deletedelete: s3://my-bucket/path/MyFile1.txt// Delete object from bucket$ aws s3 rm s3://my-bucket/path/MySubdirectory/MyFile3.txtdelete: s3://my-bucket/path/MySubdirectory/MyFile3.txt// Sync with deletion - local file is deleted$ aws s3 sync s3://my-bucket/path . --deletedelete: MySubdirectory\MyFile3.txt// Sync with Infrequent Access storage class$ aws s3 sync . s3://my-bucket/path --storage-class STANDARD_IA挪动对象#格局aws s3 mv <source> <target> [--options]#将所有对象从 s3://bucket-name/example 挪动到 s3://my-bucket/aws s3 mv s3://bucket-name/example s3://my-bucket/#将本地文件从当前工作目录挪动到 Amazon S3 存储桶(相当于cp+rm)aws s3 mv filename.txt s3://bucket-name#将文件从 Amazon S3 存储桶挪动到当前工作目录(相当于cp+rm)aws s3 mv s3://bucket-name/filename.txt ./复制对象#将所有对象从 s3://bucket-name/example 复制到 s3://my-bucket/aws s3 cp s3://bucket-name/example s3://my-bucket/#以下示例应用 s3 cp 命令,将本地文件从当前工作目录复制到 Amazon S3 存储桶aws s3 cp filename.txt s3://bucket-name#下载单个文件aws s3 cp s3://bucket-name/filename.txt ./删除对象aws s3 rm s3://bucket-name/example/filename.txt #删除单个文件aws s3 rm s3://bucket-name/example --recursive#删除所有对象更多更多最新文章,请关注同名公众号。 ...

May 5, 2023 · 1 min · jiezi

关于cli:现代化命令行工具之Ubuntu-2004qbit

批量配置繁难的 shell 脚本:https://github.com/qbit-git/m... bash modern_cli_ubuntu2004.sh本脚本实用于与有 root/sudo 权限应用本脚本实用于初始化,没做条件判断,不适用于简单情景工具rg 替换 grep编程语言: RustGitHub:https://github.com/BurntSushi...装置与配置 sudo apt install ripgrep -y# 上面一句是防止与下文介绍的 bat 抵触sudo sed -i '/\/usr\/.crates2.json/d' /var/lib/dpkg/info/ripgrep.listecho 'alias grep=rg' >> ~/.bashrcbat 替换 cat编程语言: RustGitHub: https://github.com/sharkdp/bat装置与配置 sudo apt install bat -yecho 'alias cat=batcat' >> ~/.bashrc fd 替换 find编程语言: RustGitHub: # https://github.com/sharkdp/fd sudo install fd-find -yecho 'alias fd=fdfind' >> ~/.bashrc echo 'alias find="fdfind -H"' >> ~/.bashrc fzf 命令行含糊匹配编程语言: GoGitHub: https://github.com/junegunn/fzf sudo apt install fzfecho 'source /usr/share/doc/fzf/examples/completion.bash' >> ~/.bashrc echo 'source /usr/share/doc/fzf/examples/key-bindings.bash' >> ~/.bashrcpipx编程语言: PythonGitHub: https://github.com/pypa/pipx装置与配置 ...

August 16, 2021 · 1 min · jiezi

关于cli:入门cli

overview本示例能够创立一个目录、二个文件。 init projectmkdir projectCommandcd projectCommandlerna initlerna create testCommandlerna add commanderdefined批改./projectCommand/packages/testCommand/package.json { ... "bin": { ... "demo": "./bin/initProject.js", }}init cli在中创立./projectCommand/packages/testCommand/bin/initProject.js。编辑内容如下: #!/usr/bin/env nodeconst program = require('commander')const fs = require('fs')console.log('begin')<!-- 定义命令 start -->program .command('create <project-name>') .description('create a new project') .option('-f, --file <file>', 'name of main file') .option('-m, --minor', 'create minor file') .action((projectName, options) => { console.log('projectName', projectName) console.log('options', options) fs.mkdir(`./${projectName}`, (error) => { if (error) { console.log('我的项目曾经存在,不能再次创立。') } else { console.log('创立目录胜利') fs.writeFile(`./${projectName}/${options.file}`, 'this is main file.', (err) => { if (err) { console.log('创立主文件失败') } else { console.log('创立主文件胜利') } }) if (options.minor) { fs.writeFile(`./${projectName}/minor.md`, 'this is minor file.', (err) => { if (err) { console.log('创立主文件失败') } else { console.log('创立主文件胜利') } }) } } })})<!-- 定义命令 end -->// 解析命令。即依据输出到终端的内容执行相应命令。program.parse(process.argv)usage在本包中应用命令在./projectCommand/packages/testCommand下执行npm link再执行initProject create li -f main.js -m,则输入: ...

May 11, 2021 · 1 min · jiezi

关于cli:入门commander

overview官网写的readme太差了,连一个像样的demo都没有。我看了几天readme也没学会。起初还是去看源码本人揣摸进去的。是tj太nb了,让我这个小白的看不懂他的的文章,还是写不分明本人的库能干什么、怎么干。上面是我写的demo。不便像我这样的小白入门。若你能看懂tj写的readme。关了这个页面吧。 init projectmkdir projectCommandcd projectCommandlerna initlerna create testCommandlerna add commanderdefined批改./projectCommand/packages/testCommand/package.json { ... "bin": { "demo": "./bin/index.js" }}init cli在中创立./projectCommand/packages/testCommand/bin/index.js。编辑内容如下: #!/usr/bin/env node// 疾速引入Commander的实例const program = require('commander')// 定义选项program .option('-d, --debug', 'output extra debugging') .option('-s, --small', 'small pizza size')// 解析选项,即:执行选项program.parse(process.argv) // process.argv 会获取命令行的数据,返回类型是数组。// 依据选项及其值,执行相应逻辑。if (program.debug) console.log(program.opts())if (program.small) console.log('- small pizza size')该文件的解决逻辑:依据选项执行相应输入。 usage在本包中应用命令在./projectCommand/packages/testCommand下执行npm link再执行demo -d,则输入:{ debug: true, small: undefined }再执行demo -s,则输入:- small pizza size若失去相应输入,则验证cli运行正确。 在其余包中应用命令创立一个其余包在./projectCommand/中执行 lerna create useCommandlerna add testCommandcd packages/testCommandnpm unlink // 勾销全局软链接该包demo -d // 验证是否勾销胜利// => zsh: command not found: demo编辑./projectCommand/packages/useCommand/package.json ...

May 10, 2021 · 1 min · jiezi

关于cli:通过-Vite-的-createapp-学习如何实现一个简易版-CLI

前言前段时间,尤雨溪答复了一个宽广网友都好奇的一个问题:Vite 会不会取代 Vue CLI? 答案是:是的! 那么,你开始学 Vite 了吗?用过 Vite 的同学应该都相熟,创立一个 Vite 的我的项目模版是通过 npm init @vitejs/app 的形式。而 npm init 命令是在 npm@6.1.0 开始反对的,实际上它是先帮你装置 Vite 的 @vitejs/create-app 包(package),而后再执行 create-app 命令。 至于 @vitejs/create-app 则是在 Vite 我的项目的 packages/create-app 文件夹下。其整体的目录构造: // packages/create-app|———— template-lit-element|———— template-lit-element-ts|———— template-preact|———— template-preact-ts|———— template-react|———— template-react-ts|———— template-vanilla|———— template-vue|———— template-vue-tsindex.jspackage.jsonVite 的 create-app CLI(以下统称为 create-app CLI)具备的能力不多,目前只反对根底模版的创立,所以全副代码加起来只有 160 行,其整体的架构图: 能够看出的确非常简单,也因而 create-app CLI 是一个很值得入门学习如何实现简易版 CLI 的例子。 那么,接下来本文将会围绕以下两个局部带着大家一起通过 create-app CLI 来学习如何制作一个简易版的 CLI: create-app 中应用到的库(minimist、kolorist)逐渐拆解、剖析 create-app CLI 源码create-app CLI 中应用到的库create-app CLI 实现用到的库(npm)的确很有意思,既有咱们相熟的 enquirer(用于命令行的提醒),也有不相熟的 minimist 和 kolorist。 那么,前面这两者又是拿来干嘛的?上面,咱们就来理解一番~ ...

February 8, 2021 · 4 min · jiezi

关于cli:交互型命令行

在前人的根底上,自定义交互脚本是一个很简略的事件。 这里介绍四个工具,别离作用在不同的畛域: shelljs 执行脚本程序inquirer 命令行用户交互界面chalk 命令行日志款式可嵌套、可链式commander 自定义命令行参数// const spawn = require('child_process').spawn// const { Command } = require('commander')const shell = require('shelljs');const inquirer = require('inquirer');const chalk = require('chalk');const scriptsConfig = require('../config/scripts')const runModes = require('../config/modes')// const program = new Command();let curScriptEvent = process.env.npm_lifecycle_event; // E.g.:'npm run serve'中的'serve'let curScriptSource = process.env.npm_lifecycle_script; // E.g.:'npm run serve: vue-cli-service serve'的'vue-cli-service serve'const build = async () => { let res; try { // j、k或者数字键抉择 res = await inquirer.prompt([ { type: 'list', name: 'operation', message: '请抉择你要运行的命令:npm run', choices: ['serve', 'build', 'push', 'fds', 'test'] }, { type: 'list', name: 'mode', message: '请抉择你要执行的环境?', choices: ['dev', 'prev', 'pro'] }, ]); } catch(e) { if (e.isTryError) { console.log(chalk.red("Prompt couldn't be rendered in the current environment")) } else { console.log(chalk.red("Error:", e)) } } const { operation, mode } = res; console.log(chalk.green(`正在执行操作npm run ${operation}---${mode}......`)); curScriptEvent = `${operation}${mode ? (':' + mode) : ''}` curScriptSource = scriptsConfig[curScriptEvent] if (process.platform === 'win32') { shell.exec(`npx cross-env ${curScriptSource}`) // spawn('npx cross-env', [curScriptSource], { // stdio: 'inherit', // shell: true // }) } else { shell.exec(`npm run ${curScriptEvent}`) // spawn('npm', ['run', curScriptEvent]) }}build();优化版: ...

January 24, 2021 · 2 min · jiezi

Dojo-Build-简介

翻译自:https://github.com/dojo/frame... Dojo 提供了一套强大的命令行工具,让构建现代应用程序更加简单。 可以自动创建包(Bundle),可以使用 PWA 在本地缓存文件,可以在构建阶段渲染初始的 HTML 和 CSS,也可以使用 Dojo 的 CLI 工具和 .dojorc 配置文件按条件忽略一些代码。或者脱离(eject) Dojo 的构建工具,直接使用底层的构建工具以做到完全掌控。 功能描述Dojo CLI模块化的命令行工具,用于快速启动新的应用程序、创建部件和运行测试等。开发服务器开发时使用的本地 web 服务器,用于监听文件系统,当检测到变化时会自动重新构建。也支持 HTTPS 和设置代理。包(bundle)通过减少用户需要下载的内容和优化用户实际需要的应用程序交互时间(Time-to-Interactive)以提高用户体验。可以根据路由自动创建包,或者在配置文件中明确定义包。按条件纳入代码通过 .dojorc 配置文件可以静态方式关闭或打开使用 dojo/has 定义的功能。由于这些配置而无法访问到的代码分支会被自动忽略掉。这就很容易为特定目标(如 IE11 或 mobile)提供特定功能,而不会影响包的大小。PWA 支持渐进式 Web 应用程序通过缓存内容甚至脱机工作,创建更快、更可靠的用户体验。通过配置文件或者在代码中定义,dojo 很容易创建一个 service work,并将其构建为应用程序的一部分。构建时渲染在构建时渲染路由以生成初始的 HTML 和 CSS。在构建时渲染,Dojo 可以节省出初始渲染的成本,创建出一个响应性更高的应用程序,且不会引入额外的复杂性。基本用法Dojo 提供了一组 CLI 命令,辅助创建和构建应用程序。本指南假设已全局安装 @dojo/cli,且在项目中安装了 @dojo/cli-build-app 和 @dojo/cli-test-intern。如果项目是使用 @dojo/cli-create-app 初始化的,那么这些依赖应该已经存在。 构建Dojo 的 CLI 工具支持多种构建目标或 mode。在 dojo create app 为 package.json 生成的几个脚本(scripts)中可看到所有模式。 运行以下命令,创建一个为生产环境优化过的构建。 > dojo build --mode dist此次构建使用 dist 模式创建应用程序包,并将结果输出到 output/dist 目录中。 ...

November 3, 2019 · 1 min · jiezi

前端物料中台建设

我在上一篇的文章大BU级别的"前后端分离"实践中提出了我们当前的前端团队中存在一些问题以及解决思路,并且在其中详细地写出了统一视图服务的实现思路和收益。这篇文章主要写关于前端迭代及上下游协作效率我们是如何解决的。 背景我们的目的是提升前端迭代及上下游的协作效率,我们从四个出发点开始讲起: 团队负责的业务越来越读,如何提高资源、组件及代码的复用率,提升整体研发效率;提升项目的上下游协作效率,涉及到从UE到UI到FE再到QA的整个流程,这需要很强的推动力和足够的说服力;提升人效比,一人顶三人,提升项目中前端开发人员的人效比,让前端做的更快、更多、更好;最后是将整体方法进行梳理,把提效的这部分能力应用在web、wap、小程序中,实施中台化。 解决思路 首先,建设前端物料体系,重新定义和统一前端的开发标准,建设前端物料资源体系,其中包括设计和单元测试等资源;第二,实行工具化管理,完善工程体系,统一前端的开发流程和技术栈,保证资源复用率;最后也是最重要的就是渐进式开发,因为它决定了物料完成之后能不能达到最佳的复用率。 整体架构 问:什么是物料?答:可按照规范进行标准化构建,并且可以在不同项目,不同团队,不同成员之间复用的任意资源。我们把这类资源统称为物料。参与物料资源生产和消费的对象共有四个:开发者、使用者、设计者、产品/运营。为此,物料中台提供了标准化的物料资源,其中包括项目的基础框架、组件库、区块和模板。基础框架是项目的样板框架也就是俗称的boilerplate,组件库是基于部门内设计同学提出的Union Design设计规范开发前端组件库,它同时支持web和小程序两个宿主环境。区块可以简单理解为组件的拼装集合,它比组件库提供了更上层的业务封装能力,但是这也让它变得没有组件库那么地纯粹;模板就是各种站点的样式模板,其中整合了前面所提到的基础框架+组件库+区块等各种资源。让开发者可以最大限度地减少工作量。 图中的中间部分是可被支持的宿主环境,包括Web端、移动端及小程序。Web端可以支持TypeScript+Vue/React框架模式进行开发,移动端支持VW/REM布局方式,同时也支持Hybrid模式进行开发,具体文章请参考端动态化方案详细设计。另外由于业务原因,小程序目前支持微信小程序和智能小程序两种。 最下面是物料中台提供的工程化体系,提供了CLI工具和GUI工具。CLI工具用于在前端项目开发中,让FE可以便捷地对物料进行生成和消费;此外物料中台还提供了GUI工具,让物料的消费方可以通过拖控件的方式,对项目进行快速地原型落地和模板设计,里面具体的细节请等待后续的文章更新。 我们和设计同学一起推出了物料相关的设计资源,其中就包括Union Design设计规范。为了降低物料在项目中的落地阻力,物料中台还提供了完善学习资源,包括文档、教程,也会定期组织培训和分享。 最后,在整体上实现了物料中台的功能。 组件库设计我们在进行组件库设计是主要考虑了两个方面: 组件分级 基础UI组件复合组件业务组件组件边界 越界操作副作用侵入性循环依赖首先对组件进行分级,根据对业务的侵入性和纯粹性(复用程度)为分界点,拆分为三级,如下图: 如图中表示,越往下业务侵入性越高,抽象程度越低。越往上复用程度和纯粹性越高,同时抽象程度也越高。 组件库设计的关键就是组件的边界限制,每个组件都是非常独立的且不可再拆分的最小单元。首先是禁止组件的越界操作,只让它处理份内的事情,对外部的事情不能染指半分;第二是副作用,禁止(尽可能的少)有副作用,如果了解过函数式编程,就会知道副作用的各种优缺点;第三是侵入性,禁止纯粹的木偶组件(组件库中的每个组件都是纯粹的木偶组件)对业务的侵入性,因为侵入性越高复用率越低,组件库对于其他人也就越难用;最后是禁止组件内部的循环依赖。 最终实现 最终实现中,整体分为四层,最下面是技术实现,往上是依赖的设计资源,再往上就是组件库提供的五种基础组件,包括基本操作、表单、视图、导航和数据处理等。最上层则是接入的业务方。自下而上地完成了整个前端业务方向的组件库方案。 上下游协作对上下游协作效率优化的流程中最核心的就是效率提升和物料复用。 效率提升 交互设计单元测试回归成本物料复用 物料共享生成和消费业务迁移渐进式开发整体流程如下图: 在整体协作流程中主要包含两条操作流程,物料管理和物料使用。 物料管理的功能是给物料开发者使用的,负责物料对不同平台的支持,创建,生成和列表管理,可以在管理列表中对自己上传物料进行消费。 对于物料的消费,提供物料实时预览、下载、使用、和源代码查看功能。使用物料时会自动生成CLI命令,在终端中执行命令就可以在项目中使用此物料。 另外,也可以将项目中的某些功能抽离为物料,供其他项目使用。以此实现可持续迭代和渐进式开发。这样就完成物料资源在不同团队,不同项目,不同成员之间的共享和复用,提升前端的整体协作效率。 整体收益 内部接入6个项目;在公司内部NPM中开源了2个组件库;在BU内部共推出了4款产品,分别是Union Design设计规范、@baidu/union-design、@baidu/union-design-wechat和marble-cli/GUI工具; 在代码复用率方面,项目基础框架为46.1%,业务逻辑为21.87%;全新开发一个项目迭代周期缩短23.8%,迭代的项目缩短21.4%; 最后有任何疑问请联系作者

August 20, 2019 · 1 min · jiezi

构建一个用于创建组件库的项目脚手架工具类-Vuecli3

缘起最近公司内部想搭建一个私有的 npm 仓库,用于将平时用到次数相当频繁的工具或者组件独立出来,方便单独管理,随着项目的规模变大,数量变多,单纯的复制粘粘无疑在优雅以及实用性上都无法满足我们的需求,所以进一步模块化是必然的。 但是一个组件库的建立其实是一个非常麻烦的过程,基础 webpack 的配置不用多说,接着你还要配合增加一些 es-lint 之类的工具来规范化团队成员的代码。在开发过程中,你自然需要一个目录来承载使用示例,方便 dev 这个组件,随后呢,你还得建立一个打包规范,发布到私有 npm 仓库中。 如此一来,必然大大降低我们的积极性,所以不如创建一个用于建立模块包的脚手架工具,方便我们项目的初始化。 tips:最终成品在底部 私有 NPM这里简单提及一下 私有 npm 的搭建。 npm i verdaccio -gpm2 start verdaccio推荐配合 nrm 使用 快速切换仓库地址 verdaccio github 还整个意大利名,属实洋气。 工具在进入正题之前,我先介绍一些要点和工具,有了这写关键点,写起来其实就相当简单了。 npm bin大家有没有想过一些全局安装的工具,他是如何做到在命令行里面自由调用的呢? 事实上这个东西是 npm 提供的链接功能 // package.json{ "name": "lucky-for-you", "bin": { "lucky": "bin/lucky" }}当这样的一个模块被发布之后,一旦有人使用 -g 参数全局安装 sudo npm i luck-for-you -g/usr/local/bin/lucky -> /usr/local/lib/node_modules/luckytiger-package-cli/bin/lucky # npm 帮你进行链接 npm 事实上会帮你进行一次链接,链接到你操作系统的 Path 之中,从而但你敲出 Lucky 这个命令的时候,能从 path 中成功找到对应的程序 ...

July 11, 2019 · 5 min · jiezi

CMDR05-Tricks-Walks-Hooks

暂时来讲,这是最后一篇关于 cmdr 的系列介绍文章了。 所有这个系列包括: 另一个go命令行参数处理器 - cmdrcmdr 02 - 复刻一个 wgetcmdr 03 - 用流式接口定义命令行参数处理选项cmdr 04 - 简单微服务cmdr 05 - 扫尾 - Tricks/Walks/Hooks这一次的内容算是杂烩乱炖。 Tricks~~debug已经在前文讲述过了。这里不再凑字数了。 --treecmdr 提供了一个内置的选项:--tree。 虽然这是一个选项,但它和 --version 一样是有着命令一样的效果:如果 cmdr 在命令行参数中检测到了 --tree,那么它会忽略已经处理的和将要处理的子命令、选项,直接执行 --tree 的 Action。 要想达到类似的效果并不困难:定义一个选项,重载其 Action 字段到一个响应函数,并且在该响应函数的结尾返回 cmdr.ErrShouldBeStopException,这样就会在该选项被识别时并执行Action后直接退出应用程序了。 --tree 的功能是打印出全部命令和子命令,以树结构方式呈现出来。 一个样例如下图: 这是我在开发阶段执行 examples/demo 小程序所得到的结果。Walk for all commands--tree 实际上是利用了 cmdr 内建的 WalkAllCommands() 所提供的遍历方式。 对所有命令及其选项进行遍历,实际上有两种方式:一是利用 Painter 以及相应的内部机制,二是通过 WalkAllCommands 明确地遍历。 PainterPainter 是一个接口。它被用在输出帮助屏这个方面。尽管输出帮助屏只是一个小小的功能,但你还是可以自定义它的行为。你可以自行实现 Painter 接口并通过 SetCurrentHelpPainter(painter) 来更改帮助屏的显示内容。 如果你真的想这么做,可以查阅 Painter 的定义,也可以 issue 到我,或许说不定我能够有所建议。 ...

June 16, 2019 · 2 min · jiezi

使用golang构建一个redis交互命令行

介绍这是一个使用golang开发的redis交互式命令行,希望能解决一些redis原生cli使用中的痛点。项目处于起步阶段,功能并不完全。 项目地址 原文地址 出发点:在工作中,会生成很多规律的redis键,如:test_1,test_2,当需要人肉删除test_*键的时候,使用原生redis-cli,痛苦。在工作中,redis键太多,常常会让人忘记redis键的类型,需要先type再用对应类型的查询命令查询,太累。特点:使用一个命令,查询string,hash,list,set,zset类型的数据批量查询redis键的ttl批量查询redis键的类型使用通配符匹配redis键,选择或直接删除redis键使用table直观展示redis操作情况 命令与使用:git clone https://github.com/dalebao/gRedis-cli.gitcd gRedis-cligo run main.go按照流程填写服务器连接信息 get查询string,hash,list,set,zset类型的数据 `get redisKey`keys使用通配符匹配redis键,返回redis键与对应类型 `keys *`type批量查询redis键类型 `type redisKey1 redisKey2`ttl批量查询redis ttl信息 `ttl redisKey1 redisKey2`expire设置redis键过期时间expire redisKey1 100 单位秒 del批量删除redis键del redisKey1 redisKey2 rdel匹配redis键,直接或选择删除redis键rdel redis* 退出输入 quit 接下来要做继续完善查询功能考虑是否要增加修改redis键内容增加配置保存功能,避免重复输入配置信息思考大量数据redis键的处理方式期待在issue中与我交流鸣谢命令行构建工具 golang表格构建工具

June 13, 2019 · 1 min · jiezi

Consul-命令行最全文档

1.启动一个带ACL 控制的Agent首先,从这个网址下载consul,解压后发现就是个可执行文件,如果不可以执行,chmod +x consul 一下。 为了试验Consul较多的功能,这里我们打算启用一个dev模式,带ACL控制的Consul代理。配置文件config.json如下 { "datacenter":"dc1", "primary_datacenter":"dc1", "data_dir":"/opt/consul/data/", "enable_script_checks":false, "bind_addr":"127.0.0.1", "node_name":"consul-dev", "enable_local_script_checks":true, "log_file":"/opt/consul/log/", "log_level":"info", "log_rotate_bytes":100000000, "log_rotate_duration":"24h", "encrypt":"krCysDJnrQ8dtA7AbJav8g==", "acl":{ "enabled":true, "default_policy":"deny", "enable_token_persistence":true, "tokens":{ "master":"cd76a0f7-5535-40cc-8696-073462acc6c7" } }}下面是参数说明: datacenter 此标志表示代理运行的数据中心。如果未提供,则默认为“dc1”。 Consul拥有对多个数据中心的一流支持,但它依赖于正确的配置。同一数据中心中的节点应在同一个局域网内。primary_datacenter: 这指定了对ACL信息具有权威性的数据中心。必须提供它才能启用ACL。bind_addr: 内部群集通信绑定的地址。这是群集中所有其他节点都应该可以访问的IP地址。默认情况下,这是“0.0.0.0”,这意味着Consul将绑定到本地计算机上的所有地址,并将第一个可用的私有IPv4地址通告给群集的其余部分。如果有多个私有IPv4地址可用,Consul将在启动时退出并显示错误。如果指定“[::]”,Consul将通告第一个可用的公共IPv6地址。如果有多个可用的公共IPv6地址,Consul将在启动时退出并显示错误。 Consul同时使用TCP和UDP,并且两者使用相同的端口。如果您有防火墙,请务必同时允许这两种协议。advertise_addr: 更改我们向群集中其他节点通告的地址。默认情况下,会使用-bind参数指定的地址.server: 是否是server agent节点。connect.enabled: 是否启动Consul Connect,这里是启用的。node_name:节点名称。data_dir: agent存储状态的目录。enable_script_checks: 是否在此代理上启用执行脚本的健康检查。有安全漏洞,默认值就是false,这里单独提示下。enable_local_script_checks: 与enable_script_checks类似,但只有在本地配置文件中定义它们时才启用它们。仍然不允许在HTTP API注册中定义的脚本检查。log-file: 将所有Consul Agent日志消息重定向到文件。这里指定的是/opt/consul/log/目录。log_rotate_bytes:指定在需要轮换之前应写入日志的字节数。除非指定,否则可以写入日志文件的字节数没有限制log_rotate_duration:指定在需要旋转日志之前应写入日志的最长持续时间。除非另有说明,否则日志会每天轮换(24小时。单位可以是"ns", "us" (or "µs"), "ms", "s", "m", "h", 比如设置值为24hencrypt:用于加密Consul Gossip 协议交换的数据。在启动各个server之前,配置成同一个UUID值就行,或者你用命令行consul keygen 命令来生成也可以。acl.enabled: 是否启用acl.acl.default_policy: “allow”或“deny”; 默认为“allow”,但这将在未来的主要版本中更改。当没有匹配规则时,默认策略控制令牌的行为。在“allow”模式下,ACL是黑名单:允许任何未明确禁止的操作。在“deny”模式下,ACL是白名单:阻止任何未明确允许的操作.acl.enable_token_persistence: 可能值为true或者false。值为true时,API使用的令牌集合将被保存到磁盘,并且当代理重新启动时会重新加载。acl.tokens.master: 具有全局管理的权限,也就是最大的权限。它允许操作员使用众所周知的令牌密钥ID来引导ACL系统。需要在所有的server agent上设置同一个值,可以设置为一个随机的UUID。这个值权限最大,注意保管好。接着我们以dev模式启动agent。 ./consul agent -dev -config-file ./config.json 启动完之后,你会发现出现下面的错误这个错误是没有设置agent-token造成的,agent-token主要用于客户端和服务器执行内部操作.比如catalog api的更新,反熵同步等。 ...

June 9, 2019 · 3 min · jiezi

cmdr-03-用流式接口定义命令行参数处理选项

cmdr 03 - 用流式接口定义命令行参数处理选项基于 v0.2.17 转眼已经来到了 cmdr v0.2.17 了,为了解决此前版本中关于子命令和选项定义语句的太多嵌套的问题,我们实现了流式调用接口(Fluent APIs)。 cmdr 是我发布的一个开源的 golang 命令行参数处理器。它是 golang flags 的替代品。之所以发布它,是因为已有的 command line UI 三方包无法满足我的日常要求,迫不得己自己造一个。如果尚未有了解 cmdr 怎么使用的,不妨抽空浏览我的早前文章,以求获得一些基本概念: 另一个go命令行参数处理器 - cmdrcmdr 02 - 复刻一个 wget稍后我会继续针对 cmdr 的用法做介绍文章。 至于本文呢 ,只是简单讲述一下如何使用 cmdr 的流式接口(Fluent API)来完成定义。 定义 RootCommandroot := cmdr.Root("aa", "1.0.1").Header("aa - test for cmdr - no version")rootCmd = root.RootCommand()第二句是拿到一个 *cmdr.Command 指针,稍后可以做一下相关的其它操作。 此外,rootCmd 作为函数返回值,也便于被用到向 cmdr.Exec() 做传递参数。 func buildCmds() *cmdr.Command { root := ... rootCmdr = root.RootCommand() ... return rootCmdr}func main() { if err := cmdr.Exec(buildCmds()); err != nil { logrus.Fatal(err) }}定义命令 Command顶级的命令其实就是 RootCommand 的 子命令,所以: ...

June 1, 2019 · 2 min · jiezi

cmdr-02-复刻一个-wget

cmdr 02 - Covered for wget 基于 cmdr v0.2.11继 Getting Start 之后,我们来介绍如何用 cmdr 复刻一个 wget 的命令行界面,并具体介绍 Command 和 Flag 的各个细节以及 cmdr 能够做到哪些别人做不到的事。 此外,我们也声明一下,Getting Start ('另一个go命令行参数处理器 - cmdr') 的内容有了一些轻微的变化,因为这两周来,我们已经不停地增加了很多特性来完善 cmdr 的能力,期间有一些不恰当的策略、衍生的命名、采用的算法都有所调整,虽然尽力避免变化,但它是不可免的。我们是期望给你的编程界面越来越完美,让整个编写的流程流畅化,自然化。 wget 的参数wget 本身是一个 GNU 应用程序。它的命令行参数有长有短,短参数可能有两个字符,此外参数被分为若干个分组。请看一部分截取: 这将是我们复刻的基准。 cmdr 都能做到些什么 - First我们曾经做过多个应用,不同的开发语言,不同的目标,有的是练练手,有的是眼前有个事情有点烦、不好处理、一怒之下就干,有的是有特定的目的例如一个RESTful服务,等等。 所以,要想满足那么多的情况下命令行参数的组织和设定都能被很好地表示,不夸张地说,迄今数十年来,我们没有找到一个命令参数解释器能够完成这个任务。把时间限定在最近几年,把开发语言限定在 Golang,C++,Python 等几种之内,依然没有谁真的能这么称呼自己。现有的命令行参数解释器都有这样那样的不如意: 短参数不能重复,哪怕是在多级命令结构下也必须全局唯一;不能分组;分组后顺序随机或者字母序,开发者无法干预,无法按照自己的意愿提供最好的顺序;短参数需要两个字母、或者三个字母的缩略语,更能表达参数原意时,基本上大多数现有的命令行参数解释器都废了;想要长参数显示为“--progress=TYPE”的式样,其中的 TYPE 还可以被复用;想要 git -m 的效果,结果费尽了力,终于实现了一个,然而受制于既有命令行解释器的结构,实现的坑坑洼洼的,自己都难以满意;想要和配置文件挂钩,没错挂钩了,然而需要写很多代码来安排;想要 /etc/program 加载配置文件,结果累了;想要 /etc/nginx/sites.avaliable 那样的效果,自己 watch 了,却合并不了新的配置到已经加载和构建好的配置中,也无法有效地通知应用的业务层按需取用新的配置条目;还有很多遇到这些情况时,多数时候只能忍了,毕竟没有太多精力专门去搞参数问题,还有大把的业务需要去完成的对吧。 cmdr 选择和实现 wget-demo 也是为了展示自己大体上能够解决命令行参数处理的多数问题。不过和其它命令行参数的策略不同地在于:别人通常会对参数值的类型做很多文章,例如支持 string/int/slice/map 的多种式样,或者提供 validator,或者采用 Golang 结构 Tag 方式来挂钩参数类型处理器等等。但是 cmdr 在参数类型方面只能说有且够,整体的重心并不在这些方面。 ...

May 30, 2019 · 5 min · jiezi

前端必修课ES2017下的构建工具原理与实战

ES2017+,你不再需要纠结于复杂的构建工具技术选型。 也不再需要gulp,grunt,yeoman,metalsmith,fis3。 以上的这些构建工具,可以脑海中永远划掉。 100行代码,你将透视构建工具的本质。 100行代码,你将拥有一个现代化、规范、测试驱动、高延展性的前端构建工具。 在阅读前,给大家一个小悬念:什么是链式操作、中间件机制?如何读取、构建文件树?如何实现批量模板渲染、代码转译?如何实现中间件间数据共享。相信学完这一课后,你会发现————这些专业术语,背后的原理实在。。。太简单了吧! 构建工具体验:弹窗+uglify+模板引擎+babel转码...如果想立即体验它的强大功能,可以命令行输入npx mofast example,将会构建一个mofast-example文件夹。 进入文件后运行node compile,即可体验功能。 顺便说一句,npx mofast example命令行本身,也是用本课的构建工具实现的。——是不是不可思议? 本课程代码已在npm上进行发布,直接安装即可npm i mofast -D即可在任何项目中使用mofast,替代gulp/grunt/yeoman/metalsmith/fis3进行安装使用。 本课程github地址为: https://github.com/wanthering... 在学完课程后,你就可以提交PR,一起维护这个库,使它的扩展性越来越强! 第一步:搭建github/npm标准开发栈请搭建好以下环境: jest 测试环境eslint 格式标准化环境babel es2017代码环境或者直接使用npx lunz mofast 然后一路回车。 构建出的文件系统如下 ├── .babelrc├── .editorconfig├── .eslintrc.js├── .gitignore├── README.md├── circle.yml├── package.json├── src│   └── index.js├── test│   └── index.spec.js└── yarn.lock第二步: 搭建文件沙盒环境构建工具,都需要进行文件系统的操作。 在测试时,常常污染本地的文件系统,造成一些重要文件的意外丢失和修改。 所以,我们往往会为测试做一个“沙盒环境” 在package.json同级目录下,输入命令 mkdir __mocks__ && touch __mocks__/fs.js yarn add memfs -D yarn add fs-extra创建__mocks__/fs.js文件后,写入: const { fs } = require('memfs')module.exports = fs然后在测试文件index.spec.js的第一行写下: ...

May 23, 2019 · 5 min · jiezi

SQLRESTful开源GO脚手架工具ginbrogin-and-gorms-brother-详解

安装felixgit clone https://github.com/dejavuzhou/felixcd felixgo mod downloadgo installecho "添加 GOBIN 到 PATH环境变量"echo "或者"go get github.com/dejavuzhou/felixecho "go build && ./felix -h"What is GinbroGin脚手架工具:因为工作中非常多次的使用mysql数据库 + gin + GORM 来开发RESTful API程序,所以开发一个Go语言的RESTful APIs的脚手架工具Ginbro代码来源:Ginrbo的代码迭代自github.com/dejavuzhou/ginbroSPA二进制化工具:vuejs全家桶代码二进制化成go代码,编译的时候变成二进制,运行的时候直接加载到内存中,同时和gin API在一个域名下不需要再nginx中配置rewrite或者跨域,加快API访问速度功能一:Gin+GORM_SQL RESTful 脚手架工具工作原理通过cobra 获取命令行参数使用sql参数连接数据库后去数据库表的名称和字段类型等数据库数据库边的表名和字段信息,转换成 Swagger doc 规范字段 和 GORM 模型字段使用标准库 text/template 生成swagger.yaml, GORM 模型文件, GIN handler 文件 ...使用 go fmt ./... 格式化代码使用标准库archive/zip打包*.go config.toml ...代码,提供zip文件下载(命令行模式没有)支持数据库大多数SQL数据库mysqlSQLitepostgreSQLmssql(TODO:: sqlserver)ginbro 生成app代码包含功能简介每一张数据库表生成一个RESTful规范的资源(GET<pagination>/POST/GET<one>/PATCH/DELETE)支持API-json数据分页-和总数分页缓存,减少全表扫描支持golang-内存单机缓存缓存前端代码和API公用一个服务,减少跨域OPTION的请求时间和配置时间,同时完美支持前后端分离开箱支持jwt-token认证和Bearer Token 路由中间件开箱即用的logrus数据库开箱即用的viper配置文件开箱即用的swagger API 文档开箱即用的定时任务系统项目演示地址felix sshw 网页UI演示地址 用户名和密码都是admin生成swagger API交互文档地址 http://ginbro.mojotv.cn/swagger/msql生成go代码地址bili命令行演示视频地址命令行参数详解[root@ericzhou felix]# felix ginbro -hgenerate a RESTful APIs app with gin and gorm for gophersUsage: felix ginbro [flags]示例:felix ginbro -a dev.wordpress.com:3306 -P go_package_name -n db_name -u db_username -p 'my_db_password' -d '~/thisDir'Flags: --authColumn string 使用bcrypt方式加密的用户表密码字段名称 (default "password") --authTable string 认知登陆用户表名称 (default "users") -a, --dbAddr string 数据库连接的地址 (default "127.0.0.1:3306") -c, --dbChar string 数据库字符集 (default "utf8") -n, --dbName string 数据库名称 -p, --dbPassword string 数据库密码 (default "password") -t, --dbType string 数据库类型: mysql/postgres/mssql/sqlite (default "mysql") -u, --dbUser string 数据库用户名 (default "root") -d, --dir string golang代码输出的目录,默认是当前目录 (default ".") -h, --help 帮助 -l, --listen string 生成go app 接口监听的地址 (default "127.0.0.1:5555") --pkg string 生成go app 包名称(go version > 1.12) 生成go.mod文件, eg: ginbroSon[root@ericzhou felix]# web界面对于那些喜欢使用命令行的同学,你们可以选择使用web界面来操作 ...

May 22, 2019 · 2 min · jiezi

打造前端团队的-React-CLI-工具

关于前端 CLI 工具对于 Angular,有官方的 Angular CLI。对于 Vue,有官方的 Vue CLI。对于 React,有官方的 Create React App。Angular CLI 和 Vue CLI 是官方推荐的 CLI 工具,可直接在生产环境中使用,但 Create React App 的定位却有点不同,它的目标是让你快速 Set up 一个 React 应用,如果你要在生产环境中使用,因为它的可定制性并不好,你可能要 eject,然后手动维护 webpack 配置。 我们团队的历程首先说背景,这几年以来,我所在的团队使用的都是 React 技术栈,我们的业务属性是要频繁创建新项目的,但业务的生命周期并不一定短,也可能是长期的。 最开始,应该该是三四年前左右,那时的解决方案比较原始,每个人维护自己的 webpack 配置,有新项目就 copy and paste。这样的优点是灵活,本人对自己项目的配置可控性,缺点是配置升级和维护都不容易,项目交接后问题多。 后来,我们使用项目 Boilerplate 的方式,团队内维护一份 Boilerplate,所有新项目都使用这个 Boilerplate 初始化。这样的优点是 webpack 配置统一化,易于多人合作,缺点是配置升级不容易,因为 webpack 配置是暴露在项目中的,配置会被人修改,很容易脏掉。 再后来,官方出了 Create React App。发现原来 webpack 配置可以不暴露在项目中,可以隐藏在 node_modules 中,这是一种非常优雅的解决方案。所以我们使用了和 Create React App 类似的解决方案,创建自己团队专属的前端 CLI 工具,这也是我们当前的解决方案。这样的优点是 webpack 配置被隐藏,项目目录比较干净,webpack 配置升级容易,只需升级 CLI,缺点是维护 CLI 是一个大工程,需要较多的人力,且需要踩非常多的坑。 ...

May 5, 2019 · 1 min · jiezi

从零开始开发一个react脚手架五

前言:最近天天加班做新项目,Taro版的小程序,还要实现富文本加海报,踩了不少坑,下次专门开个坑说一下。 回到脚手架,说实话从头写一个,即便是参考create-react-app,还是遇到了很多很多问题,这些都是需要自己亲自写过,才能有所体会。这次会写的有杂乱一点,就说说自己遇到的哪些问题。虽然还没有全部完工,但是已经能够基本使用,最下面会贴git地址 问题一 现在基本实现了两个命令,start和build,start就是启动webpack-dev-server,这是开发环境, build就是构建,是production环境。 不管什么命令都需要依赖 NODE_ENV=development或者production,这样会导致每次npm run 的时候都要加上NODE_ENV,而且这个还不能直接写在script命令里面,因为window是不支持,需要安装第三方库。最后参考create-react-app 直接赋值,简单粗暴。 一开始没想到可以这么简单粗暴,走了歪路,想要通过DefinePlugin来实现赋值,但其实这反倒是错误的路线。需要理解编译工具所依赖的NODE_ENV和业务代码里面的NODE_ENV是不同的,DefinePlugin其实就是个简单的全局变量替换,只能替换chunk里面的。 问题二 优化createWebpack,一开始的做法极为简单,直接export一个对象,导致灵活性很差,后来改成导出一个方法,接收各种配置参数,返回一个webpackConfig。 问题三 webpack-dev-serve,这个问题捣鼓了很久很久。关于webpack-dev-serve的contentBase配置。一开始我设置的是dist目录,也就是打包后的目录,即output设置的path。因为以前都是这样玩的,但是这样意味着服务依赖了一个打包构建后的目录dist,否则没有办法找到index.html。这样就很诡异,难道第一次start都必须先build一下吗?很不合理,而且build的时候NODE_ENV必须是development,否则js的引用地址就成了线上地址。而根据上面一个问题,简单粗暴在代码里面写死NODE_ENV就很难改变环境变量了,build出来的只能是线上。看了很久的create-react-app的源码,因为这个脚手架并不需要先build,一开始以为他在start的时候做了什么特殊处理,后来才发现压根不是,它的webpack-dev-server配置的cotentbase竟然不是dist,而是原始目录src/index.html。。。这样我很震惊啊,因为我想的是,js什么的,css什么的引入都必须是在webpack build之后才知道的,因为会做拆分剥离的处理,JS的文件个数和名字不确定,如果引入的是原始目录,那么到底是在哪儿做的JS和CSS插入操作呢,为此寻遍了源码,一个个去找plugin,但最后发现其实压根很简单。在于html-webpack-plugin,貌似它会先在内存中生成一份html,想要访问内存地址直接就是localhost:3000,而webpack-dev-server应该是优先去取内存中的index.html,没取到就会去cotentbase中找,也就是说和cotentbase压根没有半毛钱关系。产生错觉的原因在于,内存中没找到,而cotentbase中同样没找到,就会报404。至于为嘛没有取到内存中的值,在于我闲的没事,设置了publicPath,为/assets/这样导致,想要访问html的地址就变成了localhost:3000/assets/index.html。其实开发环境压根没必要设置assets,没半毛钱作用。 问题四 这个就是很蠢的一个问题了,很粗心。关于dependencies和devDependencies,其实单纯问两者的区别,大家都知道,一个是开发依赖,一个是线上依赖。但是我们实际开发中,不管是你开发依赖还是线上依赖,其实所有的包都会安装,所以有的时候装错地方,也没啥的。but。。。如果你开发的是第三方包,这个问题就大了。。。如果是第三方包,只会安装第三方包的dependencies。因为一开始的开发不规范,导致我随意乱装。。结果查了半天,一直报XXX包找不到。。捣鼓了很久,才发现是个这么简单的问题。 问题五 关于npm publishi的时候如果报权限或者什么其他问题的错,十有八九是 设定了源的路径,如果是淘宝源或者公司源,需要切换回npm默认源,才可以发布。 问题暂时就想到这么多了。其实脚手架最简单的反而是webpack配置了,这些东西一搜索网上一大把,配置消息我就不多说了。说几个值得注意的地方。。第一就是splitChunks,这个设置拆包大小的时候记得注意单位,30000这个是30kb。所以我写的是100000,意味着要拆成独立的包,压缩前必须有100KB,其实还可以设置大一点,你想想100KB压缩后估计就50,再来个GZIP就是20KB左右了。一个包可以再大点。但如果是http2,也可以不用拆太大,毕竟多路复用吗。 第二就是关于静态资源引用的问题,其实我觉得本地业务代码里面完全不用引入静态资源,通通丢到云服务器就好,比如静态图片,我都是通通扔到七牛服务器,代码里面直接写死,爽歪歪,还能享受CDN加成和http2. react+router+redux 说的有点乱,想到啥写啥。主要是事件间隔有点久了,最近加班太忙。 脚手架已经实现了三分之一,现在是直接clone git来作为脚手架,到最后效果应该是npx的形式,不过命令内容已经实现 easy-react start和 easy-react build。代码已经更新到git了,方便给个star。 easy-react即命令的实现源码还没扔上去,等后续完工了再扔上去。毕竟还有服务器端的部分没实现。 脚手架工具很多,我觉得有时间还是自己维护一份的好,想咋玩就咋玩,想升级就升级,自己定制。 git initgit clone https://github.com/417673259/...npm install npm run start 有问题就留言,必定回复,给个赞呗

May 3, 2019 · 1 min · jiezi

miniserve-简单美观的文件服务器

如果想建立一个简单静态文件或目录服务器,通常可以用 Python 实现,而且非常简单 # Python 2python -m SimpleHTTPServer <port># Python 3python3 -m http.server <port>一般情况下,这就够用了,但如果这样的服务器在浏览器提供的界面有些简陋,而且不提供认证服务。更复杂的实现方法是使用 Nginx,但 Nginx 的配置相对繁琐,这里推荐一个使用 Rust 基于 Actix框架实现静态文件或文件夹服务器 miniserve,demo如下 除了更加漂亮的界面和基本用户认证外 miniserve 还支持如下功能 将当前文件夹压缩后下载界面上传文件(可配置)支持监听多网卡自动更改 MIME超级快(powered by Rust and Actix)下载在发行版界面找到操作系统对应的版本,文件很小,最大的 osx 也仅有 3.2MB。 Linuxsudo curl -L https://github.com/svenstaro/miniserve/releases/download/v0.4.1/miniserve-linux-x86_64 -o /usr/local/bin/miniservesudo chmod +x /usr/local/bin/miniserveOSXsudo curl -L https://github.com/svenstaro/miniserve/releases/download/v0.4.1/miniserve-osx-x86_64 -o /usr/local/bin/miniservesudo chmod +x /usr/local/bin/miniserveWindowswindows 下载好 exe 文件可直接运行 Cargo如果电脑上安装了 Rust 和 Cargo,也可以通过 Cargo 安装,但由于 miniserve仅支持 nightly channel,所以你得先切换到 nightly channel rustup add toolchain nightlyrustup default nightlycargo install miniserveDockerminiserve 在 docker hub 上的镜像名为 svenstaro/miniserve ...

May 1, 2019 · 2 min · jiezi

Archlinux逐渐从Gui到Cli化记录

前言从图形桌面到平铺桌面,虽然也是Gui的。但是少了菜单栏和需要鼠标点击的过程,肉眼上看去就像一个终端了。随着时间的变化,Gui给我带来的新鲜感和视觉上的刺激已经日趋下降了,干脆逐渐去图形化咋走像Cli的怀抱。当然,这不是为了Cli而Cli,我也是一个喜欢偷懒的人,只是Gui带来的配置和记忆远远要难与Cli(好吧,我承认图形操作很傻瓜)。Cli只是少了些鼠标上的点击(我用键盘多于鼠标),而且更加小巧简单,占用更小的资源。当然,这只是对于我个人而言——在我使用Gnu/Linux 2年后的一些感悟。在我用Linux替代Windows之前,我也是不会相信Cli会比Gui高效多少的(当然这也要看是什么软件了,像图形编辑软件Gui肯定更胜以筹了)。拥抱Cli好吧,这次也是逐步Cli化的东西是桌面控制栏。Bspwm默认是不带Bar的,我自己后期安装了个Polybar,然后花了一些时间去参考网上的一些配置然后加入自己的配置。那会用起来还是很满意的,天气,雾霾查询,系统监控等功能好个小而精。不过后来有个问题是,我用Bspwm基本上是全屏窗口化的,所以很少看到控制栏,开机和关机前看到是最多的。所以后来我思考我真的需要这个嘛,只为了偶尔看看时间或者调节下音量-况且我大部分时间都是静音的。去Gui控制栏所以我决定弃坑了。接下去就用一些命令行工具替代了。比如这些:声音调节: amixer将声音调节到40%amixer set Master 402.电池电量 acpi查看电量详情(温度等)acpi -V3.切换wmnameIDEA不用LG3D启动会有问题wmname LG3D系统监控top当然了,上面这些Cli工具都是最基本的,有很多增强的CLi工具和软件。用基础的很多都是系统自带的,省去安装了方便在不同的系统上都可以使用到相同的Cli工具。5 前后对比用polybar栏时用Cli命令替代后

April 14, 2019 · 1 min · jiezi

从零开始开发一个react脚手架(四)

这一篇可能主要讲的是热更新,写的很细,遇到很多有意思的地方,一一和大家讲解下。前沿:webpack-dev-server支持热更新,简单的说就是你修改代码,浏览器能够自动刷新页面。因为很久没有配置过webpack-dev-server,搭建出效果后,我一直以为是错的,因为在我的理解上,我以为的热更新并非是刷新页面,而是更新改动的模块。要实现非刷新页面的热更新,还需要有额外的逻辑。先看一段代码截图webpack-dev-server的配置。historyApiFallback设置为true,有点类似于app.get("*", index),就是一个兜底的路由,保证所有未拦截的404页面都转向index。contentBase 设置的就是dist目录,即webpack打包的dist目录,所以开启webpack-dev-server之前,必须打包一下,不然找不到index.html文件。hot设置为true,表示启用热更新,因为我们用的是API方式允许webpack-dev-server,所以配置项目中必须设置port和host,否则会报错。重点:基本配置完成后,在增加一段代码到webpack的entry里面因为我们走的是API,而webpackDevServer里面已经提供了以方法addDevServerEntrypoints实现。只需要传递两个配置参数即可。他的效果如图,我打印出了webpackConfig说白了就是手动把热更新的两个JS文件插入到了entry中,一并打包。如果我们手动写的webpack.config.js,就应该明白这点。所以这个API还是很方便的。其实走到这里就能实现页面自动刷了。but…根据配置经验,还需要配置一个plugins.push(new webpack.HotModuleReplacementPlugin());but,根据我实际的测试结果,不用手动加入这个plugin也可以实现热更新。原因就跟我上面说的一样,API自动加上了这个配置。我们公司的脚手架没用这个API结果,导致自己额外增添了很多配置。BUT,走到这里,我们会发现只实现了第一步页面自动刷新。如果我们开发的是react应用就远远不够了。因为一旦项目大起来,刷新页面将会是一件非常非常耗时的事情,尤其是当涉及到服务器端渲染的时候。要实现类似于懒更新的功能,需要引入react-hot-loader。引入最新版本,根据文档,只需要配置两个地方即可。 脚手架的babel配置,增加一个plugin react-hot-loader/babel然后在我们的项目目录中cli-view 中包裹一层Root.jsx至此就能完美的实现开发环境的自动的更新了,更改代码,能够实现刷新当前更改的module,而不是刷新整个页面。 其实还有一个小小的疑问,在测试过程中,我即便不加上 react-hot-loader/babel这个plugin,也能够实现懒更新,只需要在项目目录中配置即可。看了下这个plugin的源码,没看出所以然来,我猜测这个plugin,是不是说懒启动的时候,保证能走一遍babel编译? 有待大佬验证!!!。顺便简单说下proxy,一般而言调用后台接口都会报跨域,但设置了proxy,类似于在node层做了一次服务转发。我把原本cli-view目录下的webpack.config.js改成了app.config.js。我把所有的配置都放在了这个文件里面。我本地启用了一个端口8888的服务,而我的cli-view的port是3000,当我请求API后,所有的/api前缀的请求都转到了8888下。到了这里关于webpack-dev-server的内容就差不多了。很细,很有意思

April 13, 2019 · 1 min · jiezi

Angular CLI 使用教程指南参考

Angular CLI 使用教程指南参考安装要安装Angular CLI你需要先安装node和npm,然后运行以下命令来安装最新的Angular CLI:注意:Angular CLI 需要Node 4.X 和 NPM 3.X 以上的版本支持。npm install -g angular-cli在 Mac 或 Linux 平台上,你可能需要添加sudo前缀提权进行全局安装:sudo npm install -g angular-cli基本用法你可以通过 Angular CLI 的 help 命令来获取相关的命令信息.ng helpAngular CLI的命令关键字为 ngng new命令描述ng new <project-name> [options]创建一个新的 Angular 项目,默认在当前所在目录下参数描述–dry-run -d只输出要创建的文件和执行的操作,实际上并没有创建项目–verbose -v输出详细信息–skip-npm在项目第一次创建时不执行任何npm命令–name指定创建项目的名称ng serveng new PROJECT_NAMEcd PROJECT_NAMEng serve将会自动在浏览器中打开默认地址 http://localhost:4200/. 运行之后如果你修改了程序源代码.应用将会自动重载.你也可以自定义配置 IP, 端口和实时重载端口号ng serve –host 0.0.0.0 –port 4201 –live-reload-port 49153ng init命令描述ng init <project-name> [options]在当前所在目录下初始化一个新的 Angular 项目参数描述–dry-run -d只输出要创建的文件和执行的操作,实际上并没有创建项目–verbose -v输出详细信息–skip-npm在项目第一次创建时不执行任何npm命令–name指定创建项目的名称ng completion命令描述ng completion将自动完成功能添加到ng命令的shell中ng doc命令描述ng doc <keyword>在浏览器中打开Angular文档并搜索当前关键字ng e2e命令描述ng e2e使用protractor在当前应用中运行e2e测试ng format命令描述ng format使用clang-format格式化当前项目代码ng generate命令描述ng generate <type> [options]在项目中构建新代码ng g <type> [options]简写支持的类型用法Componentng g component my-new-componentDirectiveng g directive my-new-directivePipeng g pipe my-new-pipeServiceng g service my-new-serviceClassng g class my-new-classInterfaceng g interface my-new-interfaceEnumng g enum my-new-enumModuleng g module my-moduleRouteng g route my-route当前已禁用构建的组件都会使用自用目录,除非 –flat 单独指定.参数描述–flat不在自用目录内创建代码–route=<route>指定父路由.仅用于生成组件和路由.默认为指定的路径.–skip-router-generation跳过生成父路由配置。只能用于路由命令。–default指定路由应为默认路由。–lazy指定路由是延迟的。 默认为true。ng get命令描述ng get <path1, path2, …pathN> [options]从Angular CLI配置获取值pathN是一个有效的JavaScript参数路径,例如“users[1].userName”。 如果未设置该值,将显示“undefined”。 此命令默认情况下仅在项目目录中工作。参数描述–global返回全局配置值,而不是本地配置值(如果都设置). 此选项还可以使命令在项目目录外工作ng set命令描述ng get <path1=value1, path2=value2, …pathN=valueN> [options]在Angular CLI配置中设置值默认情况下,如果在项目内部运行,则设置项目配置中的值,如果不在项目内部,则失败。 pathN参数是一个有效的JavaScript路径,如“users [1] .userName”。 该值将被强制转换为正确的类型,或者如果类型无法强制,则会抛出错误。参数描述–global设置全局配置值,而不是本地配置值。 这也使ng set可以在项目之外工作。ng build构建工件将存储在/dist目录中。ng build可以指定构建目标(–target = production或–target = development)和要与该构建一起使用的环境文件(–environment = dev或–environment = prod)。 默认情况下,使用开发构建目标和环境。# 这是生产构建ng build –target=production –environment=prodng build –prod –env=prodng build –prod# 这是开发构建ng build –target=development –environment=devng build –dev –e=devng build –devng buildng github-pages:deploy命令描述ng github-pages:deploy [options]构建生产应用程序,设置GitHub存储库,然后发布应用程序。参数描述–message=<message>构建并提交信息.默认为 “new gh-pages version”–environment=<env>angular 环境构建。 默认为“production”–branch=<branch-name>推送页面的git分支。 默认为“gh-branch”–skip-build在发布之前跳过构建项目–gh-token=<token>用于部署的API令牌,必须.–gh-username=<username>使用的Github用户名,必须.ng lint命令描述ng lint在项目上运行codelyzer linterng test命令描述ng test [options]使用 karma 运行单元测试参数描述–watch继续运行测试. 默认为true–browsers, –colors, –reporters, –port, –log-level这些参数直接传递给karmang version命令描述ng version输出cli版本, node 版本和操作系统信息参数描述–watch继续运行测试. 默认为true ...

April 10, 2019 · 1 min · jiezi

从零开始开发一个react脚手架(三)

前面两篇文都只是铺垫,今天至少要实现一个简单react的hello word开始之前再说一下垫片和presets。 前几天突发疑问,create-react-app是怎么做的垫片,因为很多语法或者API不一定兼容所有浏览器,所以需要有垫片(polyfill)去帮我们做兼容。我一开始以为会在entry引入@babel/polyfill,但并不是。而是引入了一个babel-preset-react-app。如果有自己写webpack配置的经验,应该明白我们配置babel的时候,引入了许多的preset和一些plugins,比如@babel/preset-env,@babel/preset-react, @babel/plugin-proposal-object-rest-spread(支持对象展开符) @babel/plugin-syntax-dynamic-import(支持异步import语法)等等,还有很多。但是这个presets集合了很多很多preset,这样我们就没必要分别引入了。可以看到这个preset里面还有 @babel/runtime 这个就是垫片库了,结合这个 @babel/plugin-transform-runtime可以做到按需加载垫片库,具体和@babel/polyfill的差别就不说了,自己去查询文章。参考create-react-app,我简化了他的N多目录和结构,去掉了很多的兼容拓展。当然先只是简单实现了一个类似于npm run start。这是我的index.js,当然以后会拆出来,作为bin目录下的一个执行文件。cli-view当执行 node ./src/cs.js,就会进行打包构建了。会自动打开默认浏览器简单说下三个文件webpackConfig,devServerConfig,config。当然了应为还只是最初版本,很多细节功能压根没有。先说下config.js这个就是单纯的cache一些环境配置,当然我仍然依赖了一下webpack.config.js,其实这个是纯粹一个暴露在项目中的配置文件,它也可以叫其他名字例如app.config.js之类的。里面就配置了一个entry。create-react-app貌似默认去的就是src下的index.jsx,其实我们也可以这样玩,但我觉得暴露个entry的入口配置,灵活性更高点。毕竟我们还可以玩多页面。至于到底怎么整,大家可以自己取舍。createWebpack.js里面就是我们平常看到的配置文件了。这里有几个细节要提一下,1 比如写配置loader的时候,我一开始直接babel-loader,报错找不到babel-loader,我估摸着是它的逻辑是走到了项目目录下去找了,而我的项目目录没有安装这个,所以我按照create-react-app的来,加上了require.resove,估计走的就是脚手架的node_modules去找babel-loader,就没报错了。 2 babel的presets我也直接用的是babel-preset-react-app(其实就是几个preset的集合)3 写脚手架的时候稍微注意下目录,别搞混了,比如process.cwd(),__dirname之类的。createDevServer就最简单了,就是一个简单的serve配置了。但后续应该还要加上代理之类的。因为现在自己手上的项目都是引入了node,用不到webpack-dev-server。我估计用这个调用后台接口的时候,要么自己起nginx项目做转发,不然就在这里设置proxy。不然跨域。虽然看起来简单,但真正自己敲着代码来,还是会发现好多细节问题。因为功能还非常不完整,比如css,热加载都还没有就暂时不放到git上了。但下一期完善功能就放上去。下一期争取做到1 完善各种webpack基本配置2 hot热加载3 试着push到npm包管理,通过npm run start来启动项目。

April 7, 2019 · 1 min · jiezi

从零开始开发一个react脚手架(二)

上一篇已经初步整了个kkk-react,这一篇不写代码,粗略讲解下create-react-app的部分源码。前沿:科普下看源码的思路。以本人看过N多源码的经验总结,想要看这种脚手架或者npm包的源码,第一步就是看package.json的配置,一般看的就是main.js和script。main.js就是引入npm包后,取的真实的js文件地址。script就是脚手架命令,类似下面create-react-app “scripts”: { “start”: “react-scripts start”, “build”: “react-scripts build”, “test”: “react-scripts test”, “eject”: “react-scripts eject” },脚手架看script,npm包看main。找到script之后,就4个命令,第一个start就是开启本地服务,build就是打包文件,test没仔细看我估计就是代码检查吧,因为我们公司的test就是eslint检查,eject类似于生成配置文件之类的,因为他的配置走的是api,不是webpack配置文件,这个命令可能就是生成出对应的webpack文件(后面的两个没细看,不必太care)。弄清script之后,就去脚手架源代码里面找package.json。去这个文件里面看bin配置,说直接一点,为嘛script里面的命令能其效果呢,就是你安装一个包之后,如果这个包里面有bin配置,那么npm就会去node_modules里面的.bin目录下生成出对应的执行命令文件作为一个脚手架工具其实是可以分为两部分的。一是生成对应的dir和file,搭建好环境,让我们能直接跑起项目。 这一部分比较简单,我们到最后再来完成这一步(等我们完成自己的脚手架工具之后),类似create-create-app myApp之类的。二我感觉才是关键,是各种命令的实现,npm run start之类,接下来我会简单的解读下这一步的源码。create-react-app用的是分包管理lerna,这里就不讲了。直接找到react-scripts目录里面的package.json,可以看到虽然script里面有4条命令,但其实就是一个react-scripts命令,后面的只是参数。所有运行的react-scripts start|test|build,其实都是在执行react-scripts.js。看源码其实解析process.arg,然后解析出参数,最后执行对应的scripts目录下的文件,我们执行的是start,就是start.js文件。接下来就是解析这个start.js源码了。这里面有很多参数判定,代理处理,各种细节处理,抛开这些,核心其实就两个函数一 createCompiler,这个就是简单点就是 new webpack(config)的实例。因为平常我们写的大部分都是配置文件,实际是执行webpack打包的时候,他也就是读取配置文件,然后new webpack(config)。二 第二个就更简单了。读取各种配置参数,起一个服务,WebpackDevServer。平常我们都是通过命令行起一个服务,然后配置webpack.config.js里面的devServer,而现在就是通过API来实现。虽然没有讲的很细,但是明显可以发现,脚手架说白一点就是通过各种api来完成我们原本需要考命令行或者配置文件来做的事情。这样更加灵活,而且复用性高,起新项目,如果差别不大,几乎可以做到零配置,这样开发者压根就不需要关心业务之外的东西下一篇就开始真正写我们的自己的脚手架了。

March 31, 2019 · 1 min · jiezi

从零开始开发一个react脚手架(一)

前沿: 脚手架工具一大堆,但如果全部用第三方的脚手架,项目做起来肯定束手束脚,想来点差异化的东西都很难,所以最好是整一份自己的脚手架工具,想咋玩咋玩。阅读了next脚手架和create-react-app脚手架源码,next脚手架太重,create-react-app好像没有服务器端渲染的内容,心血来潮自己写一个,中途会夹杂着两个脚手架工作的源码解读,只要理解了思想,就算照搬过来也是自己东西。哈哈(必然是会参考的)脚手架不难,但是涉及到源码解读,可能会分为几个章节,反正最后能实现create-react-app一模一样的效果,并且支持服务器端渲染。第一步 创建两个项目kkk-react 这个是脚手架项目,脚手架说白点就是node项目了,但要时时看效果,总不能一直publish到npm,所以需要npm link。cli-view,这个项目理论上应该是由kkk-react创建出来的,包含一些基本的文件和文件夹,并且package.json的scripts包含了start,build等构建命令。但因为是开发脚手架啊,这一步可以放到最后来弄,先把打包构建的步骤弄好。详细步骤 在kkk-react目录下, 执行npm init ,编辑package.json中的name为’kkk-react’,最后在项目根目录中 执行npm link命令。还有一些细节看截图更改package.json中的main,指向lib目录,开发阶段先这么玩,真正发布的时候,应该是新建一个bin字段,里面可以包含命令,当npm install这个脚手架的时候,这些命令生成对应的执行命令到node_modules的bin目录中,这样我们在项目中就执行了。开发的时候我们npm run dev,就能时时编译到lib目录了。至于cli-view一样的,先npm init,然后执行npm link kkk-react。创建一个cs.js引入kkk-react,就能看到效果了。可以看到引入后,通过node执行 就打印了我们在kkk-react输出的测试字段。同样的只是开发阶段这么玩,等一切都搞定了,就是通过npm run xxx,来执行对应的操作了。第一篇先这么着了,还只是试试水,争取明天出第二篇

March 26, 2019 · 1 min · jiezi

手把手带你撸一个cli工具

你有没有遇到过在没有vue-cli、create-react-app这样子的脚手架的时候一个文件一个文件的去拷贝老项目的配置文件。最近,笔者就在为组里的框架去做一套基本的cli工具。通过这边文章,笔者希望大家都能简单的去实现一个属于自己的脚手架工具。原文链接: https://juejin.im/user/57ac15…做好准备工作首先,我们需要去新建一个项目并初始化package.jsonmkdir my-cli && cd my-clinpm init然后我们需要在项目中新建bin文件夹,并将package.json中提供一个bin字段并指向我们的bin文件夹下,这样通过npm我们就可以实现指令的软链了。“bin”: { “mycli”: “bin/mycli”},在mycli中,我们要在头部增加这样一句注释,作用是"指定由哪个解释器来执行脚本"。#!/usr/bin/env nodeconsole.log(‘hello world’);接下来,全局安装我们这个包,这样我们就可以直接在本地使用mycli这个指令了。sudo npm install -g提供基本模版既然我们要去做一个初始化项目的cli,那么项目模版就必不可少了,笔者在这里提前准备了一个demo的项目目录模版,这里就不展开赘述了。编写逻辑其实核心逻辑很简单,就是通过控制台获取到用户的一些自定义选项,然后根据选项去从本地或者远程仓库拿到我们提前准备好的模版,将配置写入模版并最后拷贝模版到本地就行了。我们在src下新增creator.js文件,这个文件导出一个Creator的类。在这个类中现在仅需要三个简单的方法:init用于初始化、ask用于和命令行交互获取用户选择输入的数据、write用于调用模版的构建方法去执行拷贝文件写数据的任务。class Creator { constructor() { // 存储命令行获取的数据,作为demo这里只要这两个; this.options = { name: ‘’, description: ‘’, }; } // 初始化; init() {} // 和命令行交互; ask() {} // 拷贝&写数据; write() {}}module.exports = Creator;先去完善init方法,这个方法里我们仅需要调用ask方法和命令行交互并做一些提示即可(可以通过chalk这个库去丰富我们的命令行交互色彩)// …init() { console.log(chalk.green(‘my cli 开始’)); console.log(); this.ask();}// …接下来是ask方法,在这个方法中,我们需要根据提示引导用户输入问题并获取用户的输入,这里用到inquirer这个库来和命令行交互。// …ask() { // 问题 const prompt = []; prompt.push({ type: ‘input’, name: ’name’, message: ‘请输入项目名称’, validate(input) { if (!input) { return ‘请输入项目名称!’; } if (fs.existsSync(input)) { return ‘项目名已重复!’ } return true; } }); prompt.push({ type: ‘input’, name: ‘description’, message: ‘请输入项目描述’, }); // 返回promise return inquirer.prompt(prompt);}// …修改刚才的init方法,将ask方法改为Promise调用。init() { console.log(chalk.green(‘my cli 开始’)); console.log(); this.ask().then((answers) => { this.options = Object.assign({}, this.options, answers); console.log(this.options); });}现在我们去命令行试一下,修改bin/mycli文件,然后去运行mycli命令。#!/usr/bin/env nodeconst Creator = require(’../src/creator.js’);const project = new Creator();project.init();在和用户交互完毕并获取到数据后,我们要做的就是去调用write方法执行拷贝构建了。考虑到日后可能增加很多的模版目录,不妨我们将每一类的模版拷贝构建工作放到模版中的脚本去做,从而增大可扩展性,新增template/index.js文件。接下来首先根据项目目录结构创建文件夹(注意区分项目的执行目录和项目目录的关系)。module.exports = function(creator, options, callback) { const { name, description } = options; // 获取当前命令的执行目录,注意和项目目录区分 const cwd = process.cwd(); // 项目目录 const projectPath = path.join(cwd, name); const buildPath = path.join(projectPath, ‘build’); const pagePath = path.join(projectPath, ‘page’); const srcPath = path.join(projectPath, ‘src’); // 新建项目目录 // 同步创建目录,以免文件目录不对齐 fs.mkdirSync(projectPath); fs.mkdirSync(buildPath); fs.mkdirSync(pagePath); fs.mkdirSync(srcPath); callback();}然后回到creator.js文件,在Creator中的write调用这个方法。// …init() { console.log(chalk.green(‘my cli 开始’)); console.log(); this.ask().then((answers) => { this.options = Object.assign({}, this.options, answers); this.write(); });}// …write() { console.log(chalk.green(‘my cli 构建开始’)); const tplBuilder = require(’../template/index.js’); tplBuilder(this, this.options, () => { console.log(chalk.green(‘my cli 构建完成’)); console.log(); console.log(chalk.grey(开始项目: cd ${this.options.name } &amp;&amp; npm install)); });}// …在开启文件拷贝写数据之前,我们需要用到两个库mem-fs和mem-fs-editor,前者可以帮助我们在内存中创建一个临时的文件store,后者可以以ejs的形式去编辑我们的文件。现在constructor中初始化store。constructor() { // 创建内存store const store = memFs.create(); this.fs = memFsEditor.create(store); this.options = { name: ‘’, description: ‘’, }; this.rootPath = path.resolve(__dirname, ‘../’); this.tplDirPath = path.join(this.rootPath, ’template’);}接下来在Creator中增加两个方法copy和copyTpl分别用于直接拷贝文件和拷贝文件并注入数据。getTplPath(file) { return path.join(this.tplDirPath, file);}copyTpl(file, to, data = {}) { const tplPath = this.getTplPath(file); this.fs.copyTpl(tplPath, to, data);}copy(file, to) { const tplPath = this.getTplPath(file); this.fs.copy(tplPath, to);}然后我们根据ejs的语法修改模版中的package.json文件以实现数据注入的功能{ “name”: “<%= name %>”, “version”: “1.0.0”, “description”: “<%= description %>”, “main”: “index.js”, “scripts”: {}, “author”: “”, “license”: “ISC”}回到template/index.js中,对模版中的文件进行相应的拷贝和数据注入操作,最后打印一些可视化的信息。module.exports = function(creator, options, callback) { const { name, description } = options; // 获取当前命令的执行目录,注意和项目目录区分 const cwd = process.cwd(); // 项目目录 const projectPath = path.join(cwd, name); const buildPath = path.join(projectPath, ‘build’); const pagePath = path.join(projectPath, ‘page’); const srcPath = path.join(projectPath, ‘src’); // 新建项目目录 // 同步创建目录,以免文件目录不对齐 fs.mkdirSync(projectPath); fs.mkdirSync(buildPath); fs.mkdirSync(pagePath); fs.mkdirSync(srcPath); creator.copyTpl(‘packagejson’, path.join(projectPath, ‘package.json’), { name, description, }); creator.copy(‘build/build.js’, path.join(buildPath, ‘build.js’)); creator.copy(‘page/index.html’, path.join(pagePath, ‘index.html’)); creator.copy(‘src/index.js’, path.join(srcPath, ‘index.js’)); creator.fs.commit(() => { console.log(); console.log(${chalk.grey(创建项目: ${name})} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建目录: ${name}/build)} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建目录: ${name}/page)} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建目录: ${name}/src)} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建文件: ${name}/build/build.js)} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建文件: ${name}/page/index.html)} ${chalk.green('✔ ')}); console.log(${chalk.grey(创建文件: ${name}/src/index.js)} ${chalk.green('✔ ')}); callback(); });}执行mycli指令创建项目,一个简单的cli就完成了。结语到此,一个简单的cli就制作完成了,大家可以参考vue-cli、create-react-app等优秀的cli适当的扩展自己的cli工具。 ...

March 26, 2019 · 2 min · jiezi

vue-cli 3.0 源码分析

写在前面其实最开始不是特意来研究 vue-cli 的源码,只是想了解下 node 的命令,如果想要了解 node 命令的话,那么绕不开 tj 写的 commander.js。在学习 commander.js 过程中发现 vue-cli 的交互方式挺炫酷的,然后就去看了下源码,所以就有了这篇文章。链接vue-cli 3.0 源码分析目录@vue/cli前言介绍开始环境介绍常见 npm 包vue createcreate 入口整体分析基础验证获取预设选项(preset)依赖安装(installDeps)Generator结尾分析总结vue addadd 入口安装插件调用插件总结vue invokeinvoke 命令vue inspectinspect 命令vue serveserve 命令vue buildbuild 命令vue uiui 入口整体分析server 端client 端总结vue initinit 入口@vue/cli-init 分析vue-cli 2.x init 分析generate 函数分析总结vue configconfig 命令vue upgradeupgrade 命令vue infoinfo 命令@vue/cli-service整体介绍入口new Service()service.run()内置插件servebuildinspecthelp总结vue-cli-analysis 整个项目可大致分为两个部分,一部分是 vue 命令分析,包含 create,add,invoke, ui 等等,另一部分就是 vue-cli-service 的分析。通过分析发现与 2.X 相比,3.0 变化太大了,通过引入插件系统,可以让开发者利用其暴露的 API 对项目进行扩展。在分析之前对插件机制不是很了解,不知道如何实现的,分析之后逐渐了解了其实现机制,而且对于 vue 项目的配置也更加熟悉了。除此之外,在分析过程过程中还了解了很多有意思的 npm 包,比如 execa, debug, lowdb,lodash,inquirer 等等,最后,如果你想学习 node 命令或者想写一些比较有意思的命令行工具的话,阅读 vue-cli 源码是一个不错的选择。 ...

February 13, 2019 · 1 min · jiezi

Parcel + react + ts 的脚手架

一开始creat-react-app一直让我很舒服,不管是结合TS还是开发组件,突然有一天npm run buildCreating an optimized production build…(预计10分钟通过)然后大家提倡升级webpack,然后就开启了如履薄冰的升级之路,天天跟便秘一样直到我看到了网红打包工具Parcel(确实孤陋寡闻了),小试一下,然后就又舒服了,然后自己尝试结合 React 和 TS 弄了个脚手架,初尝Parcel(全是参考官网)1. 初始npm install parcel-bundlerparcel index.html就是这么直接并且粗暴,localhost:1234就起来了,但是为了脚手架,还是把parcel-bundle放到具体的项目里边npm install –save-dev parcel-bundlernpx parcel index.html参考: https://parceljs.org/getting_...2. 结合Reactnpm install –save reactnpm install –save react-domnpm install –save-dev babel-preset-react然后去package.json里边配置命令"scripts": { “start”: “npx parcel index.html”}依旧简单粗暴参考: https://parceljs.org/recipes....3. 结合TypeScript结合TS稍微麻烦一点,但是这跟parcel没关系,是TS自己事儿多npm install –save-dev typescriptnpm install –save-dev @types/reactnpm install –save-dev @types/react-dom然后按照惯例构建tsconfig.json文件就可以了,简单粗暴我的package.json顺便加了tslint和sass+postCSS “dependencies”: { “react”: “^16.2.0”, “react-dom”: “^16.2.0”, }, “devDependencies”: { “sass”: “^1.15.2”, “autoprefixer”: “^9.4.3”, “postcss-modules”: “^1.4.1”, “@types/react-dom”: “^16.0.3”, “@types/node”: “^10.12.18”, “@types/react”: “^16.7.18”, “babel-preset-react”: “^6.24.1”, “parcel-bundler”: “^1.0.3”, “tslint”: “^5.12.0”, “tslint-config-airbnb”: “^5.11.1”, “typescript”: “^3.2.2” }广告自己弄了一个简陋的脚手架github: https://github.com/ZJBC/react...npm: https://www.npmjs.com/package…支持 组件开发 和 应用开发 两种模式,????️????️ ...

January 29, 2019 · 1 min · jiezi

一些在Emacs中搜索文本的方法

在Emacs中写代码的时候,常常需要查找一个函数、方法,或者变量的定义。如果是正在写Common Lisp,那么SLIME已经配置好了相应的快捷键M-.,只需要将光标移动到要查看的函数、方法,或者变量的名字上,按下M-.便可以跳转过去——再按一下M-,还能回到原来的位置。如果是写其它语言的代码,很多时候都没办法方便地跳转过去,这时候就需要依赖于文本搜索了,这也是本篇所要讲述的主题。通常情况下,用C-s和C-r就足够了——一个负责“往下”搜索一个负责“往上”搜索。尤其在安装了Emacs的插件swiper之后,只需使用C-s便可以同时查看到上下两个方向的匹配文本。但C-s也有其局限性。例如,它不能跨文件搜索,如果要查看的函数、方法,或者变量的定义不在当前buffer中,就不得不手动在多个buffer间切换并频繁按下C-s了。有多种办法可以解决上面这种问题。例如,可以用Emacs的projectile-ag。通常,如果代码散布在多个源文件中,那么它们多半是放在一个项目中——比如一个Git仓库。打开位于项目中的文件时,Emacs的projectile-mode就会启动。此时,按下C-c C-p s s这套组合键,会调用projectile-ag函数。projectile-ag会在minibuffer中等候输入要搜索的内容,按下回车后,Emacs会调用命令行工具ag来搜索这个项目下的所有文件,找出匹配关键字的行并显示。projectile-ag函数会打开另一个buffer来展示搜索结果,一个示例如下ag –literal –group –line-number –column –color –color-match 30;43 –color-path 1;32 –smart-case –stats – emacs .0 matches0 files contained matches36 files searched111365 bytes searched0.007795 seconds使用projectile-ag的前提是要搜索的文件都在同一个一个项目中,但并非所有时候都满足这个要求。这时,可以用Emacs的find-grep函数。find-grep函数调起后同样要求使用者在minibuffer输入内容,但它更原始一点光标会定位在-e选项之后,需要填补交给grep的正则表达式。由于minibuffer中给出的是完整的、将会被运行的命令,因此可以也给find命令添加一些选项和参数,来改变搜索行为。如果是在一个Node.js项目中搜索,一般还要让find忽略一些文件,如node_modules目录下的大量依赖,或者构建产生出来的.css和.js文件。这些文件中的行不仅很可能会命中输入的正则表达式,还极可能成片地出现,占据搜索结果中的半壁江山。除了grep之外,还有许多命令行的文本搜索工具,例如ack和rg,并且它们都称自己更快。要在Emacs中使用它们也很简单,尤其是后者还有相应的插件rg.el可以方便调起。如果经常要控制find来忽略node_modules,可以考虑用git-grep。man git-grep中说到,它只会搜索tracked的文件node_modules一般都不会被git跟踪,自然也就不会被搜索。全文完

January 11, 2019 · 1 min · jiezi

从 1 到完美,用 node 写一个命令行工具

从 1 到完美,用 node 写一个命令行工具1. package.json 中的 bin 字段现在,不管是前端项目还是 node 项目,一般都会用 npm 做包管理工具,而 package.json 是其相关的配置信息。对 node 项目而言,模块导出入口文件由 package.json 的 main 字段指定,而如果是要安装到命令行的工具,则是由 package.json 的 bin 字段指定。1.1 配置单个命令与包名同名{ “name”: “pro”, “bin”: “bin/pro.js”}这样安装的命令名称就是 pro。自定义命令名称(与包名不同名){ “name”: “pro-cli”, “bin”: { “pro”: “bin/pro.js” }}这样安装的命令名称也是 pro。1.2 配置多个命令{ “name”: “pro-cli”, “bin”: { “pro”: “bin/pro.js”, “mini”: “bin/mini.js” }}这样安装就有 pro 与 mini 两个命令。2. 对应 bin/pro.js 文件的写法#!/usr/bin/env noderequire(’../lib/pro’);与普通的 js 文件写法一样,只是前面要加上 #!/usr/bin/env node。这段前缀代码叫 shebang,具体可以参考 Shebang (Unix) - Wikipedia).3. 安装方式3.1 全局安装npm i -g pro-cli这种安装方式可以在命令行全局使用。pro devpro build3.2 本地安装npm i –save-dev pro-cli这种安装方式需要配合 npm 一起使用,比如:# package.json{ “scripts”: { “dev”: “pro dev”, “build”: “pro build” }}# 使用npm run devnpm run build4. 选择合适的命令行封装库一般来说,一个命令都会有如下的一些参数:-v, –version 或 -V, –version: 查看版本号-h, –help: 查看帮助信息如果完全自己来写的,就会很麻烦,尤其是帮助信息。所以,选择一个好的命令行封装库,能够帮我们省去很多工作。用的比较多的:commander.jsyargsmeow以 commander.js 为例:4.1 安装npm install commander –save4.2 注册const commander = require(‘commander’);注册版本号与描述commander .version(‘0.0.1’) .description(‘A cli application named pro’);注册参数(非子命令参数)commander .option(’-p, –peppers’, ‘Add peppers’) .option(’-P, –pineapple’, ‘Add pineapple’) .option(’-b, –bbq-sauce’, ‘Add bbq sauce’) .option(’-c, –cheese [type]’, ‘Add the specified type of cheese [marble]’, ‘marble’)注册子命令commander .command(‘rm <dir>’) .option(’-r, –recursive’, ‘Remove recursively’) .action((dir, cmd) => { console.log(‘remove ’ + dir + (cmd.recursive ? ’ recursively’ : ‘’)) })解析commander.parse(process.argv);4.3 使用查看版本号pro -Vpro –version# 打印结果0.0.1运行 rm 子命令pro rm dir查看帮助(commander 会自动生成)pro -hpro –help# 打印结果Usage: pro [options]A cli application named proOptions: -h, –help output usage information -V, –version output the version number -p, –peppers Add peppers -P, –pineapple Add pineapple -b, –bbq Add bbq sauce -c, –cheese <type> Add the specified type of cheese [marble] -C, –no-cheese You do not want any cheese更多用法查看 commander.js。5. 常用的命令行相关工具库5.1 minimist: 解析命令行的参数var argv = require(‘minimist’)(process.argv.slice(2));console.dir(argv);$ node example/parse.js -a beep -b boop{ _: [], a: ‘beep’, b: ‘boop’ }$ node example/parse.js -x 3 -y 4 -n5 -abc –beep=boop foo bar baz{ _: [ ‘foo’, ‘bar’, ‘baz’ ], x: 3, y: 4, n: 5, a: true, b: true, c: true, beep: ‘boop’ }更多参考 minimist。5.2 chalk: 让命令行的字符带上颜色更多参考 chalk。5.3 Inquirer.js: 让命令行与用户进行交互,如输入、选择等更多参考 Inquirer.js。5.4 shelljs: 跨平台 Unix shell 命令 的 node 封装var shell = require(‘shelljs’);if (!shell.which(‘git’)) { shell.echo(‘Sorry, this script requires git’); shell.exit(1);}// Copy files to release dirshell.rm(’-rf’, ‘out/Release’);shell.cp(’-R’, ‘stuff/’, ‘out/Release’);// Replace macros in each .js fileshell.cd(’lib’);shell.ls(’*.js’).forEach(function (file) { shell.sed(’-i’, ‘BUILD_VERSION’, ‘v0.1.2’, file); shell.sed(’-i’, /^.REMOVE_THIS_LINE.$/, ‘’, file); shell.sed(’-i’, /.REPLACE_LINE_WITH_MACRO.\n/, shell.cat(‘macro.js’), file);});shell.cd(’..’);// Run external tool synchronouslyif (shell.exec(‘git commit -am “Auto-commit”’).code !== 0) { shell.echo(‘Error: Git commit failed’); shell.exit(1);}更多参考 shelljs。5.5 blessed-contrib: 命令行图表更多参考 blessed-contrib。5.6 cash: 跨平台 linux 命令 的 node 封装与 shelljs 功能差不多。const $ = require(‘cash’);const out = $.ls(’.’, {l: true});更多参考 cash。5.7 prompts: 又一个让命令行与用户进行交互的工具与 Inquirer.js 功能差不多。更多参考 prompts。5.8 ora: 命令行加载中图标更多参考 ora。5.9 progress: 命令行进度条downloading [===== ] 39/bps 29% 3.7s更多参考 progress。5.10 更多更多关于命令行的工具库可以参考 command-line-utilities。6. 比较常用的命令行 APP命令行相关的应用就很多啦,比如 babel、webpack、rollup、eslint 等,但这些不仅仅是命令行工具。下面介绍一些纯命令行应用:vtop: 美美的 linux top 命令界面speed-test: 测试网络链接速度http-server: 零配置启动一个 http 服务器fkill-cli: 跨平台 kill 命令更多纯命令行应用可以参考 command-line-apps。后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) ...

September 29, 2018 · 2 min · jiezi