vite 发布基于 Ant Design Vue 二次封装的组件库到 npm

5,845 阅读2分钟

我正在参加「掘金·启航计划」

之前对 Ant Design TreeSelect 组件进行了二次封装,可查看文章Antd TreeSelect 树选择控件“二次封装”及封装原理,现在使用 vite 将该组件发布到 npm 上,整体打包发布流程和使用 vite 发布自定义组件到 npm基本一致,如下只列出差异部分

Ant Design Vue 组件库按需引入

  1. 安装依赖
pnpm i ant-design-vue
pnpm i vite-plugin-components -D
  1. 按需配置
// 2.1 新增src/libs/antdv.js
import { TreeSelect } from "ant-design-vue";
const components = [TreeSelect];

export function setupAntd(app) {
  components.forEach((component) => {
    app.use(component);
  });
}

// 2.2 main.js引入
import { setupAntd } from "./libs/antdv";

const app = createApp(App);
setupAntd(app);
app.mount("#app");

// 2.3 vite.config.js配置
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";

export default defineConfig({
  plugins: [
    vue(),
    ViteComponents({
      customComponentResolvers: [AntDesignVueResolver()],
    }),
  ],
});

组件编写

完成treeSelect树选择组件的二次封装,这个比较简陋,只是做为测试发布使用

  1. 新建 packages/treeSelect/src/index.vue
<template>
  <a-tree-select
    v-bind="$attrs"
    placeholder="请选择"
    style="width: 100%"
    :tree-data="departSource"
    :dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
    show-search
    allow-clear
    tree-default-expand-all
    treeNodeFilterProp="title"
    :getPopupContainer="(triggerNode) => triggerNode.parentNode"
    @select="handleSelect"
  >
  </a-tree-select>
</template>

<script>
  import { watchEffect } from "vue";
  export default {
    name: "tTreeSelect",
    props: {
      // 数据源
      departSource: {
        type: Array,
        default: [],
      },
    },
    setup(props, context) {
      console.log("props:", props);
      console.log("context.attrs:", context.attrs); // $attrs
      console.log("context.slots:", context.slots); // $slots
      console.log("context.emit:", context.emit); // $slots
      console.log("context.expose:", context.expose); // vue3.2 新增

      watchEffect(() => {
        console.log(`value is: ` + context.attrs.value);
      });

      const handleSelect = (value, node, extra) => {
        console.log(value, node, extra);
      };

      return { handleSelect };
    },
  };
</script>

<style scoped></style>
  1. 新建 packages/treeSelect/index.js 文件
import tTreeSelect from "./src/index.vue";

tTreeSelect.install = function (Vue) {
  Vue.component(tTreeSelect.name, tTreeSelect);
};

export default tTreeSelect;

打包配置

  1. 修改 vite.config.js 文件
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 安装插件
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";
import path from "path";

