8

由于公司基于内网开环境开发,所有依赖包必须从外网拷贝到内网,而且当有依赖包变动时,还得再同步更新到内网,无疑降低了开发效率。再者我们也可以将常用开发组件封装发布于自己的npm仓库,方便复用。

一、外网全局安装 verdaccio

npm install -g verdaccio

首先启动 verdaccio,任意控制台执行命令

verdaccio

出现以下信息表明安装成功

   warn --- config file  - /home/yg/.config/verdaccio/config.yaml  // 配置文件所在位置
   warn --- Plugin successfully loaded: htpasswd 
   warn --- Plugin successfully loaded: audit 
   warn --- http address - http://localhost:4873/ - verdaccio/3.10.1 // 仓库所在地址 4873表示默认端口

浏览器中输入地址 http://localhost:4873/,启动服务。这里显示了我之前上传过的组件。

verdaccio.png

二、config.yaml配置说明

# #号后面是注释
# 所有包的缓存目录
storage: ./storage
# 插件目录
plugins: ./plugins

#开启web 服务,能够通过web 访问
web:
  # WebUI is enabled as default, if you want disable it, just uncomment this line
  #enable: false
  title: Verdaccio
#验证信息
auth:
  htpasswd:
    #  用户信息存储目录
    file: ./htpasswd
    # Maximum amount of users allowed to register, defaults to "+inf".
    # You can set this to -1 to disable registration.
    #max_users: 1000

# a list of other known repositories we can talk to
#公有仓库配置
uplinks:
  npmjs:
    url: https://registry.npmjs.org/

packages:
  '@*/*':
    # scoped packages
    access: $all
    publish: $authenticated

    #代理 表示没有的仓库会去这个npmjs 里面去找 ,
    #npmjs 又指向  https://registry.npmjs.org/ ,就是上面的 uplinks 配置
    proxy: npmjs

  '**':
    # 三种身份,所有人,匿名用户,认证(登陆)用户
    # "$all", "$anonymous", "$authenticated"

    #是否可访问所需要的权限
    access: $all

    #发布package 的权限
    publish: $authenticated

    # 如果package 不存在,就向代理的上游服务发起请求
    proxy: npmjs

# To use `npm audit` uncomment the following section
middlewares:
  audit:
    enabled: true
# 监听的端口 ,重点, 不配置这个,只能本机能访问
listen: 0.0.0.0:4873
# 允许离线发布
publish:
  allow_offline: true
# log settings
logs:
  - {type: stdout, format: pretty, level: http}
  #- {type: file, path: verdaccio.log, level: info}

需要格外注意的是必须配置 listen: 0.0.0.0:4873 才能运行其他机子访问!!!
在离线时要发布依赖包必须设置 allow_offline: true !!!

三、添加用户并设置npm镜像源

#当前npm 服务指向本地 
npm set registry http://localhost:4873
# 注册用户 在本地注册一个用户然后指向我们的地址然后我们就可以发布包了
npm adduser --registry http://xxx:4873
Username: xxx
Password: xxx
Password:  xxx
Email: (this IS public) xxx 
Logged in as yg-ui on http://xxx/ (你的ip地址)
这时候我们就注册一个用户,我们可以用这个用户名和密码去登录去上图窗口去登录了

下次我们再登录时,只需要输入

npm login

然后依次输入账户密码,接着检查当前用户。

npm who am i

查看当前用户用户是否登录成功

四、发布依赖包

首先必须设置镜像源:

npm set registry http://localhost:4873

为了方便管理npm源,推荐安装 nrm, 具体如何使用这里不多介绍可以直接百度。
接着,以vuecli3库模式为例子,发布vue组件到私服
1、 package.js 中新增一条编译为库的命令

"lib": "vue-cli-service build --target lib --name vcolorpicker --dest lib packages/index.js"
  • --target : 构建目标,默认为应用模式。这里修改为 lib 启用库模式。
  • --dest : 输出目录,默认 dist 。这里我们改成 lib
  • [entry] : 最后一个参数为入口文件,默认为 src/App.vue 。这里我们指定编译 packages/ 组件库目录。

    在库模式中,Vue是外置的,这意味着即使在代码中引入了 Vue,打包后的文件也是不包含Vue的。
    如果我们在依赖中引入了 lodash 等生产环境必须安装的依赖库等,我们可以在vue.config.js中配置 external 属性, 使之不打包对应的依赖包。

   module.exports = {
      configureWebpack:{
          externals: {
             'vue': 'Vue',
             'vue-router':'VueRouter',
             'axios': 'axios',
             'lodash' : '\_'
          }
      }
   }

2、 配置 package.json

  {
    "name": "packagename",
    "version": "0.1.5",
    "description": "基于 Vue 组件库",
    "main": "lib/index.js",
    "keyword": "vcolorpicker colorpicker color-picker",
    "files": ['dist', 'page'],
    "private": false
  }

其中

  • private : 必须设置为 fasle
  • files : 设置要上传到npm库的文件目录
  • main : 项目入口,默认为同级目录的 index.js
  • name : npm包名,就是我们 import xxx from packagename 中的包名

也可以这么理解 import xxx from packagenamexxx 就是引入 main 入口中 lib/index.js暴露出的变量名。

