Skip to content

ui 分包打包上架

ui分包

目录

bash

├── packages
   ├── ui
   ├── public
   ├── packages(最后把要打包的文件都放到这里面)
   ├── command
   ├── lib (打包后生成的)
   ├── vite.config.ts
   ├── package.json
   ├── packages
    ├── chooseButton
    ├── hooks
    ├── utils
    ├── index.ts
    ├── vue.d.ts
   ├── src
    ├── assets
    ├── hooks
    ├── utils
    ├── styles
    ├── router
    ├── App.vue
    ├── mian.ts
    ├── components
     ├── chooseButton
    ├── lib(外部依赖)
    ├── index.ts
    ├── src
    ├── index.vue
     ├── index.ts

第一步

安装vue

  • cd 到packages/ui目录下
bash
npm create vite@latest

安装elementuiplus

  • 执行命令
npm
pnpm add element-plus -S
  • main.ts
ts
import { createApp } from "vue";

import "./style.css";

import App from "./App.vue";

import ElementPlus from "element-plus";

import "element-plus/dist/index.css";

const app = createApp(App);

app.use(ElementPlus);

app.mount("#app");

创建command文件夹

  • build.js 里面的yjtestone换成你自己的
js
import { createRequire } from "module";
import { fileURLToPath } from "node:url";

// 使用require
const require = createRequire(import.meta.url);

const path = require("path");
// 使用文件路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const { defineConfig, build } = require("vite");
const vue = require("@vitejs/plugin-vue");
// const vueJsx = require('@vitejs/plugin-vue-jsx')

// 打包入口文件夹 ,找到packages下的所有文件夹
const entryDir = path.resolve(__dirname, "../packages");
// 出口文件夹,打包到lib文件夹下
const outDir = path.resolve(__dirname, "../lib");
// vite 基础配置
const baseConfig = defineConfig({
  configFile: false,
  publicDir: false,
  plugins: [vue()],
});

/* 单独打包 要引入的 */

const fsExtra = require("fs-extra");

const fs = require("fs");

// rollup 配置
const rollupOptions = {
  // vue 全家桶不需要打包
  external: ["vue"],
  output: {
    globals: {
      vue: "Vue",
    },
  },
};
// 全量打包配置
const buildAll = async () => {
  await build({
    ...baseConfig,
    build: {
      rollupOptions,
      lib: {
        entry: path.resolve(entryDir, "index.ts"),
        name: "index",
        fileName: "index",
        formats: ["es", "umd"],
      },
      outDir,
    },
  });
};

// 单组件打包
// name 就是最终的组件名称
const buildSingle = async (name) => {
  await build({
    ...baseConfig,
    build: {
      rollupOptions,
      lib: {
        entry: path.resolve(entryDir, name),
        name: "index",
        fileName: "index",
        formats: ["es", "umd"],
      },
      outDir: path.resolve(outDir, name),
    },
  });
};

// 每个组件生成package.json
const createJson = async (name) => {
  if (name) {
    const fileStr = `
    {
      "name": "yjtestone-${name}",
      "main": "index.umd.js",
      "module": "index.mjs",
      "style":"styles.css"
    }
    `;
    // 输出
    fsExtra.outputFile(
      path.resolve(outDir, `${name}/package.json`),
      fileStr,
      "utf-8",
    );
  } else {
    const fileStr = `
    {
    "name":"yjtestone",
    "version":"1.0.1",
    "main":"index.umd.js",
    "module":"index.mjs",
    "types":"index.d.ts",
    "author":{
      "name":"yj"
    },
    "keywords":[
      "element-plus",
      "ts",
      "封装组件",
      "二次封装",
      "element-plus二次封装",
      "yj"
    ]
    }
    `;
    // 输出
    fsExtra.outputFile(
      path.resolve(outDir, "./package.json"),
      fileStr,
      "utf-8",
    );
  }
};

// 生成index.d.ts 文件

const createDTS = async (name) => {
  const fileStr = `
  import { App } from 'vue'
    declare const _default: {
      install(app: App): void
  }
  export default _default
    `;
  // 输出
  if (name) {
    fsExtra.outputFile(
      path.resolve(outDir, `${name}/index.d.ts`),
      fileStr,
      "utf-8",
    );
  } else {
    fsExtra.outputFile(path.resolve(outDir, "./index.d.ts"), fileStr, "utf-8");
  }
};
// 打包成库
const buildLib = async () => {
  await buildAll();
  await createJson();
  await createDTS();
  // 获取到组件名称组成的数组
  const components = fs.readdirSync(entryDir).filter((name) => {
    // 判断是否是组件
    const componentsDir = path.resolve(entryDir, name);
    // 判断是否是一个目录
    const isDir = fs.lstatSync(componentsDir).isDirectory();
    // 是一个目录 并且包含index.ts
    return isDir && fs.readdirSync(componentsDir).includes("index.ts");
  });
  // 循环构建
  for (const name of components) {
    // 单独打包
    await buildSingle(name);
    // 生成package.json
    await createJson(name);
    // 生成index.d.ts
    await createDTS(name);
  }
};

buildLib();

tsconfig.src.json

  • 全局搜索tsconfig.src.json 里面增加include 对应的路径