const resolve = (dir) => path.join(__dirname, dir);

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    ViteComponents({
      customComponentResolvers: [AntDesignVueResolver()],
    }),
  ],
  resolve: {
    alias: {
      "@": resolve("src"),
      packages: resolve("packages"),
    },
  },
  build: {
    lib: {
      // 2. 基于Ant Design Vue TreeSelect二次封装的组件的打包配置
      entry: resolve("packages/treeSelect/index.js"),
      name: "TTreeSelect",
      fileName: (format) => `tTreeSelect.${format}.js`,
    },
    // 自定义构建配置,可直接调整底层Rollup选项;Rollup有一套预设
    // https://rollupjs.org/guide/en/#big-list-of-options
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ["vue"],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: "Vue",
        },
      },
    },
  },
});
  1. 修改 package.json 文件
{
  "name": "vite-npm-package",
  "version": "2.0.0",
  "description": "使用vite发布自定义组件到npm",
  "files": ["dist"],
  "main": "./dist/tTreeSelect.umd.js",
  "module": "./dist/tTreeSelect.es.js",
  "exports": {
    ".": {
      "import": "./dist/tTreeSelect.es.js",
      "require": "./dist/tTreeSelect.umd.js"
    },
    "./dist/style.css": {
      "import": "./dist/style.css",
      "require": "./dist/style.css"
    }
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "license": "MIT",
  "private": false,
  "author": "zptime",
  "dependencies": {
    "ant-design-vue": "^3.0.0-alpha.14",
    "vue": "^3.2.23"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.10.2",
    "vite": "^2.7.0",
    "vite-plugin-components": "^0.13.3"
  }
}

使用举例

<template>
  <div class="demo">
    <t-tree-select
      :depart-source="departTree"
      v-model:value="departId"
      :fieldNames="fieldNames"
    />
  </div>
</template>

<script setup>
  // 1. 本地引入
  import tTreeSelect from "packages/treeSelect/src/index.vue";

  // 2. 打包后引入(pnpm build)
  import tTreeSelect from "../../dist/tTreeSelect.es.js";
  import "../../dist/style.css";

  // 3. 发包到npm后使用 v2.0.0
  import tTreeSelect from "vite-npm-package";
  import "vite-npm-package/dist/style.css";

  const fieldNames = {
    children: "children",
    label: "name",
    key: "id",
    value: "id",
  };

  // 测试数据
  const departTree = [
    {
      id: 1,
      name: "根部门",
      departNumber: 6,
      children: [
        {
          id: 11,
          pid: 1,
          name: "一级部门1",
          departNumber: 2,
          children: [
            { id: 111, pid: 11, name: "二级部门1", departNumber: 0 },
            { id: 112, pid: 11, name: "二级部门2", departNumber: 0 },
          ],
        },
        { id: 21, pid: 1, name: "一级部门2", departNumber: 0 },
        { id: 31, pid: 1, name: "一级部门3", departNumber: 0 },
        { id: 41, pid: 1, name: "一级部门4", departNumber: 0 },
        { id: 51, pid: 1, name: "一级部门5", departNumber: 0 },
        {
          id: 61,
          pid: 1,
          name: "一级部门6",
          departNumber: 2,
          children: [
            { id: 611, pid: 61, name: "二级部门3", departNumber: 0 },
            { id: 612, pid: 61, name: "二级部门4", departNumber: 0 },
          ],
        },
      ],
    },
  ];

  const departId = 11;
</script>

<style scoped></style>

代码展示:

ic_npm_11.png

使用效果展示:

ic_npm_12.png

自定义组件库发布

自定义组件库实现发布,主要是以之前的两个组件 tButton 和 tTreeSelect为例,当然你也可以发布更多

  1. 在 package 文件夹下新增 index.js 文件,将自定义组件一起引入
import tButton from "./button";
import tTreeSelect from "./treeSelect";

const components = [tButton, tTreeSelect];

// 如果想通过CDN方式引入,需要编写install函数
// 注册组件
const install = function (Vue) {
  components.forEach((component) => {
    Vue.component(component.name, component);
  });
};

if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}
// 导出组件
export default {
  install,
  tButton,
  tTreeSelect,
};
  1. 修改 vite.config.js 配置,处理 packages 的打包
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";
import path from "path";

const resolve = (dir) => path.join(__dirname, dir);

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    ViteComponents({
      customComponentResolvers: [AntDesignVueResolver()],
    }),
  ],
  resolve: {
    alias: {
      "@": resolve("src"),
      packages: resolve("packages"),
    },
  },
  build: {
    lib: {
      // 3. 发布自定义组件库,包含tButton 和 tTreeSelect两个组件
      entry: resolve("packages/index.js"),
      name: "tAntDesign",
      fileName: (format) => `tAntDesign.${format}.js`,
    },
    rollupOptions: {
      external: ["vue"],
      output: {
        globals: {
          vue: "Vue",
        },
      },
    },
  },
});
  1. 修改 package.json 文件,调整发布的相关信息
{
  "name": "vite-npm-package",
  "version": "3.0.0",
  "description": "使用vite发布自定义组件库到npm",
  "files": ["dist"],
  "main": "./dist/tAntDesign.umd.js",
  "module": "./dist/tAntDesign.es.js",
  "exports": {
    ".": {
      "import": "./dist/tAntDesign.es.js",
      "require": "./dist/tAntDesign.umd.js"
    },
    "./dist/style.css": {
      "import": "./dist/style.css",
      "require": "./dist/style.css"
    }
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "license": "MIT",
  "private": false,
  "author": "zptime",
  "dependencies": {
    "ant-design-vue": "^3.0.0-alpha.14",
    "vite-npm-package": "^2.0.0",
    "vue": "^3.2.23"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.10.2",
    "vite": "^2.7.0",
    "vite-plugin-components": "^0.13.3"
  }
}
  1. 打包发布
// 打包
pnpm build

// 发布
npm publish
  1. 使用测试:发布成功后,在本地下载测试
<template>
  <div class="demo">
    本地引入
    <t-button class="block" />
    <t-tree-select
      :depart-source="departTree"
      v-model:value="departId"
      :fieldNames="fieldNames"
    />
  </div>
</template>

<script setup>
  import tAntDesign from "vite-npm-package";
  import "vite-npm-package/dist/style.css";
  const { tButton, tTreeSelect } = tAntDesign;
</script>