• 每日技术:npm模块安装机制


    内容来自:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22

    npm模块安装机制

    • 发出npm install命令
    • 查询node_modules目录之中是否已经存在指定模块
      • 若存在,不再重新安装
      •   若不存在,
        • npm 向register查询模块压缩包的网址
        • 下载压缩包,存放在根目录下的.npm目录里
        • 解压压缩包到当前项目的node_modules目录

    npm实现原理

    输入npm install命令并敲下回车后,会经历如下几个阶段:

    1.执行工程自身preinstall

    当前npm工程如果定义了preinstall钩子,此时会被执行

    2.确定首层依赖模块

    首先,需要做的是确定工程中的首层依赖,也就是dependencies和devDependencies属性中直接指定的模块(假设此时没有添加npm install参数)

    工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。

    3.获取模块

    获取模块是一个递归的过程,分为以下几步:

    • 获取模块信息,在下载一个模块之前,首先要确定其版本,这是因为package.json中往往是语义化版本。此时如果版本描述文件中有该模块信息直接拿即可,如果没有则从仓库获取。如package.json中某个包的版本是^1.1.0, npm就会去仓库中获取符合1.x.x形式的最新版本。
    • 获取模块实体。上一步会获取到模块的压缩包地址(resolved字段),npm会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。
    • 查找该模块依赖,如果有依赖则回到第1步,如果没有则停止

    4.模块扁平化

    上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如A模块依赖于lodash,B模块同样依赖于lodash,在npm3以前会严格按照依赖树的结构进行安装,因此会造成模块冗余。

    从npm3开始默认加入了一个dedupe的过程。它会遍历所有节点,逐个将模块放在根节点下面,也就是node-modules下,foo模块依赖lodash@^1.0.0,bar模块依赖lodash@^1.1.0,则^1.1.0为兼容版本。

    而当foo依赖lodash@^2.0.0,bar依赖lodash@^1.1.0,则根据semver的规则,二者不存在兼容版本,将会一个版本放在node_modules里,另一个仍保留在依赖树里。

    举个例子,假设一个依赖树原本是这样:

    node_modules

    -- foo

    ---- lodash@version1

    -- bar

    ---- lodash@version2

    假设version1和version2是兼容版本,则经过dedupe会成为下面的形式:

    node_module

    -- foo

    -- bar

    -- lodash (保留的版本为兼容版本)

    假设version1和version2为非兼容版本,则后面的版本保留在依赖树中

    node_modules

    -- foo

    -- lodash@version1

    -- bar

    ---- lodash@version2

    5.安装模块

    这一步将会更新工程中的node_modules,并执行模块中的生命周期函数(按照preinstall、install、postinstall的顺序)

    6.执行工程自身生命周期

    当前npm工程如果定义了钩子此时会被执行(按照install、postinstall、prepublish、prepare的顺序)

    最后一步以生成或更新版本描述文件,npm install过程完成。

     -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    读阮一峰《npm模块按照机制简介》笔记

    原文:http://www.ruanyifeng.com/blog/2016/01/npm-install.html

    npm是Node的模块管理器,功能极其强大。它是Node获得成功的重要原因之一

    本文介绍npm模块安装机制的细节,以及如何解决安装速度慢的问题。

    一、npm install

    如果你希望,一个模块不管是否安装过,npm都要强制重新安装,可以使用-f或-force参数

    npm install <packageName> --force

    二、npm update

    如果想更新已安装模块,就要用到npm update命令

    npm update <packageName>

    它会先到远程仓库查询最新版本,然后查询本地版本。如果本地版本不存在,或者远程版本较新,就会安装。

    registry

    npm模块仓库提供了一个查询服务,叫做registry。以npmjs.org为例,它的查询服务网址是https://registry.npmjs.org/

    这个网址后面跟上模块名,就会得到一个JSON对象,里面是该模块所有版本的信息。比如访问https://registry.npmjs.org/react,就会看到react模块所有版本的信息

    它跟下面命令的效果是一样的

    npm view react
    
    # npm view 的别名
    npm info react
    npm show react
    npm v react

    访问https://registry.npmjs.org/react/v0.14.6就可以看到React的0.14.6版。

    返回的JSON对象里面,有一个dist.tarball属性,是该版本压缩包的网址。

    dist: {
        shasum:'2a57c2cf8747b483759ad8de0fa47fb0c5cf5c6a',
        tarball:  'http://registry.npmjs.org/react/-/react-0.14.6.tgz'
    }

    到这个网址下载压缩包,在本地解压,就得到了模块的源码。

    缓存目录

    npm install 或npm update命令,从registry下载压缩包之后,都存放在本地的缓存目录。

    这个缓存目录,在Linux或Mac默认是用户主目录下的.npm目录,在Windows默认是%AppData%/npm-cache.

    通过配置命令,可以查看这个目录的具体位置

    npm config get cache

    浏览目录

    ls ~/.npm
    #或者
    npm cache ls

    你会看到里面存放着大量的模块,储存结构是{cache}/{name}/{version}

    $ npm cache ls react
    ~/.npm/react/react/0.14.6/
    ~/.npm/react/react/0.14.6/package.tgz
    ~/.npm/react/react/0.14.6/package/
    ~/.npm/react/react/0.14.6/package/package.json

    清空缓存命令

    rm -rf ~/.npm/*

    或者

    npm cache clean

    模块的安装过程

    总结一下,Node模块的安装过程是这样的

    1.发出npm install命令

    2.npm向registry查询模块压缩包的网址

    3.下载压缩包,存放在~/.npm目录

    4.解压压缩包到当前项目的node_modules

    注意,一个模块安装以后,本地其实保存了两份。一份是~/.npm目录下的压缩包。另一份是node_modules目录下解压后的代码。但是运行npm install的时候,只会检查node_modules目录,而不会检查~/.npm目录。也就是说,如果一个模块在~/.npm下有压缩包,但是没有安装在node_modules目录中,npm依然会从远程仓库下载一次新的压缩包。

    这种行为固然可以保证总是取得最新的代码,但有时并不是我们想要的。最大的问题是,它会极大地影响安装速度。即使某个模块的压缩包就在缓存目录中,也要去远程仓库下载,这怎么可能不慢呢?

    另外,有些场合没有网络,但是你想安装的模块,明明就在缓存目录之中,这时也无法安装。

    --cache-min参数

    为了解决这些问题,npm提供了一个--cache-min参数,用于从缓存目录安装模块。

    --cache-min参数指定一个时间(单位为分钟),只有超过这个时间的模块,才会从registry下载。

    npm install --cache-min 999999 <package-name>

    上面命令指定,只有超过999999分钟的模块才从registry下载。实际上就是指定,所有模块都从缓存安装,这样就大大加快了下载速度。

    它还有另一种写法

    npm install --cache-min Infinity <package-name>

    离线安装的解决方案

    第一类,Registry代理

    • npm-proxy-cache
    • local-npm
    • npm-lazy

    上面三个模块的用法很类似,都是在本机起一个Registry服务,所有npm install命令都要通过这个服务代理。

    npm-proxy-cache

    npm --proxy http://localhost:8080
    --https-proxy http://localhost:8080
    --strict-ssl false
    install

    local-npm

    npm set registry http://127.0.0.1:5080

    npm-lazy

    npm --registry http://localhost:8080/ install socket.io

    有了本机的Registry服务,就能完全实现缓存安装,可以实现离线使用

    第二类,npm install替代

    如果能够改变npm install的行为,就能实现缓存安装。npm-cache工具就是这个思路。凡是使用npm install的地方,都可以使用npm-cache替代

    npm-cache install

     

    第三类,node_modules作为缓存目录

    这个方案的思路是,不使用.npm缓存,而是使用项目的node_modules目录作为缓存。

  • 相关阅读:
    学习17 表单提交按钮标签
    学习16 下拉列表标签
    学习15 单选框/多选框标签
    学习14 多行文本输入标签
    学习13 文本/密码输入框的标签
    学习12 互动表单的标签
    学习10 table表格制作标签
    学习9 <span>可为部分文本设置样式属性
    金融行业网络架构与技术探讨
    二叉树
  • 原文地址:https://www.cnblogs.com/cathy1024/p/11302209.html
Copyright © 2020-2023  润新知