Close
升级到 Vue 3 | Vue 2 EOL

将 Vue 组件打包到 npm

基本示例

Vue 组件本质上是为了重复使用而设计的。当组件仅在单个应用程序中使用时,这很容易。但是,如何编写一次组件并在多个站点/应用程序中使用它呢?也许最简单的解决方案是通过 npm。

通过将您的组件打包以通过 npm 共享,它可以被导入/需要到构建过程中,以用于完整的 Web 应用程序

import MyComponent from 'my-component';

export default {
components: {
MyComponent,
},
// rest of the component
}

或者甚至通过浏览器中的<script>标签直接使用

<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/my-component"></script>
...
<my-component></my-component>
...

这不仅可以帮助您避免复制/粘贴组件,还可以让您回馈 Vue 社区!

我不能直接共享.vue文件吗?

Vue 已经允许将组件编写为单个文件。因为单文件组件 (SFC) 已经只是一个文件,所以您可能会问

“为什么人们不能直接使用我的.vue文件?这不是共享组件最简单的方法吗?”

没错,您可以直接共享.vue文件,任何使用包含 Vue 编译器的Vue 构建的人都可以立即使用它。此外,SSR 构建使用字符串连接作为优化,因此在这种情况下可能更喜欢.vue文件(有关详细信息,请参阅将组件打包到 npm > SSR 使用)。但是,这排除了希望通过<script>标签直接在浏览器中使用组件的任何人,使用运行时构建的任何人,或者不理解如何处理.vue文件的构建过程。

通过 npm 正确打包您的 SFC 以进行分发,可以使您的组件以一种可以在任何地方使用的形式共享!

将组件打包到 npm

在本节中,假设以下文件结构

package.json
build/
rollup.config.js
src/
wrapper.js
my-component.vue
dist/

在本文档中,会引用上面列出的 package.json 文件。这些示例中使用的文件是手动生成的,将包含讨论/任务所需的最小配置。您的 package.json 文件可能包含比这里列出的内容多得多的内容。

npm 如何知道要向浏览器/构建过程提供哪个版本?

npm 使用的 package.json 文件实际上只需要一个版本 (main),但事实证明,我们并不局限于此。我们可以通过指定另外 2 个版本 (moduleunpkg) 来解决最常见的用例,并使用browser字段提供对.vue文件的访问。一个示例 package.json 将如下所示

{
"name": "my-component",
"version": "1.2.3",
"main": "dist/my-component.umd.js",
"module": "dist/my-component.esm.js",
"unpkg": "dist/my-component.min.js",
"browser": {
"./sfc": "src/my-component.vue"
},
...
}

当使用 webpack 2+、Rollup 或其他现代构建工具时,它们将拾取module构建。旧版应用程序将使用main构建,unpkg构建可以直接在浏览器中使用。事实上,当有人将您的模块的 URL 输入其服务时,unpkg cdn 会自动使用它!

SSR 使用

您可能已经注意到一些有趣的事情 - 浏览器不会使用browser版本。这是因为该字段实际上是用来让作者提供对捆绑器的提示,这些提示反过来会为客户端使用创建自己的包。凭借一点创造力,这个字段允许我们将别名映射到.vue文件本身。例如

import MyComponent from 'my-component/sfc'; // Note the '/sfc'

兼容的捆绑器在 package.json 中看到browser定义,并将对my-component/sfc的请求转换为my-component/src/my-component.vue,从而导致使用原始.vue文件。现在,SSR 过程可以使用它需要的字符串连接优化来提高性能。

注意:当直接使用.vue组件时,请注意scriptstyle标签所需的任何预处理。这些依赖项将传递给用户。考虑提供“普通”SFC 以尽可能保持轻量级。

如何制作多个版本的组件?

无需多次编写模块。可以在一步内准备模块的所有 3 个版本,只需几秒钟。此处的示例使用Rollup,因为它配置简单,但其他构建工具也可以进行类似的配置 - 有关此决定的更多详细信息,请参阅此处。package.json 的scripts部分可以使用每个构建目标的单个条目更新,以及一个更通用的build脚本,该脚本在一遍中运行所有这些条目。示例 package.json 文件现在如下所示

{
"name": "my-component",
"version": "1.2.3",
"main": "dist/my-component.umd.js",
"module": "dist/my-component.esm.js",
"unpkg": "dist/my-component.min.js",
"browser": {
"./sfc": "src/my-component.vue"
},
"scripts": {
"build": "npm run build:umd & npm run build:es & npm run build:unpkg",
"build:umd": "rollup --config build/rollup.config.js --format umd --file dist/my-component.umd.js",
"build:es": "rollup --config build/rollup.config.js --format es --file dist/my-component.esm.js",
"build:unpkg": "rollup --config build/rollup.config.js --format iife --file dist/my-component.min.js"
},
"devDependencies": {
"rollup": "^1.17.0",
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^11.1.0",
"rollup-plugin-vue": "^5.0.1",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10"
...
},
...
}

