vue3框架开发过程中,当项目达到一定数量,开始有搭建依赖包私服的需求了,毫无疑问,目前基本都在使用verdaccio,简单易用,跟npmjs库的发布方式基本一致,不废话,说说verdaccio的基本安装使用:
安装使用
npm i verdaccio -g
全局安装,这个时候会在nvm
的安装目录,当前使用版本文件夹下的node_modules
下有一个verdaccio
文件夹,如果你也是用nvm
进行node
版本管理,那么路径一般大概都在这里:
C:\Users\Administrator\AppData\Roaming\nvm\v20.10.0\node_modules\verdaccio
多找找应该都能找到,然后verdaccio
目录下有个conf
文件夹,里面有几个yaml
文件:
然后修改default.yaml里面的配置即可,一些基本配置如下:
# path to a directory with all packages
storage: H:/verdaccio/storage #发布的包的目录
# path to a directory with plugins to include
plugins: H:/verdaccio/plugins
# https://verdaccio.org/docs/webui
web:
title: Verdaccio本地库
showSearch: true #搜索生效
# https://verdaccio.org/docs/configuration#max-body-size
max_body_size: 500mb
listen:
# - localhost:4873 # default value
# - http://localhost:4873 # same thing
- 0.0.0.0:4873 #这样其他电脑才能访问你本地的verdaccio仓库
基本需要修改的配置就这些,注意:yaml文件的缩进只能用空格,不要用tab键;
然后打开cmd或者Powershell窗口直接运行verdaccio:
verdaccio
verdaccio
库就会跑起来,浏览器输入http://localhost:4873 访问,就能看到verdaccio
界面;
有个特殊的坑提前说下:max_body_size: 500mb
,这个参数可能改了不生效,不改连element-plus
这种大点的库都发布不了,报request entity too large
,要去这个目录下:
C:\Users\Administrator\AppData\Roaming\verdaccio
删掉config.yaml文件,再重新启动verdaccio
,这个时候verdaccio
会自动将上面的default.yaml
复制一份过来,直接手动改config.yaml
文件可能会直接无法启动verdaccio
;
发布第三方包到verdaccio
所谓第三方包,就是本地项目已经下载过的node_modules
目录依赖包,因为需要私服的一般都是内网,将其他人发布过的包重新发布一份到verdaccio
,其实也很简单,因为那些依赖都是打包过的,可以直接发布;
要发布依赖包先要创建verdaccio
用户:
npm adduser --registry http://localhost:4873/
接着会让你输入用户名、密码、邮箱,照填就行;
登录:
npm login --registry http://localhost:4873/
输入用户名、密码、邮箱即可,登录成功!
接下来就可以进入发布步骤了:
我们只需要通过CMD
命令进入到要发布的包的根目录下:
cd 项目目录/node_modules/element-plus
执行:
npm version patch
npm publish --registry http://localhost:4873/
可能有同学不懂npm version patch
是什么,其实是因为发布包之前一定要改package.json
文件的版本号,这里是用命令直接改了,当然也可以手动修改package.json
里面的version
版本号,才能发布成功,每次发布都要改一次,另外很重要的一点,就是要把package.json
里的scripts
属性置为空才行,不然可能会失败,因为里面也有publish
命令;
用命令执行是为后面的批量发布准备,目录里几百个依赖包,不可能每个依赖都手动改一次版本号,改一次scripts命令,然后再发布吧,因此批量发布时,最好是拷一份依赖到指定目录去执行,保证不污染原依赖;
不出意外,这样就可以发布成功啦
成功会打印一行:+ ………
注意:发布后需要重启verdaccio才能搜索到;
发布自己开发的组件包到verdaccio
会发布第三方包以后,那如何自己开发这种依赖包呢?
简单的做法是创建一个目录,然后在目录里创建一个index.js
文件,然后在这个目录下执行:
npm init -y
会生成一个默认的package.json
文件,里面的main
属性默认就是上面的index.js
,如果是普通的方法,直接在index.js
里开发然后export
导出即可,这种不用编译,相对简单;
复杂的就要用rollup
、vite
等依赖一起开发来编译过后才能发布;
其实也是跟平时开发一样,开发完也要npm run build
打包到dist
目录然后在dist
目录下发布,下面小编举一个自己发布过的组件的做法吧,这是一个实现瀑布流布局的composable
函数组件:
最终文件目录预览:
因为要用到vue+ts
,所以在目录下分别创建src/index.ts
、rollup.config.js
、tsconfig.json
;
然后在目录下执行:
npm init -y
生成package.json
文件,然后在src/index.ts
里开发composable
组合式方法:
import { ref } from 'vue'
import type { Ref } from 'vue'
export function useWaterFallLayout(options: Object) {
......
}
rollup.config.js文件:
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import vue from 'rollup-plugin-vue'
import { terser } from 'rollup-plugin-terser'
export default {
input: 'src/index.ts',//包入口
output: {
file: 'dist/index.js',//输出路径
format: 'esm', // 输出为ES模块
exports: 'named'
},
plugins: [
resolve(),
commonjs(),
vue(),
typescript(),
terser() // 用于压缩代码
],
external: ['vue'] // 假设vue是外部依赖,不包含在打包文件中
}
tsconfig.json文件:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"outDir": "./dist",
"strict": true,
"jsx": "preserve",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
package.json文件:
{
"name": "usewaterfalllayout",
"version": "1.0.5",
"description": "一个vue3+ts的composable瀑布流布局组件",
"main": "index.js",
"scripts": {
"build": "rollup -c",
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [
"waterfalllayout"
],
"author": "wengyd",
"license": "ISC",
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/node": "^20.11.24",
"rollup": "^2.79.1",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.36.0",
"typescript": "^5.3.3"
},
"dependencies": {
"rollup-plugin-vue": "^6.0.0",
"vue": "^3.4.21"
}
}
开发完以后npm run build
打包,生成dist
目录下的:
然后将根目录的package.json
文件拷贝过来dist
目录,执行:
npm publish --registry http://localhost:4873/
进行发布即可;
批量发布依赖
新建一个publish.mjs
文件,用mjs是为了能使用ES语法,直接上代码吧:
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import process from 'process'
import child_process from 'child_process'
const exec = child_process.execSync
const __filename = fileURLToPath(import.meta.url)
const rootPath = path.dirname(__filename)
const consoleLogPath = path.join(rootPath, `publishLog_${format(getNowTime())}`)
const modulePath = path.join(rootPath, 'npmjs/node_modules') //要发布的包目录
const res = fs.readdirSync(modulePath)
let total = 0,
packtotal = 0,
successCount = 0,
failedCount = 0,
failedList = [],
beginTime = getNowTime()
function getNowTime() {
const d = new Date()
return `${d.getFullYear()}-${
d.getMonth() + 1
}-${d.getDate()}:${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`
}
function format(timeStr) {
return timeStr.replace(/[\-\.\:]/g, '_')
}
batchPublish(res, consoleLogPath, modulePath)
function batchPublish(res, consoleLogPath, modulePath, isSubDir) {
let subPacktotal = 0,
subSuccessCount = 0,
subFailCount = 0
const subBeginTime = getNowTime()
const isPublish = true
console.log(
`\n\n 【-- ${isSubDir ? getNowTime() : beginTime} --${isSubDir ? '子' : '总'}任务开始...】`
)
res.forEach((item, index) => {
const dPath = path.join(modulePath, item)
const stat = fs.statSync(dPath)
total++
if (stat.isDirectory()) {
process.chdir(dPath)
console.log(`-->进入目录:${dPath}`)
const packageJsonPath = path.join(dPath, 'package.json')
if (fs.existsSync(packageJsonPath)) {
console.log(`\n已找到package.json文件,启动发布程序...\n`)
subPacktotal++
packtotal++
const packageJsonContentString = fs.readFileSync(packageJsonPath, 'utf-8')
const parsePackageJson = JSON.parse(packageJsonContentString)
if (
parsePackageJson.scripts &&
Object.keys(parsePackageJson.scripts).length !== 0
) {
parsePackageJson.scripts = {}
fs.writeFileSync(packageJsonPath, JSON.stringify(parsePackageJson))
}
try {
console.log(`包 名 称:${parsePackageJson.name}`)
console.log(`文件位置:${dPath}`)
console.log(`开始时间:${getNowTime()}\n`)
if (isPublish) {
console.log(`正在发布中...\n`)
exec('npm version patch && npm publish --registry http://localhost:4873/')
const newPackageJsonString = fs.readFileSync(packageJsonPath, 'utf-8')
const newPackageJson = JSON.parse(newPackageJsonString)
console.log(`\n结束时间:${getNowTime()}`)
console.log(`原版本号:${parsePackageJson.version}`)
console.log(`新版本号:${newPackageJson.version}`)
console.log(`处理结果:发布成功!`)
} else {
console.log(`正在删除中...\n`)
exec(
`npm unpublish ${parsePackageJson.name} --force --registry http://localhost:4873/`
)
console.log(`\n结束时间:${getNowTime()}`)
console.log(`处理结果:删除成功!`)
}
successCount++
subSuccessCount++
fs.appendFileSync(
consoleLogPath,
`[${index + 1}/${res.length}]${
isPublish ? '成功发布包' : '成功删除包'
}->${dPath}:${parsePackageJson.name}@${
parsePackageJson.version
} @${getNowTime()}\n`
)
} catch (e) {
console.log(e)
console.log(`\n结束时间:${getNowTime()}`)
if (isPublish) {
console.log(`处理结果:已发布过/发布失败!`)
} else {
console.log(`处理结果:不存在/删除失败!`)
}
failedCount++
subFailCount++
failedList.push(
`${dPath}:${parsePackageJson.name}@${
parsePackageJson.version
} @${getNowTime()}\n`
)
}
}
const subRes = fs.readdirSync(dPath)
batchPublish(subRes, consoleLogPath, dPath, true)
process.chdir(modulePath)
console.log(`<--回到目录:${modulePath}`)
}
})
if (isSubDir) {
console.log(`\n【---${getNowTime()}--子任务结束--exit-】\n`)
console.log(`\n【子任务统计】:`)
console.log(`总文件数:${res.length}`)
console.log(`有 效 包:${subPacktotal}`)
console.log(`处理成功:${subSuccessCount}`)
console.log(`失败/不处理:${subFailCount}`)
console.log(`耗 时:${subBeginTime} --> ${getNowTime()}`)
console.log(`成 功 率:${(subSuccessCount / subPacktotal) * 100}%\n`)
} else {
console.log(`\n总任务已处理完毕,失败/不处理的包日志正在写入...\n`)
if (failedList.length !== 0) {
fs.appendFileSync(consoleLogPath, `失败日志列表:\n`)
failedList.forEach((f, i) => {
fs.appendFileSync(
consoleLogPath,
`${isPublish ? '发布失败' : '删除失败'}${i + 1}.${f}`
)
})
}
console.log(`【总任务统计】:`)
console.log(`累计文件数:${total}`)
console.log(`有 效 包:${packtotal}`)
console.log(`处理成功:${successCount}`)
console.log(`失败/不处理:${failedCount}`)
console.log(`耗 时:${beginTime} --> ${getNowTime()}`)
console.log(`成 功 率:${(successCount / packtotal) * 100}%`)
console.log(`\n本次总任务已结束,退出-- over --------------------\n`)
}
}
目录如下:
然后在publish.mjs文件目录下执行:
node publish.mjs
这样node_modules
文件夹下的依赖就会被批量发布到verdaccio,等待执行完毕即可:
到此,关于verdaccio的相关操作都已经说啦,快去试试吧