Monorepo使用和npm包开发
什么是Monorepo
单一代码管理库
是一种将多个项目的源代码存储在同一个版本库中的开发方法。这种方法可以简化代码管理、促进代码共享,并统一构建和部署流程。
- 统一代码管理
- 共享代码资源
- 一致的构建部署流程
- 统一的版本控制
- 简化依赖管理
闲话不扯,开始
总共就这么几个步骤:
- 新建目录
- 配置pnpm-workspace
- 配置packages
- 配置命令
Pnpm初始化一个目录
我们用pnpm workspace来管理
新建一个目录
执行
pnpm init -y
根目录下面新建一个 pnpm-workspace.yaml 文件
packages:
- packages/*
- components/*
- hooks/*
- utils/*
packages下面放项目的目录
其它的可以不要。我这里面其它目录是开发组件和公共函数的
当然也可以把组件放在packages下面,当作子目录
然后我们在packages新建我们的项目
pnpm create vite
// 您看着选择
next进入目录运行
pnpm i
pnpm dev
正常的话,我们应该看到我们带端口号的访问链接了
OK.STOP IT! ctrl + c吧
继续把node_modules目录删除 do it
我们来到monorepo的根目根,再去执行pnpm i
再打开我们刚刚删除的目录看一下,是不是node_modules又出来了
所以理解这个意思了吧
正常我们启动一个项目不想一层层的cd packages/目录 啊
在根目录的package.json的scripts里面增加一个
"start-demo":"cd packages/demo-admin && pnpm dev"
我们在根目录跑对应的名称就行了
接着来处理公共的库
在根目录或者其它的地方新建一个目录
- 我建了一个目录function // 大家命名认真一点啊
pnpm init -y
// name 自已配,比如我写的wild
- 新建一个url.js文件,我复制的我之前的 // 大家命名认真一点啊
// 解析URL
export function etURLParameters(url = window.location.search) {
// 如果不指定url,则解析返回当前地址的
let searchParams;
if (url) {
searchParams = new URLSearchParams(url.split('?')[1]);
} else {
searchParams = new URLSearchParams(window.location.search)
}
const params = {}
searchParams.forEach(function (value, key) {
params[key] = value
})
return params;
}
- 在里面新建一个index.js文件,引入这个文件,再导出去。单纯的为了导入出口
import * as url from './url.js'
export default {
...url,
}
package.json里面记往跟这个index.js文件一致
- 去掉项目目录安装,当然也可以从根目录安装,命令不一样
import wild from 'wild'
console.log(wild.etURLParameters())
其它项目也可以这样引用。
来个组件库
在根目录或者其它的地方新建一个目录
新建一个目录components
pnpm init -y
// name 自已配
"dependencies": {
"@icon-park/vue-next": "^1.4.2",
"fs": "0.0.1-security",
"less": "^4.2.0",
"vue": "^3.4.19"
},
// 在这个字段里面配上相应的依赖,这里面我肯定需要vue
然后直接新建组件目录或者建一个src目录全放在里面也行。只要路径是对的
新建一个button组件
<script>
import { defineComponent} from "vue"
export default defineComponent({
name:'Button'
})
</script>
<script setup>
const props = defineProps({
text:String,
default:'按钮'
})
</script>
<template>
<button >{{props.text}}</button>
</template>
<style scoped lang="less">
button{
height: 30px;
width: 120px;
border:1px solid #f00;
}
</style>
index.js文件导出注册。忽视掉Icon
import Icon from './icon/index.vue'
import Button from './button/index.vue'
// 按需引入
const component = [Button, Icon];
const WilderUI = {
install(Vue) {
component.forEach((item,index) => {
Vue.component(item.name, component[index]);
});
},
};
export default WilderUI;
这时候在其它项目执行
import { Button } from 'WilderUI'
正常引用就行了
打包发布
我一开始的想法是搞一个工程目录,用来开发组件,函数,hooks包的。这自然要发布到npm。那接着来吧
注册npm账号。这个不说了,如果需要@xx/xx 这种,记往转组织账号
基于前面的组件或者函数目录,增加一些打包的东西。
支持TS
安装TS
pnpm add typescript
初化始TS
npx tsc --init
项目里面会生成tsconfig.json的配置文件
配置vite.config.ts
打包的目录大家看自已的情况随便配,我的npm-publish里面放了所有的,全部放打包后的文件的。
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
build: {
//打包文件目录
outDir: "es",
//压缩
//minify: false,
rollupOptions: {
//忽略打包vue文件
external: ["vue"],
input: ["index.js"],
output: [
{
//打包格式
format: "es",
//打包后文件名
entryFileNames: "[name].js",
//让打包目录和我们目录对应
// preserveModules: true,
exports: "named",
//配置打包根目录
dir: "../../npm-publish/DUI/es",
},
{
//打包格式
format: "cjs",
//打包后文件名
entryFileNames: "[name].js",
//让打包目录和我们目录对应
// preserveModules: true,
exports: "named",
//配置打包根目录
dir: "../../npm-publish/DUI/lib",
},
],
},
lib: {
entry: "./index.ts",
},
},
plugins: [vue()],
});
package里面添加打包命令
"build": "vite build",
运行之后,我们会得到一个打包后的目录文件
当然这时候应该没有package和reame.md文件
那就自已生成package.json吧。在这个目录运行 pnpm init -y
生成package.json文件,输入你的包的名称,修改 main:'./lib/index.js'
写上readme.md的说明文件。
npm login
npm publish 或者 npm publish --access public
// 如果你是组织的话,需要加公开的参数,私有的需要收费
后续
每次都要打包,再npm publish
很烦,关键还要修改版本号,很烦。
人懒就想了个办法,新建了一个js文件
- 读取build目录的package.json里面的版本号
- 将版本号修改+1
- 在build目录执行npm publish
// 1.获取版本号
// 2.增加版本号
// 3.推送发布
const fs = require('fs');
const { exec } = require('child_process');
function incrementVersionAndPublish(packageJsonPath) {
fs.readFile(packageJsonPath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading package.json:', err);
return;
}
try {
const packageJson = JSON.parse(data);
const currentVersion = packageJson.version;
const [major, minor, patch] = currentVersion.split('.').map(Number);
let newMajor = major;
let newMinor = minor;
let newPatch = patch + 1;
if (newPatch === 10) {
newPatch = 0;
newMinor += 1;
}
if (newMinor === 10) {
newMinor = 0;
newMajor += 1;
}
const newVersion = `${newMajor}.${newMinor}.${newPatch}`;
packageJson.version = newVersion;
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8', (err) => {
if (err) {
console.error('Error writing package.json:', err);
return;
}
console.log('版本更新到', newVersion);
// 更改工作目录到 package.json 所在目录
const packageDir = packageJsonPath.split('/').slice(0, -1).join('/');
process.chdir(packageDir);
// 执行 npm publish 命令
exec('npm publish --access public', (error, stdout, stderr) => {
if (error) {
console.error('Error publishing package:', error);
return;
}
console.log('包发布成功!');
console.log(stdout);
});
});
} catch (error) {
console.error('Error parsing package.json:', error);
}
});
}
// 调用示例
const packageJsonPath = '../../npm-publish/DUI/package.json'; // 替换成你的 package.json 路径
incrementVersionAndPublish(packageJsonPath);
最后在打包的命令后面加上执行这个js文件
"build": "vite build && node npmPublish.js",
其它:
如果提示index.d.ts的包飘红信息,在src下面新建一个index.d.ts文件,在里面输入
declare module '包名'
在根目录安装包需要加 -w
pnpm install eslint typescript --save-dev -w
如果不想,则修改配置
pnpm config set ignore-workspace-root-check true