3、 添加.npmignore 文件,设置忽略发布文件
package.jsonfiles 属性一样,我们发布到 npm 中,只有编译后的 libpackage.jsonREADME.md等文件才是需要被发布的。所以我们需要设置忽略目录和文件。
语法同.gitignore 的语法一样,具体需要提交什么文件,看各自的实际情况。

# 忽略目录
examples/
packages/
public/
 
# 忽略指定文件
vue.config.js
babel.config.js
*.map

4、登录到 npm

npm login

具体登录过程同上,具体不多说了。

5、 发布到 npm

npm publish

执行后显示发布成功即可在npm私服上找到自己的包,如果没有发布成功,有可能是包名称或者版本重复了,改个配置即可。

五、 内网npm私服搭建

将以下对应的外网目录拷贝到内网环境中

文件:C:\\Users\\用户名\\AppData\\Roaming\\npm\\verdaccio

文件:C:\\Users\\用户名\\AppData\\Roaming\\npm\\verdaccio.cmd

目录:C:\\Users\\用户名\\AppData\\Roaming\\npm\\node\_modules\\verdaccio

文件:C:\\Users\\用户名\\AppData\\Roaming\\verdaccio\\config.yaml

目录:C:\\Users\\用户名\\AppData\\Roaming\\verdaccio\\storage

注意,其中 storage 目录是存放npm依赖包的地方, 我们可以先直接在外网发布好npm包,然后把storage文件夹复制到内网,接着打开内网verdaccio地址,就能发现这些依赖包自动发布到内网了

我们发布依赖包到npm私服,有两种包,一种是自己开发的包,另一种是外网npm上的开源包,如果想要将开源包发布到自己的私服上使用,请注意一下几点(踩着坑过来的!!!):

  • 1: 要发布的npm依赖包,最好用npm下载,别用cnpm,因为cnpm包含了各种快捷方式,拷贝到其他电脑会出现各种问题
  • 2: 注意查看发布的npm包中package.json中的 script 中是否含有 republish等钩子属性,republish 会在发布之前执行该命令,而往往我们并不具备执行该命令的环境,从而报错,我们可以将它删除
  • 3:依赖包中有可能嵌套了其他依赖包,该依赖包中子目录的 node_modules文件夹查看依赖情况,发布之前也必须一并将它发布。因为 node_modules 是冒泡向上查找依赖的,相同依赖包有不同版本,公共版本被提取到了依赖包的最外层,特殊版本只能存在于当前目录底下 node_modules文件夹
  • 4:如果想发布指定 package.json 下的所以npm包,正常情况下至少有100多个依赖包,我们手动发布是很辛苦的。但是, verdaccio又不支持一键导入,所以我们只能一个个发布。这里我写了一个 node自动读取 node_modules 目录下的文件并且自动发布依赖包到 npmpublish 的文件,供大家试用!
const fs = require('fs');
const path = require('path');
const process = require('process');
// 导入执行控制台命令的方法
const exec = require('child_process').execSync;
// 保存根路径
const rootPath = __dirname;
let result = fs.readdirSync(rootPath);
const consoleLogPath = path.join(rootPath, 'log');
result.forEach(fName => {
    const dPath = path.join(__dirname, fName);
    const stat = fs.statSync(dPath);
    if (stat.isDirectory()) {
        const modulePath = path.join(dPath, 'node_modules');
        if (!fs.existsSync(modulePath)) return;
        const statModules = fs.statSync(modulePath);
        if (statModules.isDirectory()) {
            const res = fs.readdirSync(modulePath)
            getVersion(res, consoleLogPath, modulePath, fName)
        }
    }
})

function getVersion(result, logPath, modulePath, fName) {
    result.forEach(item => {
        // 拼接当前包路径
        const dPath = path.join(modulePath, item);
        // 获取路径的stat
        const stat = fs.statSync(dPath);
        // 判断该包路径是否为目录
        if (stat.isDirectory()) {
            // 如果是目录,则执行下述操作
            // 当前包目录下的package.json文件路径
            const packageJsonPath = path.join(dPath, 'package.json');
            if (fs.existsSync(packageJsonPath)) {
                // 读取当前包目录下的package.json文件的内容,返回字符串
                const packageJsonContentString = fs.readFileSync(packageJsonPath, 'utf8');
                // 解析package.json文件的内容成json格式的对象
                const parsedPackJson = JSON.parse(packageJsonContentString);
                if (parsedPackJson.scripts && Object.keys(parsedPackJson.scripts).length !== 0) {
                    parsedPackJson.scripts = {};
                    fs.writeFileSync(packageJsonPath, JSON.stringify(parsedPackJson));
                }
                // 把当前的目录切换到当前包路径下
                process.chdir(dPath);
                // 在包路径下执行cmd控制台命令 npm publish
                try {
                    exec('npm publish');
                } catch {
                    console.log(`包${fName}:${item}已发布`);
                }
                // 操作执行完成,则把当前路径切换到原来的根目录
                process.chdir(rootPath);
                console.log(fName, parsedPackJson.name, parsedPackJson.version)
                // 如果解析好的json格式的对象存在scripts属性且该属性不为空对象,那么将其内容设置成空对象
                fs.appendFileSync(logPath, `${fName} / ${parsedPackJson.name} /@${parsedPackJson.version}\n`);
            }
        }
    })
}

参考文档

  1. 《npm 模块安装机制简介》
  2. 《node_modules 困境》

chinamasters
761 声望1.3k 粉丝

本人岂是池中物!