请记住,如果您有现有的 package.json 文件,它可能包含比这个文件多得多的内容。这仅仅说明了一个起点。此外,devDependencies 中列出的(而不是它们的版本)是 rollup 创建上面提到的三个单独构建(umd、es 和 unpkg)的最低要求。随着新版本的发布,应根据需要更新它们。

我们对 package.json 的更改已完成。接下来,我们需要一个小的包装器来导出/自动安装实际的 SFC,再加上一个最小的 Rollup 配置,我们就完成了!

我的打包组件是什么样子的?

根据您的组件的使用方式,它需要公开为CommonJS/UMD javascript 模块,ES6 javascript 模块,或者在<script>标签的情况下,它将通过Vue.use(...)自动加载到 Vue 中,因此它立即可供页面使用。这是通过一个简单的 wrapper.js 文件来完成的,该文件处理模块导出和自动安装。该包装器完整内容如下所示

// Import vue component
import component from './my-component.vue';

// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('MyComponent', component);
}

// Create module definition for Vue.use()
const plugin = {
install,
};

// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}

// To allow use as module (npm/webpack/etc.) export component
export default component;

请注意第一行直接导入您的 SFC,最后一行保持不变地导出它。正如代码中其余部分的注释所示,包装器为 Vue 提供了一个 install 函数,然后尝试检测 Vue 并自动安装组件。完成了 90% 的工作,现在该冲刺到终点了!

如何配置 Rollup 构建?

有了 package.jsonscripts 部分和 SFC 包装器,剩下的就是确保 Rollup 正确配置。幸运的是,这可以通过一个只有 16 行的 rollup.config.js 文件来完成。

import commonjs from '@rollup/plugin-commonjs'; // Convert CommonJS modules to ES6
import vue from 'rollup-plugin-vue'; // Handle .vue SFC files
import buble from '@rollup/plugin-buble'; // Transpile/polyfill with reasonable browser support
export default {
input: 'src/wrapper.js', // Path relative to package.json
output: {
name: 'MyComponent',
exports: 'named',
},
plugins: [
commonjs(),
vue({
css: true, // Dynamically inject css as a <style> tag
compileTemplate: true, // Explicitly convert template to render function
}),
buble(), // Transpile to ES5
],
};

这个示例配置文件包含将您的 SFC 打包到 npm 的最小设置。可以进行自定义,例如将 CSS 提取到单独的文件中,使用 CSS 预处理器,压缩 JS 输出等。

此外,值得注意的是这里给组件起的 name。这是一个 PascalCase 名称,将赋予组件,并且应该与本食谱中其他地方使用的 kebab-case 名称相对应。

这会取代我当前的开发流程吗?

这里的配置并非旨在取代您当前使用的开发流程。如果您目前有一个带有热模块替换 (HMR) 的 webpack 设置,请继续使用它!如果您从头开始,可以随意安装 Vue CLI 3,它将为您提供完整的 HMR 体验,无需配置。

vue serve --open src/my-component.vue

换句话说,以任何您觉得舒适的方式进行开发。本食谱中概述的内容更像是“收尾工作”,而不是完整的开发流程。

何时避免使用这种模式

在某些情况下,以这种方式打包 SFC 可能不是一个好主意。本食谱没有详细介绍组件本身的编写方式。某些组件可能会提供副作用,例如指令,或扩展其他库以提供额外的功能。在这些情况下,您需要评估对本食谱所需的更改是否过于广泛。

此外,请注意您的 SFC 可能具有的任何依赖项。例如,如果您需要一个第三方库来进行排序或与 API 通信,Rollup 可能会将这些包合并到最终代码中,如果未正确配置。要继续使用本食谱,您需要配置 Rollup 将这些文件从输出中排除,然后更新您的文档以告知您的用户这些依赖项。

替代模式

在编写本食谱时,Vue CLI 3 本身处于 beta 阶段。此版本的 CLI 带有一个内置的 library 构建模式,它创建组件的 CommonJS 和 UMD 版本。这可能足以满足您的用例,但您仍然需要确保您的 package.json 文件正确指向 mainunpkg。此外,除非在发布之前或通过插件添加了该功能,否则将不会有 ES6 module 输出。

致谢

本食谱是 Mike Dodge 在 2018 年 3 月的 VueConf.us 上发表的闪电演讲的结果。他已将一个实用程序发布到 npm,它将使用本食谱快速搭建一个示例 SFC。您可以从 npm 下载该实用程序,vue-sfc-rollup。您也可以 克隆仓库 并自定义它。