【四】组件库打包

398 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

两种打包方式:

  • 全量打包:打包全部组件
  • 按需打包:单个组件打包

之所以要这么做是因为用户有时候希望只引入那些项目中用到的组件,这样可以减小最终的项目体积

全量打包

  1. 编写一个基于 vite 的创建脚本 build.ts
  2. 设置一个入口文件 entry.ts
  3. 入口文件中具名导出我们所编写的所有组件
  4. 实现并默认导出一个 vue 插件

类似于:

export {
  Button,
  // 其他组件...
}
export default {
  install(app: App): void {
    [
      ButtonInstall,
      // 其他组件的install
    ].forEach(p => app.use(p as any))
  }
}

1、添加入口文件

scripts/entry.ts 为全量打包的入口文件

// 1、引入实现的组件,并批量导出
import type { App } from "vue";
import ButtonPlugin, { Button } from "../src/button";

// 2、导出这些组件
export {
  Button,
};

const installs = [ButtonPlugin];

// 3、导出一个vue插件
export default {
  install(app: App): void {
    installs.forEach(p => app.use(p));
  },
};

2、添加打包脚本

scripts/build.js :打包创建脚本

// 这里是js,因为将来是使用node去执行的

// 引入vite导出的build方法,用它来创建
import Vue from "@vitejs/plugin-vue";
import VueJsx from "@vitejs/plugin-vue-jsx";
import { defineConfig, build } from "vite";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 基础配置
const baseConfig = defineConfig({
  configFile: false,
  publicDir: false,
  plugins: [
    Vue(),
    VueJsx(),
  ],
});

// 入口文件
const entryFile = path.resolve(__dirname, "./entry.ts");
// 输出目录
const outputDir = path.resolve(__dirname, "../build");

// rollup配置
const rollupOptions = {
  // 外置(不会打包进组件库)
  external: ["vue", "vue-router"],
  output: {
    // iife格式下有全局调用
    globals: {
      vue: "Vue",
    },
  },
};

// 全量构建
const buildAll = async() => {
  await build(
    defineConfig({
      ...baseConfig,
      build: {
        rollupOptions,
        lib: {
          entry: entryFile,
          name: "hay-ui",
          fileName: "hay-ui",
          formats: ["es", "umd"],
        },
        outDir: outputDir,
      },
    }),
  );
};

// 执行
const buildLib = async() => {
  await buildAll();
};

buildLib();

3、添加 script 命令

package.json

{
  "script": {
    "build:components": "node ./scripts/build.js"
  }
}

4、打包并使用

执行命令:npm run build:components后,打包成功

  • 修改 main.ts
// 使用全量导出
import HayUI from "../build/hay-ui.js";

createApp(App)
  .use(HayUI)
  .mount("#app");

可以看到页面上依旧正常

5、自动生成库中的 package.json

  • 安装依赖
pnpm add -D fs-extra
  • 修改 build.js 文件
import fsExtra from "fs-extra";

// 生成package.json(库中的)
const createPackageJson = () => {
  const fileStr = `{
    "name": "hay-ui",
    "version": "0.0.0",
    "main": "hay-ui.umd.cjs",
    "module": "hay-ui.js",
    "author": "hayesLv",
    "description": "组件库",
    "repository": {
      "type": "git",
      "url": "git+https://github.com/hayeslv/hay-ui.git"
    },
    "keywords": ["vue3", "组件库", "tsx", "UI"],
    "license": "ISC",
    "bugs": {
      "url": "https://github.com/hayeslv/hay-ui/issues"
    }
  }`;

  fsExtra.outputFile(path.resolve(outputDir, "package.json"), fileStr, "utf-8");
};

// 全量构建
const buildAll = async() => {
  await build(/* ... */);
  // 生成package.json
  createPackageJson();
};

此时再执行打包命令,就可以额外生成 package.json 文件了

按需打包

// build.js

// 组件目录
const componentsDir = path.resolve(__dirname, "../src")

// 生成package.json(库中的)
const createPackageJson = (name) => {
  const fileStr = `{
    "name": "${name ? name : "hay-ui"}",
    "version": "0.0.0",
    "main": "${name ? "index.umd.cjs" : "hay-ui.umd.cjs"}",
    "module": "${name ? "index.js" : "hay-ui.js"}",
    "author": "hayesLv",
    "description": "组件库",
    "repository": {
      "type": "git",
      "url": "git+https://github.com/hayeslv/hay-ui.git"
    },
    "keywords": ["vue3", "组件库", "tsx", "UI"],
    "license": "ISC",
    "bugs": {
      "url": "https://github.com/hayeslv/hay-ui/issues"
    }
  }`;

  if(name) {
    // 单个组件,输出对应的package.json
    fsExtra.outputFile(path.resolve(outputDir, `${name}/package.json`), fileStr, "utf-8");
  } else {
    // 全量
    fsExtra.outputFile(path.resolve(outputDir, "package.json"), fileStr, "utf-8");
  }
};

// 但组件按需构建
const buildSingle = async name => {
  await build(
    defineConfig({
      ...baseConfig,
      build: {
        rollupOptions,
        lib: {
          entry: path.resolve(componentsDir, name),
          name: "index",
          fileName: "index",
          formats: ["es", "umd"],
        },
        outDir: path.resolve(outputDir, name),
      },
    }),
  );
  createPackageJson(name);
};

// 执行
const buildLib = async() => {
  // 全量打包
  await buildAll();

  // 按需打包
  fsExtra.readdirSync(componentsDir)
    .filter(name => {
      // 只要目录,不要文件,且目录中包含index.ts
      const componentDir = path.resolve(componentsDir, name);
      const isDir = fsExtra.lstatSync(componentDir).isDirectory();
      return isDir && fsExtra.readdirSync(componentDir).includes("index.ts");
    })
    .forEach(async name => {
      // 此时已经读出来了符合要求的目录,这里开始打包
      await buildSingle(name);
    });
};

执行打包命令:npm run build:components

可以看到 button 单独打包出来了