返回顶部
首页 > 资讯 > 前端开发 > node.js >如何实现一个简易的NpmInstall
  • 225
分享到

如何实现一个简易的NpmInstall

2024-04-02 19:04:59 225人浏览 薄情痞子
摘要

本篇文章给大家分享的是有关如何实现一个简易的NpmInstall ,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。现在写代码我们一般不会全部自己

本篇文章给大家分享的是有关如何实现一个简易的NpmInstall ,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

现在写代码我们一般不会全部自己实现,更多是基于第三方的包来进行开发,这体现在目录上就是 src 和 node_modules 目录。

如何实现一个简易的NpmInstall

src 和 node_modules(第三方包) 的比例不同项目不一样。

运行时查找第三方包的方式也不一样:

  • 在 node 环境里面,运行时就支持 node_modules 的查找。所以只需要部署 src 部分,然后安装相关的依赖。

如何实现一个简易的NpmInstall

  • 在浏览器环境里面不支持 node_modules,需要把它们打包成浏览器支持的形式。

如何实现一个简易的NpmInstall

跨端环境下,它是上面哪一种呢?

都不是,不同跨端引擎的实现会有不同,跨端引擎会实现 require,可以运行时查找模块(内置的和第三方的),但是不是 node  的查找方式,是自己的一套。

如何实现一个简易的NpmInstall

和 node 环境下的模块查找类似,但是目录结构不一样,所以需要自己实现 xxx install。

思路分析

npm 是有自己的 reGIStry server 来支持 release 的包的下载,下载时是从 registry server  上下载。我们自己实现的话没必要实现这一套,直接用 git clone 从 gitlab 上下载源码即可。

依赖分析

要实现下载就要先确定哪些要下载,确定依赖的方式和打包工具不同:

  • 打包工具通过 AST 分析文件内容确定依赖关系,进行打包

  • 依赖安装工具通过用户声明的依赖文件 (package.JSON / bundle.json)来确定依赖关系,进行安装

这里我们把包的描述文件叫做 bundle.json,其中声明依赖的包:

{     "name": "xxx",     "dependencies": {         "yyyy": "aaaa/bbbb#release/1111"     } }

通过分析项目根目录的 bundle.json 作为入口,下载每一个依赖,分析  bundle.json,然后继续下载每一个依赖项,递归这个过程。这就是依赖分析的过程。

这样依赖分析的过程中进行包的下载,依赖分析结束,包的下载也就结束了。这是一种可行的思路。

但是这种思路存在问题,比如:版本冲突怎么办?循环依赖怎么办?

解决版本冲突

版本冲突是多个包依赖了同一个包,但是依赖的版本不同,这时候就要选择一个版本来安装,我们可以简单的把规则定为使用高版本的那个。

解决循环依赖

包之间是可能有循环依赖的(这也是为什么叫做依赖图,而不是依赖树),这种问题的解决方式就是记录下处理过的包,如果同个版本的包被分析过,那么久不再进行分析,直接拿缓存

这种思路是解决循环依赖问题的通用思路。

我们解决了版本冲突和循环依赖的问题,还有没有别的问题?

版本冲突时会下载版本最高的包,但是这时候之前的低版本的包已经下载过了,那么就多了没必要的下载,能不能把这部分冗余下载去掉。

依赖分析和下载分离

多下载了一些低版本的包的原因是我们在依赖分析的过程中进行了下载,那么能不能依赖分析的时候只下载 bundle.json  来做分析,分析完确定了依赖图之后再去批量下载依赖?

从 gitlab 上只下载 bundle.json 这一个文件需要通过 ssh 协议来下载,略微复杂,我们可以用一种更简单的思路来实现:

git clone --depth=1 --branch=bb xxx

加上 --depth 以后 git clone 只会下载单个 commit,速度会很快,虽然比不上只下载  bundle.json,但是也是可用的(我试过下载全部 commit 要 20s 的时候,下载单个 commit 只要 1s)。

这样我们在依赖分析的时候只下载一个 commit 到临时目录,分析依赖、解决冲突,确定了依赖图之后,再去批量下载,这时候用 git clone 下载全部的  commit。最后要把临时目录删除。

这样,通过分离依赖分析和下载,我们去掉了没必要的一些低版本包的下载。下载速度会得到一些提升。

全局缓存

当本地有多个项目的时候,每个项目都是独立下载自己的依赖包的,这样对于一些公用的包会存在重复下载,解决方式是全局缓存。

