如何利用 `Vite` 的 `lib` 模式,将 Vue 组件库打包为多种格式,并支持按需引入?

Vite Lib 模式:让你的 Vue 组件库“变身大法”!

各位观众,各位靓仔靓女,晚上好!我是今天的讲师,大家可以叫我“组件优化小能手”。今天咱们来聊聊如何用 Vite 的 lib 模式,把你的 Vue 组件库打造成一个“百变金刚”,支持多种格式,还能按需引入,让你的用户用得舒心,你赚得开心!

1. 啥是 Vite 的 Lib 模式?

想象一下,你要做一个 Vue 组件库,里面有各种各样的组件,比如按钮、输入框、弹窗等等。你希望别人能方便地使用你的组件,最好是想用哪个就引入哪个,而不是把整个组件库都打包进去,那样太浪费资源了!

Vite 的 lib 模式就是为了解决这个问题而生的。它可以让你把你的组件库打包成各种各样的格式,比如 CommonJS、ES Module、UMD 等等,这样你的用户就可以根据自己的项目选择最合适的格式来引入你的组件。同时,结合一些插件,还可以实现按需引入,让你的组件库更加轻量级。

简单来说,lib 模式就是 Vite 提供的一种专门用于构建库的配置模式,它可以让你更灵活地控制打包输出。

2. 准备工作:搭建一个简单的 Vue 组件库

咱们先来搭一个简单的 Vue 组件库,里面就放两个组件:一个按钮组件 (MyButton.vue) 和一个输入框组件 (MyInput.vue)。

MyButton.vue:

<template>
  <button class="my-button" @click="handleClick">
    {{ label }}
  </button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    label: {
      type: String,
      default: 'Click Me!'
    }
  },
  methods: {
    handleClick() {
      this.$emit('click');
    }
  }
}
</script>

<style scoped>
.my-button {
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 10px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  cursor: pointer;
}
</style>

MyInput.vue:

<template>
  <input
    type="text"
    class="my-input"
    :value="value"
    @input="handleInput"
  />
</template>

<script>
export default {
  name: 'MyInput',
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods: {
    handleInput(event) {
      this.$emit('input', event.target.value);
    }
  }
}
</script>

<style scoped>
.my-input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
</style>

然后,咱们再创建一个 index.js 文件,作为组件库的入口文件:

import MyButton from './MyButton.vue';
import MyInput from './MyInput.vue';

const components = [
  MyButton,
  MyInput
];

const install = (Vue) => {
  components.forEach(component => {
    Vue.component(component.name, component);
  });
};

export {
  MyButton,
  MyInput
};

export default {
  install
};

这个 index.js 文件做了两件事:

  1. 导入了所有的组件。
  2. 导出了一个 install 方法,用于注册组件。

3. 配置 Vite:让 Lib 模式发挥作用

接下来,咱们要配置 Vite,让它知道我们要用 lib 模式来打包我们的组件库。 修改 vite.config.js 文件,如果没有就创建一个。

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'url';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: fileURLToPath(new URL('./index.js', import.meta.url)),
      name: 'MyComponentLib',
      fileName: (format) => `my-component-lib.${format}.js`
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

咱们来解释一下这个配置:

  • entry: 指定组件库的入口文件,就是我们刚刚创建的 index.js
  • name: 指定组件库的名字,这个名字会在 UMD 格式中使用。
  • fileName: 指定输出的文件名,这里我们使用了一个函数,可以根据不同的 format 生成不同的文件名。
  • external: 指定哪些依赖不需要打包进组件库,这里我们指定了 vue,因为我们的组件库依赖 Vue,但是我们希望用户在自己的项目中引入 Vue,而不是把 Vue 也打包进我们的组件库。
  • globals: 在 UMD 格式下,我们需要为外部依赖提供一个全局变量,这里我们指定 vue 的全局变量是 Vue

需要注意的是: 这里的 rollupOptions 是给 Rollup 用的,Vite 内部使用了 Rollup 进行打包。

4. 打包组件库:见证奇迹的时刻

配置好了 Vite,咱们就可以运行 npm run build 命令来打包我们的组件库了。 (前提是在package.json中配置了 build 命令:"build": "vite build" )

打包完成后,你会发现 dist 目录下生成了以下文件:

  • my-component-lib.es.js: ES Module 格式的文件,适用于现代浏览器和模块化环境。
  • my-component-lib.umd.js: UMD 格式的文件,适用于各种环境,包括浏览器和 Node.js。

5. 使用组件库:感受按需引入的魅力 (配合插件)

现在,咱们来尝试使用我们刚刚打包好的组件库。首先,你需要创建一个新的 Vue 项目,或者在一个已有的 Vue 项目中使用。

5.1 完整引入

如果你想完整引入整个组件库,你可以这样:

import MyComponentLib from 'my-component-lib'; // 假设你已经将组件库发布到 npm 上了

Vue.use(MyComponentLib);

// 然后你就可以在你的组件中使用 MyButton 和 MyInput 了

但是,这样做会把整个组件库都打包进去,即使你只用到了一个组件。为了解决这个问题,咱们需要使用按需引入。

5.2 按需引入 (使用 vite-plugin-style-import 插件)

