挑战21天手写前端框架 day4 框架的本质是命令行工具?

1,266 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

阅读本文需要 7 分钟,编写本文耗时 2.5 小时。

其实现代前端框架的本质就是一个使用 js 编写的 node 命令行工具(cli)

比如 umi、 react-script、vue-cli、ng、ionic 等,我们在使用他们进行项目开发的时候,都是使用如 umi dev 的命令去启动我们的开发服务,这里有两个关键字,“启动” 和 “服务”。

关于开发服务,我们会在明天的内容中说明,今天我先简单的介绍一下 cli 的开发过程。

使用 node 执行 js

新建一个 js 文件 src/index.js

console.log('Hello Malita!');

在终端中执行 node src/index.js

Hello Malia!

我们可以看到在控制态中打印了 Hello Malia!

使用命令

每次执行都要输入 node src/index.js 有些太长了,而且有时候文件名字取的复杂了,也很不好记忆,因此我们可以在 package.json 中增加使用脚本入口 scripts

    "scripts": {
        "hello": "node src/index.js"
    },

这样我们只需要在终端中使用 npm run hello 的命令,就相当于 node src/index.js

值得注意的是 hello 只是我们取的速记别名,你可以使用任意的别名,所以在使用 scripts 的时候,我们需要关注的是别名真正对应的脚本。

这个是在当前项目中有效的命令,那如果是不在当前项目中的命令,而是在第三方依赖中的命令的话,又该如何声明呢?

声明自己的命令

声明命令不是什么黑魔法,这就是 node 的用法说明,只要在 package.json 里面配置上 bin 属性就可以实现。

    "bin": {
        "malita": "./bin/malita.js"
    },

编写 bin/malita.js

注意开头要添加脚本的解释程序,比如我们这里使用的是 node

#!/usr/bin/env node

console.log('Hello Malia!')

bin 中配置的 malita 就是我们提供给其他用户的命令速记符号。

这个“速记符”是提供给其他用户使用的,我们本地开发的时候,可以使用 package.jsonscripts 中的命令即可。

使用 link 调试

bin 声明的命令需要被安装到 node_modules.bin 目录下才能找到,比如我们在当前项目直接执行 malita 会提示我们 sh: malita: command not found

因此我们可以新建一个测试仓库,来关联测试。

$ mkdir examples/app
$ cd examples/app
$ npm init -y
$ npm link malita
$ npx malita

Hello Malia!

npx 是 npm 自带的一个工具,它会自动查找当前依赖包中的可执行文件,如果没找到,就会找你全局安装的命令,如果还是没找到,那它就会从远程服务器上下载你需要的依赖。

如果你按上面的步骤执行,看到控制台有正确打印,说明你的配置都是正确的。这样你就可以继续阅读下面的内容了。

使用 pnpm monorepo 自动 link

平时开发测试都要手动 link 比较麻烦,有时候还容易忘记执行 link,而且由于 link 的绑定与你执行 link 的目录相关的。像我有个同名包,有多个版本的本地仓库,我经常忘记我当前 link 的哪个仓库。所以我们需要引入一种新的方式,来帮助我们自动执行 link,我们称之为 workspace

我们使用最近比较流行的 pnpm 来完成。

你可以通过命令安装 pnpm

curl -fsSL https://get.pnpm.io/install.sh | PNPM_VERSION=7.0.0-rc.2 sh -

更多安装方法,请查看 pnpm 官网

新建配置文件 pnpm-workspace.yaml

packages:
  - 'packages/*'
  - 'examples/*'

声明我们的 packagesexamples 下的包,都是我们的 workspace 。在这里面的所有子包,都可以被整个工作区中的其他包引用。 就算 npm 上不存在我们的子包,只要我们的工作区内存在,就不会出现找不到子包的问题。 还有一个好处就是不管 npm 上我们的子包是什么版本,开发的时候,我们都可以直接使用我们本地的代码。

不像单包管理那样,还要等子包修改完,编译构建上传到 npm 之后,我们再下载使用它。

新建 packages/malita 文件夹,将我们前面创建的 binsrcpackage.json 移动到这里面。