分析完依赖进行下载每一个依赖包的时候,首先查找全局有没有这个包,如果有的话,直接复制过来,拉取下最新代码。如果没有的话,先下载到全局,然后复制到本地目录。

通过多了一层全局缓存,我们实现了跨项目的依赖包复用。

代码实现

为了思路更清晰,下面会写伪代码

依赖分析

依赖分析会递归处理 bundle.json,分析依赖并下载到临时目录,记录分析出的依赖。会解决版本冲突、循环依赖问题。

const allDeps = {}; function installDeps(projectDir) {     const bundleJsonPath = path.resolve(projectDir, 'bundle.json');     const bundleInfo = JSON.parse(fs.readFileSync(bundleJsonPath));          const bundleDeps = bundleInfo.dependencies;     for (let depName in bundleDeps) {         if(allDeps[depName]) {             if (allDeps[depName] 和 bundleDeps[depName] 分支和版本一样) {                 continue;// 跳过安装             }             if (allDeps[depName] 和 bundleDeps[depName] 分支和版本不一样){                 if (bundleDeps[depName] 版本 < allDeps[depName] 版本 ) {                     continue;                 } else {                     // 记录下版本冲突                     allDeps[depName].conflit = true;                 }                         }         }         childProcess.exec(`git clone --depth=1 ${临时目录/depName}`);         allDeps[depName] = {             name: depName             url: xxx             branch: xxx             version: xxx         }         installDeps(`${临时目录/depName}`);     }     }

下载

下载会基于上面分析出的 allDeps 批量下载依赖,首先下载到全局缓存目录,然后复制到本地。

function batchInstall(allDeps) {     allDeps.forEach(dep => {         const 全局目录 = path.resolve(os.homedir(), '.xxx');         if (全局目录/dep.name 存在) {             // 复制到本地             childProcess.exec(`cp 全局目录/dep.name 本地目录/dep.name`);         } else {             // 下载到全局             childProcess.exec(`git clone --depth=1 ${全局目录/dep.name}`);              // 复制到本地             childProcess.exec(`cp 全局目录/dep.name 本地目录/dep.name`);         }     }); }

这样,我们就完成了依赖的分析和下载,实现了全局缓存。

 我们首先梳理了不同环境(浏览器、node、跨端引擎)对于第三方包的处理方式不同,浏览器需要打包,node  是运行时查找,跨端引擎也是运行时查找,但是用自己实现的一套机制。

然后明确了打包工具确定依赖的方式是 AST 分析,而依赖下载工具则是基于包描述文件 bundl.json(package.json)  来分析。然后我们实现了递归的依赖分析,解决了版本冲突、循环依赖问题。

为了减少没必要的下载,我们做了依赖分析和下载的分离,依赖分析阶段只下载单个 commit,后续批量下载的时候才全部下载。下载方式没有实现 registry  的那套,而是直接从 gitlab 来 git clone。

为了避免多个项目的公共依赖的重复下载,我们实现了全局缓存,先下载到全局目录,然后再复制到本地。

npm install、yarn install  的实现流程细节会更多一些,但是整体流程类似。希望这篇文章能帮你梳理清楚思路:不同环境是怎么处理第三方包的,xxx install  的依赖分析和下载的流程是什么样的。

以上就是如何实现一个简易的NpmInstall ,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网node.js频道。

--结束END--

本文标题: 如何实现一个简易的NpmInstall

本文链接: https://lsjlt.com/news/81113.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何实现一个简易的NpmInstall
    本篇文章给大家分享的是有关如何实现一个简易的NpmInstall ,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。现在写代码我们一般不会全部自己...
    99+
    2024-04-02
  • 如何实现一个简易promise
    这篇文章主要介绍如何实现一个简易promise,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!step1 搭建框架 首先我们需要在这里放置一个promise函数本体 后面要在里面添加resolve、reject的执行函...
    99+
    2023-06-25
  • python实现一个简易hashmap
    ...
    99+
    2023-01-31
    简易 python hashmap
  • 用Python实现一个简易的WebSoc
    </pre><pre name="code" class="python">#coding=utf-8 from threading import Thread import struct import time...
    99+
    2023-01-31
    简易 Python WebSoc
  • 如何使用Python实现一个简易的ORM模型
    目录元类描述器本文记录下自己使用Python实现一个简易的ORM模型 使用到的知识 元类 描述器 元类 对于元类,我的理解其实也便较浅,大概是这个意思 所有的类都是使用元类来进行创建的,而所有的类的父类中必然是obj...
    99+
    2022-06-02
    python ORM python 实现ORM模型
  • js实现一个简易的计算器
    利用原生js实现一个简易的计算器(附详细注释),供大家参考,具体内容如下 <!DOCTYPE html> <html lang="en"> <...
    99+
    2024-04-02
  • 教你一步步实现一个简易promise
    目录step1 搭建框架step2 填充搭建好了的Promise框架总结step1 搭建框架 1. 首先我们需要在这里放置一个promise函数本体 后面要在里面添加resolve、...
    99+
    2024-04-02
  • php7怎么实现一个简易框架
    本篇内容主要讲解“php7怎么实现一个简易框架”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“php7怎么实现一个简易框架”吧!框架的核心链路是从开始的请求路由解析到控制器的分发,model的数据...
    99+
    2023-06-20
  • 如何使用Python实现一个简易版Web服务器
    今天小编给大家分享一下如何使用Python实现一个简易版Web服务器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、简介我...
    99+
    2023-07-05
  • Java实现一个简易聊天室流程
    目录文件传输Tcp方式Udp 方式简易聊天室的实现接收端发送端启动说到网络,相信大家都对TCP、UDP和HTTP协议这些都不是很陌生,学习这部分应该先对端口、Ip地址这些基础知识有一...
    99+
    2022-11-13
    Java聊天室 Java简易聊天室
  • VUE实现一个简易老虎机的项目实践
    目录简单分析下先看看效果htmljs部分总结今天突然要做一个竖直滚动老虎机,可以设置中奖位置,以及中奖回调,然后再带点常规的滚动动画,还是有点意思,和之前的转盘抽奖有点类似,有兴趣可...
    99+
    2024-04-02
  • 如何使用Java实现一个简易版的多级菜单功能
    小编给大家分享一下如何使用Java实现一个简易版的多级菜单功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!正文1,首先是数据库的设计DROP TABL...
    99+
    2023-06-26
  • 基于python的Tkinter实现一个简易计算器
    本文实例介绍了基于python的Tkinter实现简易计算器的详细代码,分享给大家供大家参考,具体内容如下 第一种:使用python 的 Tkinter实现一个简易计算器 #coding:utf-8 ...
    99+
    2022-06-04
    计算器 简易 python
  • 利用Python实现一个简易的截图工具
    这是工作期间同事想要个截完图之后可以显示并且永远前置的截图小工具(即不会被其他程序覆盖)直接上代码: # # -*- coding: utf-8 -*- import tkinter...
    99+
    2024-04-02
  • 怎么用java实现一个简易的聊天室
    要实现一个简易的聊天室,可以使用Java的Socket编程实现。下面是一个简单的实现示例: 服务器端代码: import java....
    99+
    2024-02-29
    java
  • c++实现一个简易的网络缓冲区的实践
    目录1. 前言2. 数据结构3. 外部接口设计与实现4. 完整代码与测试1. 前言 请思考以下几个问题: 1).为什么需要设计网络缓冲区,内核中不是有读写缓冲区吗? 需要设计的网络缓...
    99+
    2024-04-02
  • Android中怎么实现一个简易时间轴
    这期内容当中小编将会给大家带来有关Android中怎么实现一个简易时间轴,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。新建一个自定义控件:public class WorkExcVie...
    99+
    2023-05-30
    android
  • 基于Python+Tkinter实现一个简易计算器
    目录设计原理示例效果主要代码设计原理 从结构上来说,一个简单的图形界面,需要由界面组件、组件的事件监听器(响应各类事件的逻辑)和具体的事件处理逻辑组成。界面实现的主要工作是创建各个界...
    99+
    2024-04-02
  • 怎么用Python实现一个简易的截图工具
    这篇文章主要讲解了“怎么用Python实现一个简易的截图工具”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Python实现一个简易的截图工具”吧!代码:# # -*...
    99+
    2023-07-02
  • Java实现一个简易版的多级菜单功能
    一:前言 最近老师布置了给多级菜单的作业,感觉蛮有意思的,可以提升自己的逻辑!下面我写个简易版的多级菜单,本人还是菜鸟,欢迎各位给予宝贵的建议! 二:正文 由于是给各位演示,所有我把...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作