【vue组件库搭建06】组件库构建及npm发包

一、格式化目录结构

根据以下图片搭建组件库目录

index.js作为入口文件,将所有组件引入,并注册组件名称

复制代码
import { EButton } from "./Button";
export * from "./Button";
import { ECard } from "./Card";
export * from "./Card";

const cmpts = [EButton, ECard];

const EricUI = {
  install(Vue) {
    cmpts.forEach(cmpt => {
      Vue.component(cmpt.name, cmpt);
    });
  },
};

export default EricUI;

utils.js:给组件绑定注册方法

复制代码
export function withInstall(component) {
  component.install = app => {
    app.component(component.name, component);
  };
  return component;
}

在main.js中引入,方便后续使用

复制代码
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";

import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.less";

import EricUI from "../components";

const app = createApp(App).use(Antd).use(EricUI).mount("#app");

在docs\.vitepress\theme\index.ts同样引入

复制代码
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'

import Antd from 'ant-design-vue';
import './antd-overwrite.less'

import { AntDesignContainer } from '@vitepress-demo-preview/component'
import '@vitepress-demo-preview/component/dist/style.css'

import './style.css'

import HomeImage from './HomeImage.vue'
import EricUI from "../../../components";

export default {
  extends: DefaultTheme,
  Layout: () => {
    return h(DefaultTheme.Layout, null, {
      // https://vitepress.dev/guide/extending-default-theme#layout-slots
      'home-hero-image': () => h(HomeImage)
    })
  },
  enhanceApp({ app, router, siteData }) {
    app.use(Antd)
    app.use(EricUI)
    app.component('demo-preview', AntDesignContainer)
  }
} satisfies Theme

到此为止,组件库开发的组件可以在docs中展示:

EButton是我们开发的button组件,在Button.md中引入

效果:

二、组件库构建

新建build文件夹,以及以下三个文件:

复制代码
// base.confi.js

import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";

// 文档: https://vitejs.dev/config/
export default defineConfig({
  minify: false,
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        modifyVars: {
          "ant-prefix": "ant",
        },
      },
    },
  },
  plugins: [],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("../src", import.meta.url)),
    },
  },
});

// lib.config.js

import { defineConfig } from "vite";
import { fileURLToPath, URL } from "node:url";
import vue from "@vitejs/plugin-vue";
import lessEntry from "./vite-plugin-less-entry";
import baseConfig from "./base.config";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { viteStaticCopy } from "vite-plugin-static-copy";

export default defineConfig({
  ...baseConfig,
  build: {
    sourcemap: true,
    outDir: "lib",
    lib: {
      entry: fileURLToPath(new URL("../components/index.js", import.meta.url)),
      name: "EricUI",
      fileName: format => `eric-ui.${format}.js`,
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: [
        "vue",
        "@ant-design/icons-vue",
        "vxe-table",
        "xe-utils",
        "@vitepress-demo-preview/component",
        "@vitepress-demo-preview/plugin",
      ],
    },
  },
  plugins: [
    vue(),
    vueJsx(),
    viteStaticCopy({
      targets: [
        {
          src: "components/**/*.less",
          dest: "/",
        },
      ],
      structured: true,
    }),
    lessEntry({
      // 生成的入口文件名
      entry: "components",
      // libPath需要与viteStaticCopy中的dest保持一致
      libPath: "components",
      name: "style",
    }),
  ],
});

// vite-plugin-less-entry.js

import path from "node:path";
import fs from "fs-extra";

const name = "vite-plugin-custom-less-entry";
export const formatConsole = msg => `[${name}] ${msg}`;
/**
 * 生成项目less的入口文件
 */
export default function lessEntryPlugin({ entry, libPath, name }) {
  let outputed = false;
  let rootConfig = null;
  return {
    name,
    apply: "build",
    order: "post",

    configResolved(config) {
      rootConfig = config;
    },

    writeBundle() {
      if (outputed) {
        return;
      }
      outputed = true;

      // 遍历entry下的index.less文件,生成${name}.less文件
      const componentsPath = path.join(rootConfig.root, entry);

      let componentsLessContent = "";
      fs.readdir(componentsPath, (err, files) => {
        files.forEach(file => {
          if (fs.existsSync(path.join(componentsPath, file, "index.less"))) {
            componentsLessContent += `@import "./${libPath}/${path.posix.join(
              file,
              "index.less"
            )}";\n`;
          }
        });

        const lessEntryFile = path.join(
          rootConfig.root,
          rootConfig.build.outDir,
          `${name}.less`
        );
        fs.outputFile(lessEntryFile, componentsLessContent, err => {
          if (err) {
            console.error(formatConsole("Failed to generate less entry file"));
          } else {
            console.info(
              formatConsole("Successfully generated less entry file")
            );
          }
        });
      });
    },
  };
}

配置package.json:

复制代码
{
  "name": "eric-ui-lib",
  "version": "0.0.2",
  "description": "eric-ui组件库",
  "main": "lib/eric-ui.umd.js",
  "module": "lib/eric-ui.es.js",
  "files": [
    "lib"
  ],
  "keywords": [
    "eric-ui",
    "eric",
    "ui"
  ],
  "author": "Eric",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "build:lib": "vite build --config ./build/lib.config.js",
    "preview": "vite preview",
    "docs:dev": "vitepress dev docs",
    "docs:build": "vitepress build docs",
    "docs:preview": "vitepress preview docs"
  },
  "dependencies": {
    "@vitepress-demo-preview/component": "^2.3.2",
    "@vitepress-demo-preview/plugin": "^1.2.3",
    "ant-design-vue": "^3.2.20",
    "fs-extra": "^11.2.0",
    "less-loader": "^12.2.0",
    "vite-plugin-static-copy": "^1.0.6",
    "vue": "^3.4.29"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.5",
    "@vitejs/plugin-vue-jsx": "^4.0.0",
    "less": "^4.2.0",
    "vite": "^5.3.1",
    "vitepress": "^1.2.3"
  }
}

三、npm发布

npm login 登录,没有注册的自行注册

npm publish

查看npm,即发布成功

相关推荐
PPPPickup1 分钟前
easychat项目复盘---获取联系人列表,联系人详细,删除拉黑联系人
java·前端·javascript
老前端的功夫2 分钟前
前端高可靠架构:医疗级Web应用的实时通信设计与实践
前端·javascript·vue.js·ubuntu·架构·前端框架
前端大卫38 分钟前
【重磅福利】学生认证可免费领取 Gemini 3 Pro 一年
前端·人工智能
孜燃1 小时前
Flutter APP跳转Flutter APP 携带参数
前端·flutter
脾气有点小暴1 小时前
前端页面跳转的核心区别与实战指南
开发语言·前端·javascript
lxh01131 小时前
最长递增子序列
前端·数据结构·算法
vipbic1 小时前
我封装了一个“瑞士军刀”级插件,并顺手搞定了自动化部署
vue.js·nuxt.js
Youyzq2 小时前
前端项目发布到cdn上css被编译失效问题rgba失效和rgb失效
前端·css·算法·cdn
San30.2 小时前
深入 JavaScript 内存机制:从栈与堆到闭包的底层原理
开发语言·javascript·udp
Fantastic_sj2 小时前
Vue3相比Vue2的改进之处
前端·javascript·vue.js