修改根目录下的 package.json ,增加 "private": true,

examples/app 中使用 malita

{
    "name": "@examples/app",
    "scripts": {
        "start": "malita"
    },
    "dependencies": {
        "malita": "workspace:*"
    }
}

然后在根目录下执行安装,并启动用例,查看效果

$ pnpm i
// 等待安装完成
$ cd examples/app
$ pnpm start

Hello Malia!

如果控制台成功打印,那说明你前面的步骤都正确执行了。

到此处,你应该要很清晰的知道,上面这个打印是如何被执行的,如果你有疑问或者疑惑,建议你再回头看看文章,或者在评论区讨论

编写帮助命令

前面讲了这么多,只是为了让读者清晰的知道整个代码逻辑是如何被执行的,从现在开始我们才真正的进入框架的开发工作中。

命令行开发毕竟不是我们的重点,所以这里我们使用一个 完成的 node 命令行解决方案 commander.js

commander

$ cd packages/malita/  
$ pnpm i commander

dependencies:
+ commander 9.1.0

编写我们的入口文件 bin/malita.js

#!/usr/bin/env node

const {
    program
} = require('commander');

program
    .version(require('../package.json').version, '-v, -V', '输出当前框架的版本')
    .description('这是21天短文,挑战手写前端框架的产物框架')
    .usage('<command> [options]')
    .parse(process.argv)

在用例中执行

$ cd examples/app
$ npx malita -V

0.0.1

增加 help 命令

program.command('help')
    .alias('-h')
    .description('帮助命令')
    .action(function(name, other) {
        console.log(`
这是21天短文,挑战手写前端框架的产物框架 malita

支持的命令:
  version, -v,-V 输出当前框架的版本
  help,-h 输出帮助程序

Example call:
    $ malita <command> --help`)
    }).parse(process.argv);

在用例中执行

$ cd examples/app
$ npx malita help

这是21天短文,挑战手写前端框架的产物框架 malita

支持的命令:
  version, -v,-V 输出当前框架的版本
  help,-h 输出帮助程序

Example call:
    $ malita <command> --help

将新的框架版本发布到 npm 上

$ cd packages/malita 
$ npm publish

npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npmjs.org/malita - You cannot publish over the previously published versions: 0.0.1.

这个错误是说已经存在一个这个版本的包了,npm 上的包的每一次更新都需要对应一次版本号的提升,所以我们修改 packages/malita/package.json 中的 "version": "0.0.2", 之后再次执行发包

$ cd packages/malita 
$ npm publish

npm notice 
npm notice 📦  malita@0.0.2
npm notice === Tarball Contents === 
npm notice 705B bin/malita.js
npm notice 581B package.json 
npm notice 32B  src/index.js 
npm notice === Tarball Details === 
npm notice name:          malita                                  
npm notice version:       0.0.2                                   
npm notice filename:      malita-0.0.2.tgz                        
npm notice package size:  800 B                                   
npm notice unpacked size: 1.3 kB                                  
npm notice shasum:        60c926f7714570a9a5228179d5a3df383680bc70
npm notice integrity:     sha512-VifOKdtdspHlS[...]dyA+jFEwR9Nhw==
npm notice total files:   3                                       
npm notice 
+ malita@0.0.2

到这里,你就成功的在 npm 上发布了一个拥有一个 help 命令的 cli 工具了,后续我们会完善它,使它成为一个基本可用的前端框架。

本篇文章旨在帮助你了解前端框架命令的执行流程,这在你遇到 Bug ,需要定位问题的时候,你就能有一个很清晰的定位步骤。 当你知道它是如何执行的,你甚至可以根据这个思路,去阅读你现在项目中所使用的前端框架的源码,比如 vue-cli 或者 umi。

今天的内容就到这里了,明天就会真正进入我们的框架开发工作中。前期我总共花了 4 天的篇幅来详细的说明了,编写框架的基础知识,有大部分的内容都属于常识性的知识点。 正所谓磨刀不误砍柴工,希望这些简单常识,对你有所帮助。

感谢观看,如果你有任何疑问,欢迎在评论区和我互动。

源码归档