import-local 执行流程
作用:当前项目中的 node_modules
中存在一个脚手架命令,和全局的 node
环境中也存在一个脚手架命令的时候,它会优先选用 node_modules
中的本地版本。
// corelernacli.js
// 引入 import-local
const importLocal = require("import-local");
// 判断 importLocal(__filename) 为 true 的时候 会输出一行 log
// 判断本地 `node_modules` 中是否存在脚手架
// __filename 就是当前文件所在的地址 D:lerna-maincorelernacli.js
if (importLocal(__filename)) {
// 输出使用本地版本的log
require("npmlog").info("cli", "using local version of lerna");
} else {
require(".")(process.argv.slice(2));
}
查看 import-local
的源码,源码看起来并不是很多
// node_modulesimport-localindex.js
const path = require('path');
const resolveCwd = require('resolve-cwd');
const pkgDir = require('pkg-dir');
module.exports = filename => {
// filename 脚手架所在全局的执行文件
// 获取脚手架所在的全局目录,包含 package.json 的目录
// 如果当前模块嵌套比较深 会逐层向上找 找到包含 package.json的目录
const globalDir = pkgDir.sync(path.dirname(filename));
// 将 globalDir 和 filename 进行相对路径比较 得到最后的cli.js
const relativePath = path.relative(globalDir, filename);
// 将 globalDir 和 package.json 的路径进行合并 , 并拿到 package.json 文件的内容
const pkg = require(path.join(globalDir, 'package.json'));
// 将 pkg.name 和 relativePath 进行了合并 得到lerna/cli.js
// 然后调用 resolveCwd 判断当前项目的本地有没有这个模块 返回本地模块所在的路径
const localFile = resolveCwd.silent(path.join(pkg.name, relativePath));
// 如果模块存在 加载模块 否则返回 null
return localFile && path.relative(localFile, filename) !== '' ? require(localFile) : null;
};
可以看到 import-local
模块就是一个函数,当条件满足的时候,会先执行函数内部的 require(localFile)
,然后再返回来执行
require("npmlog").info("cli", "using local version of lerna");
那为什么 using local version of lerna
会在最前面打印出来呢,因为前一篇文章中提到,脚手架 command
内部的实现是利用了微任务队列,虽然先执行了 require(localFile)
,但是真正的执行逻辑都放在微任务队列里了,而 using local version of lerna
是宏任务最后的内容,所以会在执行宏任务的时候输出 log
,然后再执行微任务队列里脚手架实际的业务逻辑代码。
基于 Lerna 设计简历
完全掌握本章以后可在简历中增加
注:第二周
4-12 ~ 4-18
没看懂,后面需要再复习一下,但是不影响主课程的进行
- 熟悉
yargs
脚手架开发框架 - 熟悉多
package
管理工具lerna
的使用方法和实现原理 - 深入理解
node.js
模块路径解析流程
如何使用 yargs 开发一个脚手架
先讲一下脚手架构成,以 vue-cli
为例,最基本的命令 vue create project --force
- bin:
package.json
中配置的bin
属性,可以理解为主命令,也就是vue
,本地开发的时候通过npm link
进行本地安装。 - 需要在
bin
指向的文件,也就是脚手架的可执行文件中添加#!/usr/bin/env node
,告诉操作系统在环境变量中查询node
,并通过node
来执行此文件。 - command:命令,也就是例子中的
create
。 - param:参数,也就是例子中的
project
。 - option:参数也可以携带选项,比如例子中的
--force
。
然后说一下脚手架初始化流程
- 调用构造函数生成一个脚手架:
Yargs()
- 调用
yargs
常用方法,对脚手架的功能进行增强Yargs.options
Yargs.group
... ...
- 解析脚手架的参数
- 利用
yargs/helpers
提供的hideBin
,调用Yargs(hideBin(process.argv)).argv
完成解析 Yargs.parse(argv,options)
- 利用
- 注册脚手架命令
Yargs.command(command,describe,builder,handler)
Yargs.command({command,describe,builder,handler})
lerna 的实现原理是什么
lerna
是基于 git npm
的多 package
项目管理工具
Lerna
的用法
- Lerna init
- Lerna create
- Lerna add
- Lerna link
- ... ...
Lerna
的实现原理
- 通过
import-local
优先调用本地lerna
命令。 - 通过
Yargs
生成脚手架,先注册全局属性,再注册命令,最后通过parse
方法解析参数。 Lerna
命令注册时需要传入builder
和handler
两个参数,builder
方法用于注册命令专属的options
,handler
用来处理命令的业务逻辑。Lerna
通过配置npm
本地依赖的方式来进行本地开发,具体写法是在package.json
的依赖中写入:file:your-local-module-path
,在lerna publish
的时候会自动将改路径替换成线上路径。
node.js 模块路径解析流程
... ...loading