Monorepo使用和npm包开发

Monorepo使用和npm包开发

什么是Monorepo

image

单一代码管理库

是一种将多个项目的源代码存储在同一个版本库中的开发方法。这种方法可以简化代码管理、促进代码共享,并统一构建和部署流程。

  • 统一代码管理
  • 共享代码资源
  • 一致的构建部署流程
  • 统一的版本控制
  • 简化依赖管理

闲话不扯,开始

总共就这么几个步骤:
  1. 新建目录
  2. 配置pnpm-workspace
  3. 配置packages
  4. 配置命令

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

正常的话,我们应该看到我们带端口号的访问链接了

image

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"

我们在根目录跑对应的名称就行了

image

接着来处理公共的库

在根目录或者其它的地方新建一个目录

  1. 我建了一个目录function // 大家命名认真一点啊
pnpm init -y
// name 自已配,比如我写的wild
  1. 新建一个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;
}
  1. 在里面新建一个index.js文件,引入这个文件,再导出去。单纯的为了导入出口
import * as url from './url.js'
export default {
    ...url,
}

package.json里面记往跟这个index.js文件一致

  1. 去掉项目目录安装,当然也可以从根目录安装,命令不一样
import wild from 'wild'

console.log(wild.etURLParameters())

image

其它项目也可以这样引用。

来个组件库

在根目录或者其它的地方新建一个目录

新建一个目录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'

正常引用就行了

image

打包发布

我一开始的想法是搞一个工程目录,用来开发组件,函数,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文件

image

那就自已生成package.json吧。在这个目录运行 pnpm init -y

生成package.json文件,输入你的包的名称,修改 main:'./lib/index.js'

写上readme.md的说明文件。

npm login
npm publish  或者 npm publish --access public
// 如果你是组织的话,需要加公开的参数,私有的需要收费

后续

每次都要打包,再npm publish很烦,关键还要修改版本号,很烦。

人懒就想了个办法,新建了一个js文件

  1. 读取build目录的package.json里面的版本号
  2. 将版本号修改+1
  3. 在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

image