将插件与 Rollup 捆绑在一起,但在客户端应用程序的捆绑包中导入了重复的 Vue.js 包 (Nuxt)

Bundling a plugin with Rollup but having duplicate Vue.js package imported in the client app's bundle (Nuxt)

亲爱的 Stack Overflow / Vue.js / Rollup 社区

对于使用 Vue 和 Rollup 的主插件开发人员来说,这可能是一个菜鸟问题。我会把问题写的很明确,希望以后能帮到像我一样的菜鸟

我有一个简单的插件可以帮助进行表单验证。此插件中的组件之一导入 Vue,以便以编程方式创建组件并附加到 DOM 挂载,如下所示:

import Vue from 'vue'
import Notification from './Notification.vue' /* a very simple Vue component */
...
mounted() {
  const NotificationClass = Vue.extend(Notification)
  const notificationInstance = new NotificationClass({ propsData: { name: 'ABC' } })
  notificationInstance.$mount('#something')
}

这按预期工作,并且此插件使用 Rollup 捆绑在一起,配置如下:

import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel'
import { terser } from 'rollup-plugin-terser'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'

export default {
  input: 'src/index.js',
  output: {
    name: 'forms',
    globals: {
      vue: 'Vue'
    }
  },
  plugins: [
    vue(),
    babel(),
    resolve(),
    commonjs(),
    terser()
  ],
  external: ['vue']
}

如您所见,Vue.js 在此包中被外化了。目的(和假设)是导入此插件的客户端应用程序将在 Vue 上 运行,因此无需在此处捆绑它(假设)。

打包器使用的非常简单的src/index.js如下:

import Form from './Form.vue'

export default {
  install(Vue, _) {
    Vue.component('bs-form', Form)
  }
}

Rollup 创建 2 个文件(一个 esm 和一个 umd)并在插件 package.json 文件中引用它们,如下所示:

  "name": "bs-forms",
  "main": "./dist/umd.js",
  "module": "./dist/esm.js",
  "files": [
    "dist/*"
  ],
  "scripts": {
    "build": "npm run build:umd & npm run build:es",
    "build:es": "rollup --config rollup.config.js --format es --file dist/esm.js",
    "build:umd": "rollup --config rollup.config.js --format umd --file dist/umd.js"
  }

到目前为止,一切都按预期工作,并且很好地生成了捆绑包。

客户端应用程序 (Nuxt SSR) 导入此插件(使用 npm-link 因为它正在开发中),在插件文件中进行非常简单的导入:

/* main.js*/
import Vue from 'vue'

import bsForms from 'bs-forms'
Vue.use(bsForms)

此插件文件 (main.js) 已作为插件添加到 nuxt.config.js:

// Nuxt Plugins
...
plugins: [{src: '~/plugins/main'}]
...

一切正常,但问题来了:

由于客户端是 Nuxt 应用程序,Vue 当然是默认导入的,但外部化的 Vue 模块(通过表单插件)也在客户端中导入。因此在客户端包中有一个重复的包。

我想客户端应用程序可以配置其 webpack 配置以删除这个重复的模块。也许通过使用像 Dedupe 插件之类的东西?有人可以建议如何最好地处理这种情况吗?

但我真正想学习的是首先捆绑插件的最佳实践,这样客户端就不必更改其配置中的任何内容,只需导入该插件即可继续。

我知道在插件中导入 Vue.js 一开始可能不是一件好事。但是像这样的导入也可能有其他原因,例如想象插件可以用 Typescript 编写并且 Vue.js / Typescript 是通过使用 Vue.extend 语句(见下文)编写的,它也导入Vue(为了启用类型界面):

import Vue from 'vue'

const Component = Vue.extend({
  // type inference enabled
})

这是一个很长的问题。请 Rollup 的主人,通过建议最佳实践方法(或您的方法)来处理此类情况,帮助我和社区。

谢谢!!!

我用一个有趣的警告对这个问题进行了排序:

当通过 NPM 包(由 npm install -save <plugin-name> 安装)使用插件时,不会导入重复的 Vue 包

但是,在开发过程中,如果您使用包 vie npm link(如 npm link <plugin-name>),那么 Vue 将被导入两次,如原始问题中的图像所示。

以后遇到类似问题的朋友,请尝试发布和导入你的包,看看是否有什么不同。

谢谢!

我遇到了同样的问题,我发现@vatson 的 this answer 很有帮助

Your problem is the combination of "npm link", the nature of nodejs module loading and the vue intolerance to multiple instances from different places.

Short introduction how import in nodejs works. If your script has some kind of library import, then nodejs initially looks in the local node_modules folder, if local node_modules doesn't contain required dependency then nodejs goes to the folder above to find node_modules and your imported dependency there.

您不需要在 NPM 上发布包。如果您使用 npm pack 在本地生成您的包,然后将其安装在您的其他项目 npm install /absolute_path_to_your_local_package/your_package_name.tgz 中就足够了。如果您更新包中的某些内容,您可以将其重新安装到您的其他项目中,一切都应该正常工作。

这是关于 npm packnpm link 之间区别的来源。