湖州品牌网站建设,百度云 做视频网站,国际最新十大新闻事件,文旅策划公司1. 前言大家好#xff0c;我是若川。最近组织了源码共读活动#xff0c;感兴趣的可以加我微信 ruochuan12想学源码#xff0c;极力推荐之前我写的《学习源码整体架构系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、… 1. 前言大家好我是若川。最近组织了源码共读活动感兴趣的可以加我微信 ruochuan12想学源码极力推荐之前我写的《学习源码整体架构系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、vue-next-release、vue-this、create-vue等十余篇源码文章。本文仓库 ni-analysis求个star^_^[1]最近组织了源码共读活动之前写了 Vue3 相关的两篇文章。初学者也能看懂的 Vue3 源码中那些实用的基础工具函数Vue 3.2 发布了那尤雨溪是怎么发布 Vue.js 的文章里都是写的使用 yarn 。参加源码共读的小伙伴按照我的文章却拉取的最新仓库代码发现 yarn install 安装不了依赖向我反馈报错。于是我去 github仓库 一看发现尤雨溪把 Vue3仓库 从 yarn 换成了 pnpm[2]。贡献文档[3]中有一句话。We also recommend installing ni[4] to help switching between repos using different package managers. ni also provides the handy nr command which running npm scripts easier.我们还建议安装 ni[5] 以帮助使用不同的包管理器在 repos 之间切换。ni 还提供了方便的 nr 命令可以更轻松地运行 npm 脚本。这个 ni 项目源码虽然是 ts没用过 ts 小伙伴也是很好理解的而且主文件其实不到 100行非常适合我们学习。阅读本文你将学到1. 学会 ni 使用和理解其原理
2. 学会调试学习源码
3. 可以在日常工作中也使用 ni
4. 等等2. 原理github 仓库 ni#how[6]ni 假设您使用锁文件并且您应该在它运行之前它会检测你的 yarn.lock / pnpm-lock.yaml / package-lock.json 以了解当前的包管理器并运行相应的命令。单从这句话中可能有些不好理解还是不知道它是个什么。我解释一下。使用 ni 在项目中安装依赖时假设你的项目中有锁文件 yarn.lock那么它最终会执行 yarn install 命令。假设你的项目中有锁文件 pnpm-lock.yaml那么它最终会执行 pnpm i 命令。假设你的项目中有锁文件 package-lock.json那么它最终会执行 npm i 命令。使用 ni -g vue-cli 安装全局依赖时默认使用 npm i -g vue-cli当然不只有 ni 安装依赖。还有 nr - runnx - executenu - upgradenci - clean installnrm - remove我看源码发现ni相关的命令都可以在末尾追加\?表示只打印不是真正执行。所以全局安装 ni 后可以尽情测试比如 ni \?nr dev --port3000 \?因为打印所以可以在各种目录下执行有助于理解 ni 源码。我测试了如下图所示命令测试图示假设项目目录下没有锁文件默认就会让用户从npm、yarn、pnpm选择然后执行相应的命令。但如果在~/.nirc文件中设置了全局默认的配置则使用默认配置执行对应命令。Config; ~/.nirc; fallback when no lock found
defaultAgentnpm # default prompt; for global installs
globalAgentnpm因此我们可以得知这个工具必然要做三件事1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm
2. 抹平不同的包管理器的命令差异
3. 最终运行相应的脚本接着继续看看 README 其他命令的使用就会好理解。3. 使用看 ni github文档[7]。npm i in a yarn project, again? F**k!ni - use the right package manager全局安装。npm i -g antfu/ni如果全局安装遭遇冲突我们可以加上 --force 参数强制安装。举几个常用的例子。3.1 ni - installni# npm install
# yarn install
# pnpm installni axios# npm i axios
# yarn add axios
# pnpm i axios3.2 nr - runnr dev --port3000# npm run dev -- --port3000
# yarn run dev --port3000
# pnpm run dev -- --port3000nr
# 交互式选择命令去执行
# interactively select the script to run
# supports https://www.npmjs.com/package/npm-scripts-info conventionnr -# 重新执行最后一次执行的命令
# rerun the last command3.3 nx - executenx jest# npx jest
# yarn dlx jest
# pnpm dlx jest4. 阅读源码前的准备工作4.1 克隆# 推荐克隆我的仓库我的保证对应文章版本
git clone https://github.com/lxchuan12/ni-analysis.git
cd ni-analysis/ni
# npm i -g pnpm
# 安装依赖
pnpm i
# 当然也可以直接用 ni# 或者克隆官方仓库
git clone https://github.com/vuejs/ni.git
cd ni
# npm i -g pnpm
# 安装依赖
pnpm i
# 当然也可以直接用 ni众所周知看一个开源项目先从 package.json 文件开始看起。4.2 package.json 文件{name: antfu/ni,version: 0.10.0,description: Use the right package manager,// 暴露了六个命令bin: {ni: bin/ni.js,nci: bin/nci.js,nr: bin/nr.js,nu: bin/nu.js,nx: bin/nx.js,nrm: bin/nrm.js},scripts: {// 省略了其他的命令 用 esno 执行 ts 文件// 可以加上 ? 便于调试也可以不加// 或者是终端 npm run dev \?dev: esno src/ni.ts ?},
}根据 dev 命令我们找到主入口文件 src/ni.ts。4.3 从源码主入口开始调试// ni/src/ni.ts
import { parseNi } from ./commands
import { runCli } from ./runner// 我们可以在这里断点
runCli(parseNi)找到 ni/package.json 的 scripts把鼠标移动到 dev 命令上会出现运行脚本和调试脚本命令。如下图所示选择调试脚本。VSCode 调试VSCode 调试 Node.js 说明5. 主流程 runner - runCli 函数这个函数就是对终端传入的命令行参数做一次解析。最终还是执行的 run 函数。对于 process 不了解的读者可以看阮一峰老师写的 process 对象[8]// ni/src/runner.ts
export async function runCli(fn: Runner, options: DetectOptions {}) {// process.argv返回一个数组成员是当前进程的所有命令行参数。// 其中 process.argv 的第一和第二个元素是Node可执行文件和被执行JavaScript文件的完全限定的文件系统路径无论你是否这样输入他们。const args process.argv.slice(2).filter(Boolean)try {await run(fn, args, options)}catch (error) {// process.exit方法用来退出当前进程。它可以接受一个数值参数如果参数大于0表示执行失败如果等于0表示执行成功。process.exit(1)}
}我们接着来看run 函数。6. 主流程 runner - run 主函数这个函数主要做了三件事1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm - detect 函数
2. 抹平不同的包管理器的命令差异 - parseNi 函数
3. 最终运行相应的脚本 - execa 工具// ni/src/runner.ts
// 源码有删减
import execa from execa
const DEBUG_SIGN ?
export async function run(fn: Runner, args: string[], options: DetectOptions {}) {// 命令参数包含 问号? 则是调试模式不执行脚本const debug args.includes(DEBUG_SIGN)if (debug)// 调试模式下删除这个问号remove(args, DEBUG_SIGN)// cwd 方法返回进程的当前目录绝对路径let cwd process.cwd()let command// 支持指定 文件目录// ni -C packages/foo vite// nr -C playground devif (args[0] -C) {cwd resolve(cwd, args[1])// 删掉这两个参数 -C packages/fooargs.splice(0, 2)}// 如果是全局安装那么实用全局的包管理器const isGlobal args.includes(-g)if (isGlobal) {command await fn(getGlobalAgent(), args)}else {let agent await detect({ ...options, cwd }) || getDefaultAgent()// 猜测使用哪个包管理器如果没有发现锁文件会返回 null则调用 getDefaultAgent 函数默认返回是让用户选择 promptif (agent prompt) {agent (await prompts({name: agent,type: select,message: Choose the agent,choices: agents.map(value ({ title: value, value })),})).agentif (!agent)return}// 这里的 fn 是 传入解析代码的函数command await fn(agent as Agent, args, {hasLock: Boolean(agent),cwd,})}// 如果没有命令直接返回上一个 runCli 函数报错退出进程if (!command)return// 如果是调试模式那么直接打印出命令。调试非常有用。if (debug) {// eslint-disable-next-line no-consoleconsole.log(command)return}// 最终用 execa 执行命令比如 npm i// https://github.com/sindresorhus/execa// 介绍Process execution for humansawait execa.command(command, { stdio: inherit, encoding: utf-8, cwd })
}我们学习完主流程接着来看两个重要的函数detect 函数、parseNi 函数。根据入口我们可以知道。runCli(parseNi)run(fn)这里 fn 则是 parseNi6.1 根据锁文件猜测用哪个包管理器npm/yarn/pnpm - detect 函数代码相对不多我就全部放出来了。主要就做了三件事情1. 找到项目根路径下的锁文件。返回对应的包管理器 npm/yarn/pnpm。
2. 如果没找到那就返回 null。
3. 如果找到了但是用户电脑没有这个命令则询问用户是否自动安装。// ni/src/agents.ts
export const LOCKS: Recordstring, Agent {pnpm-lock.yaml: pnpm,yarn.lock: yarn,package-lock.json: npm,
}// ni/src/detect.ts
export async function detect({ autoInstall, cwd }: DetectOptions) {const result await findUp(Object.keys(LOCKS), { cwd })const agent (result ? LOCKS[path.basename(result)] : null)if (agent !cmdExists(agent)) {if (!autoInstall) {console.warn(Detected ${agent} but it doesnt seem to be installed.\n)if (process.env.CI)process.exit(1)const link terminalLink(agent, INSTALL_PAGE[agent])const { tryInstall } await prompts({name: tryInstall,type: confirm,message: Would you like to globally install ${link}?,})if (!tryInstall)process.exit(1)}await execa.command(npm i -g ${agent}, { stdio: inherit, cwd })}return agent
}接着我们来看 parseNi 函数。6.2 抹平不同的包管理器的命令差异 - parseNi 函数// ni/src/commands.ts
export const parseNi Runner((agent, args, ctx) {// ni -v 输出版本号if (args.length 1 args[0] -v) {// eslint-disable-next-line no-consoleconsole.log(antfu/ni v${version})process.exit(0)}if (args.length 0)return getCommand(agent, install)// 省略一些代码
})通过 getCommand 获取命令。// ni/src/agents.ts
// 有删减
// 一份配置写个这三种包管理器中的命令。export const AGENTS {npm: {install: npm i},yarn: {install: yarn install},pnpm: {install: pnpm i},
}// ni/src/commands.ts
export function getCommand(agent: Agent,command: Command,args: string[] [],
) {// 包管理器不在 AGENTS 中则报错// 比如 npm 不在if (!(agent in AGENTS))throw new Error(Unsupported agent ${agent})// 获取命令 安装则对应 npm installconst c AGENTS[agent][command]// 如果是函数则执行函数。if (typeof c function)return c(args)// 命令 没找到则报错if (!c)throw new Error(Command ${command} is not support by agent ${agent})// 最终拼接成命令字符串return c.replace({0}, args.join( )).trim()
}6.3 最终运行相应的脚本得到相应的命令比如是 npm i最终用这个工具 execa[9] 执行最终得到的相应的脚本。await execa.command(command, { stdio: inherit, encoding: utf-8, cwd })7. 总结我们看完源码可以知道这个神器 ni 主要做了三件事1. 根据锁文件猜测用哪个包管理器 npm/yarn/pnpm - detect 函数
2. 抹平不同的包管理器的命令差异 - parseNi 函数
3. 最终运行相应的脚本 - execa 工具我们日常开发中可能容易 npm、yarn、pnpm 混用。有了 ni 后可以用于日常开发使用。Vue 核心成员 Anthony Fu[10] 发现问题最终开发了一个工具 ni[11] 解决问题。而这种发现问题、解决问题的能力正是我们前端开发工程师所需要的。另外我发现 Vue 生态很多基本都切换成了使用 pnpm[12]。因为文章不宜过长所以未全面展开讲述源码中所有细节。非常建议读者朋友按照文中方法使用VSCode调试 ni 源码。学会调试源码后源码并没有想象中的那么难。最后可以持续关注我若川。欢迎加我微信 ruochuan12源码共读 活动大家一起学习源码共同进步。参考资料[1]本文仓库 ni-analysis求个star^_^: https://github.com/lxchuan12/ni-analysis.git[2]pnpm: https://github.com/vuejs/vue-next/pull/4766/files[3]贡献文档: https://github.com/vuejs/vue-next/blob/master/.github/contributing.md#development-setup[4]ni: https://github.com/antfu/ni[5]ni: https://github.com/antfu/ni[6]github 仓库 ni#how: https://github.com/antfu/ni#how[7]ni github文档: https://github.com/antfu/ni[8]阮一峰老师写的 process 对象: http://javascript.ruanyifeng.com/nodejs/process.html[9]execa: https://github.com/sindresorhus/execa[10]Anthony Fu: https://antfu.me[11]ni: https://github.com/antfu/ni[12]pnpm: https://pnpm.io最近组建了一个江西人的前端交流群如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。推荐阅读1个月200人一起读了4周源码我历时3年才写了10余篇源码文章但收获了100w阅读老姚浅谈怎么学JavaScript我在阿里招前端该怎么帮你可进面试群················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列从2014年起每年都会写一篇年度总结已经写了7篇点击查看年度总结。同时最近组织了源码共读活动识别上方二维码加我微信、拉你进源码共读群今日话题略。欢迎分享、收藏、点赞、在看我的公众号文章~