按需引入是指只引入你用到的组件,而不是把整个组件库都打包进去。为了实现按需引入,我们需要借助一些插件,比如 vite-plugin-style-import

首先,安装 vite-plugin-style-import

npm install vite-plugin-style-import -D

然后,修改 vite.config.js 文件:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'url';
import styleImport, { VantResolve } from 'vite-plugin-style-import'; // 引入 styleImport

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    styleImport({
      resolves: [VantResolve()], // 这里可以配置不同的组件库
    }),
  ],
  build: {
    lib: {
      entry: fileURLToPath(new URL('./index.js', import.meta.url)),
      name: 'MyComponentLib',
      fileName: (format) => `my-component-lib.${format}.js`
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

注意: 上面的 VantResolve() 只是一个例子,你需要根据你自己的组件库来配置 resolves。你需要告诉 vite-plugin-style-import 你的组件库的组件名和样式文件在哪里。 这里我们模拟一个MyComponentLibResolve

// MyComponentLibResolve.js

export function MyComponentLibResolve() {
  return {
    'MyButton': 'my-component-lib/MyButton.vue', // 组件路径
    'MyInput': 'my-component-lib/MyInput.vue',    // 组件路径
  };
}

然后修改 vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'url';
import styleImport from 'vite-plugin-style-import'; // 引入 styleImport
import { MyComponentLibResolve } from './MyComponentLibResolve'; // 引入自定义resolver

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    styleImport({
      resolves: [MyComponentLibResolve()], // 这里可以配置不同的组件库
    }),
  ],
  build: {
    lib: {
      entry: fileURLToPath(new URL('./index.js', import.meta.url)),
      name: 'MyComponentLib',
      fileName: (format) => `my-component-lib.${format}.js`
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

现在,你就可以在你的组件中直接使用 MyButtonMyInput 了,而不需要手动引入它们,vite-plugin-style-import 会自动帮你引入你用到的组件。

<template>
  <div>
    <MyButton label="按需引入的按钮" @click="handleClick" />
    <MyInput v-model="inputValue" />
  </div>
</template>

<script>
import { ref } from 'vue';
import MyButton from 'my-component-lib/MyButton.vue';  // 直接引入组件,不再需要Vue.use
import MyInput from 'my-component-lib/MyInput.vue';

export default {
  components: {
    MyButton,
    MyInput
  },
  setup() {
    const inputValue = ref('');

    const handleClick = () => {
      alert('按钮被点击了!');
    };

    return {
      inputValue,
      handleClick
    };
  }
};
</script>

重要提示: vite-plugin-style-import 的配置非常灵活,你可以根据你的组件库的结构来配置 resolves。你需要仔细阅读 vite-plugin-style-import 的文档,才能正确地使用它。 并且组件库本身也需要做一些适配才行,比如导出单个组件,而不是导出一个包含所有组件的对象。

6. 进阶技巧:更多打包姿势

除了上面介绍的基本用法,Vite 的 lib 模式还有很多高级用法,可以让你更加灵活地控制打包过程。

6.1 多入口打包

如果你的组件库比较复杂,你可以使用多入口打包,将不同的组件打包成不同的文件。例如,你可以把核心组件打包成一个文件,把扩展组件打包成另一个文件。

要实现多入口打包,你需要修改 vite.config.js 文件:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'url';

export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: {
        core: fileURLToPath(new URL('./src/core.js', import.meta.url)),
        extended: fileURLToPath(new URL('./src/extended.js', import.meta.url))
      },
      name: 'MyComponentLib',
      fileName: (format, entryName) => `${entryName}.${format}.js`
    },
    rollupOptions: {
      external: ['vue'],
      output: {
        globals: {
          vue: 'Vue'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

在这个配置中,entry 变成了一个对象,key 是入口的名字,value 是入口文件的路径。fileName 也变成了一个函数,可以根据入口的名字生成不同的文件名。

6.2 自定义 Rollup 配置

Vite 内部使用了 Rollup 进行打包,所以你可以通过 rollupOptions 来配置 Rollup。例如,你可以使用 Rollup 的插件来优化打包结果,或者修改 Rollup 的输出选项。

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'url';
import { terser } from 'rollup-plugin-terser'; // 引入 terser 插件

export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: fileURLToPath(new URL('./index.js', import.meta.url)),
      name: 'MyComponentLib',
      fileName: (format) => `my-component-lib.${format}.js`
    },
    rollupOptions: {
      external: ['vue'],
      output: {
        globals: {
          vue: 'Vue'
        }
      },
      plugins: [
        terser() // 使用 terser 插件压缩代码
      ]
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

在这个配置中,我们使用了 rollup-plugin-terser 插件来压缩代码,可以减小打包后的文件大小。

7. 总结:Vite Lib 模式,让你的组件库更上一层楼

Vite 的 lib 模式是一个非常强大的工具,可以让你轻松地打包 Vue 组件库,并支持多种格式和按需引入。只要你掌握了它的基本用法和一些高级技巧,你就可以打造出一个高质量、高性能的组件库,让你的用户用得舒心,你赚得开心!

重点回顾:

| 特性 | 描述 whh

| 配置项 | 描述

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注