json
// tsconfig.src.json
{
  // 继承基础配置
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    // 组件库依赖浏览器的 DOM API
    "lib": ["ESNext", "DOM", "DOM.Iterable"],
    "types": ["node"]
  },
  "include": [
    "typings/env.d.ts",
    "packages/**/src",
    "packages/ui/packages/hooks/**/*.ts",
    "packages/ui/**/*.ts",
    "src/**/*.ts"
  ],
  "exclude": ["**/*.config.*"]
}

修改package.json

json
{
  "name": "ui",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build:components": "node ./command/build.js",
    "build": "npm run build:components",
    "preview": "vite preview"
  },
  "dependencies": {
    "element-plus": "^2.13.2",
    "vue": "^3.5.24"
  },
  "devDependencies": {
    "@types/node": "^24.10.1",
    "@vitejs/plugin-vue": "^6.0.1",
    "@vue/tsconfig": "^0.8.1",
    "sass": "^1.97.3",
    "typescript": "~5.9.3",
    "vite": "^7.2.4",
    "vue-tsc": "^3.1.4"
  }
}

第二步

创建组件

  • 新建 src/components/index.ts
ts
import { type App } from "vue";
import YJButton from "./chooseButton";

const components = [YJButton];

export default {
  install(app: App) {
    components.map((item) => {
      app.use(item);
    });
  },
};
  • 新建 src/components/chooseButton/index.ts
ts
import { type App } from "vue";
import YJButton from "./src/index.vue";

// 让这个组件可以通过use的形式使用
export default {
  install(app: App) {
    app.component("YJButton", YJButton);
  },
};
  • 新建 src/components/chooseButton/src/index.vue
vue
<template>
  <div>
    <el-button type="success" @click="handleClick">{{ name }}</el-button>
  </div>
</template>

<script setup lang="ts">
import { useButton } from "../../../hooks/chooseButton/useButton";

const { handleClick, name } = useButton();
</script>

<style scoped></style>
  • 新建hooks/chooseButton/useButton.ts
ts
import { ref, onMounted } from "vue";

export const useButton = () => {
  // 名字
  const name = ref("点击我");
  // 点击事件
  const handleClick = () => {
    window.alert("弹出层");
  };

  onMounted(() => {
    console.log("Button里面的onMounted");
  });

  return {
    name,
    handleClick,
  };
};

安装路由

  • 安装路由
bash
npm install vue-router@next
  • 新建 router/router.ts
ts
// 写一个vue-router 入口文件
import { createRouter, createWebHistory } from "vue-router";
import ChooseButtonPage from "../pages/chooseButton/index.vue";

//
const routes = [
  {
    path: "/chooseButton",
    name: "home",
    component: ChooseButtonPage,
    meta: { title: "按钮" }, // 可以在 meta 中存放自定义信息,如页面标题
  },
];

// 创建 router 实例
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL), // 使用 HTML5 模式
  routes,
});

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = `${to.meta.title || "默认标题"} - 我的网站`;

  // 这里可以添加登录验证逻辑
  // const isAuthenticated = checkAuth()
  // if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // else next()

  next();
});

// 全局后置钩子
router.afterEach(() => {
  // 完成导航后的逻辑,例如页面滚动到顶部
  window.scrollTo(0, 0);
});

export default router;

新建pages/chooseButton/index.vue

vue
<template>
  <div>
    <YJButton></YJButton>
  </div>
</template>

<script setup lang="ts"></script>

引用

  • main.ts
ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/router";
import ComponentsAll from "./components";
const app = createApp(App);
app.use(router);
app.use(ComponentsAll);
app.mount("#app");

App.vue

vue
<template>
  <div>
    <router-view></router-view>
  </div>
</template>

新建src/utils/tools.ts

ts
// 写一个生成年月日的函数 不足双位 前面补0 就是测试打包
export const formatDate = (date: Date) => {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return `${year}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`;
};

搬家

  • 把components下面的所有文件复制一份搬到ui/packages下面

  • 把 hooks 下面的的所有文件复制一份搬到ui/packages/hooks下面

  • 把 utils 下面的的所有文件复制一份搬到ui/packages/utils下面

  • 把 styles 下面的的所有文件复制一份搬到ui/packages/styles下面

  • ui/packages/vue.d.ts

ts
/// <reference types="vite/client" />

declare module "*.vue" {
  import { DefineComponent } from "vue";

  const component: DefineComponent<{}, {}, any>;
  export default component;
}

第三步 打包

  • 根目录 执行
bash
npm run build

发布

  • 打包成功后这个时候在ui下面会多了一个文件夹叫做lib

  • cd 到packages/ui

  • 执行命令

bash
npm publish ./lib

本地使用

本地安装

  • apps/web下面安装vue,element-plus 等等

安装包

bash
npm install 文件路径到你的那个lib文件夹 例如 (D:\Code\MyCode\testelement1\packages\ui\lib)

使用

  • main.ts
ts
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";

// yjtestone 换成你那个包的名字
import CustomComponents from "yjtestone/index.js";

const app = createApp(App);

app.use(ElementPlus);

app.use(CustomComponents);

app.mount("#app");
  • 组件目录使用
vue
<template>
  <div>
    <YJButton></YJButton>
  </div>
</template>

<script setup lang="ts"></script>