开源脚手架lerna源码分析
学习目标
- Lerna源码结构和执行流程分析
import-local
源码深度精读
学习收获
-
如何将源码分析的收获写进简历
-
学习明星项目的架构设计
-
获得脚手架执行流程的一种思路
-
脚手架调试本地源码的另外一种方法
-
Nodejs加载node_modules模块的流程(罕见)
-
各种文件操作算法和最佳实践
学习建议:需要具备一定的Node基础
源码学习
1.准备源码
源码阅读准备完成的标准(重点):
-
找到入口文件
-
能够本地调试
-
调试按钮:
-
Step Over 一行行向下执行
-
Step Into 进入到函数里面去执行
-
Step Out 跳出函数去执行
-
-
2.源码分析
-
import-local
库分析作用:当我们本地node_modules存在一个脚手架命令,同时全局在node_modules中也存在这个脚手架命令的时候,优先选用本地node_modules中的版本
last、知识点
-
require(".")
是什么意思?require("."),其实
.
就等同于./
,代表当前目录,../
就代表上一个目录,如果只有.
,没有后面的文件,则默认文件是index.js
所以,这里的
require(",")
就是require("./index.js")
-
require
是同步还是异步,require
是编译执行还是运行时执行,import
呢?require
是编译时加载 -
"@lerna/global-options":"file:../global-options"
——package.json
中存在这种依赖是什么意思?(重点,本地调试推荐使用)"dependencies": { @lerna/global-options":"file:../global-options" }
这种写法代表加载的是本地的依赖,依赖的相对路径为
../global-options
这个包这种本地调试的方法,非常方便,不需要
npm link
****,但是需要install
这种方法在本地调试可以使用,发布后,无法找到,lerna框架专门写了一个解析本地路径成远程库连接的方法来解决
-
如果一个函数的参数,使用的是反引号,那么可以不写括号,比如
const dedent = require("dedent") console.log(dedent` 111 111`) /* 这里就相当于dedent(` 111 111`) */
-
require("process")
可以不写require
,两者等价 -
constructor.name
是什么?- 就是这个类的名称
class Command{ constructor(){ console.log(this.constructor.name) // } } new Command() // 这时候this.constructor.name就是"Command"
class Command{ constructor(){ console.log(this.constructor.name) } } class ListCommand extends Command{ } new ListCommand() // 这时候this.constructor.name就是"ListCommand"
-
__filename
与__dirname
的区别__filename
当前文件的路径__dirname
当前文件所在文件夹的路径path.dirname(文件或文件夹)
文件或文件夹所在父级文件夹的路径- 所以:
path.dirname(__filename)
等同于__dirname
-
node加载一个模块的时候,会向node注入哪些变量?
等同于:require一个模块的时候,会注入哪些变量?
等同于:require源码有哪些变量?
- 总共有5个?
- module
- require
- __filename
- __dirname
- exports方法,因为要对外去输出
- 总共有5个?
-
module.exports
暴露多个方法:// cwd.js module.exports = cwd => {} // 暴露1 module.exports.sync = cwd => {} // 暴露2 module.exports.dir = cwd => {} // 暴露3
- 使用:
const findUp = require('cwd.js') findUp() // 执行暴露1 findUp.sync() // 执行暴露2 findUp.dir(); // 执行暴露3
-
path.resolve(a, b, c, ...)
- 用于**
当前路径
与cd命令
的合并**,相当于先获取当前路径,然后cd a
,再cd b
,然后cd c
,…,最终返回的是绝对路径,最后一个是文件的话就拼起来
- 用于**
-
path.join('/user','/yh','..')
- 用于路径简单合并,相当于先将
/user
和/yh
合并成/user/yh
,然后合并..
对于这种点,用cd ..
,那么结果就是/user
- 练习
path.join('/user','/yh','../a')结果是什么?
- 答案:
/user/a
- 答案:
- 练习
- 用于路径简单合并,相当于先将
-
path.dirname(相对或绝对路径)
- 用于获取父级目录
- 如果是对相对路径处理,返回的是相对路径
- 如果是对绝对路径处理,返回的是绝对路径
- 总之:
- 值是什么,去掉最后一级即可
- 特殊:值为"…/“或”./“则结果为”."
- 用于获取父级目录
-
path.parse(路径)
- 返回:
root
根路径dir
父文件夹路径base
最后一级路径ext
后缀,文件有后缀,文件夹没后缀为空字符串name
最后一级的文件名(不带后缀)或文件夹名
- 举例:
path.parse("/a/b/c.js")
- 答案:
{ root: '/', dir: '/a/b', base: 'c.js', ext: '.js', name: 'c' }
- 答案:
- 返回:
-
fs.access(路径)
和fs.accessSync(路径)
-
用于判断
路径
是否存在,这就可以判断文件或文件夹是否存在了 -
举例:
// 异步判断路径是否存在 fs.access("./index.js", (err) => { console.log(err) }) // 同步判断路径是否存在 try { fs.accessSync("./index.js") } catch (err) { console.log(err) }
-
同步的错误要用
try{...}catch(err){...}
捕获
-
-
fs.exists(path, callback)
与fs.existsSync(path)
- 与
access
通用的效果 - 但是,异步方法
fs.exists(path, callback)
已经弃用,以后就不要再使用了。同步的还可以使用 - 总的来说,建议使用
access
方法
- 与
-
如果要判断路径是否存在,建议使用
path-exists
库,这个库会帮我们做维护,哪些方法弃用,哪些没弃用,会维护 -
path.isAbsolute(路径)
判断是否为绝对路径,返回布尔值