vue2 组件可以在 vue3 中使用么?

问题描述

我目前有一个封装好的Vue2组件库A,通过npm repo在几个项目中使用。现在基于Vue3的项目也要接入组件A,我要怎样通过一套代码发布,就可以同时在基于Vue2和Vue3版本的项目中使用?

自己尝试过哪些方法

临时解决方案是分为两个分支,一个分支依赖"vue": 2.x,一个分支依赖"vue": 3.x,每次有改动需要修改发布两次,维护两个分支。

相关代码

package.json

{
  "name": "xxx",
  "version": "4.0.1",
  "description": "xxx",
  "private": false,
  "main": "xxx",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack --progress"
  },
  "dependencies": {
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@babel/core": "^7.16.12",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-stage-3": "^7.8.3",
    "@babel/runtime": "^7.16.7",
    "babel-loader": "^8.2.3",
    "cross-env": "^7.0.3",
    "css-loader": "^6.5.1",
    "less": "^4.1.2",
    "less-loader": "^10.2.0",
    "postcss-loader": "^6.2.1",
    "style-loader": "^3.3.1",
    "svg-sprite-loader": "^6.0.11",
    "vue-loader": "^15.9.8",
    "vue-style-loader": "^4.1.3",
    "vue-template-compiler": "^2.6.14",
    "webpack": "^5.67.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.7.3"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

webpack.config.js

var path = require('path')
var webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')

function resolve(dir) {
  return path.join(__dirname, dir);
}
const NODE_ENV = process.env.NODE_ENV
module.exports = {
  entry: NODE_ENV == 'development' ? './examples/main.js' : './src/index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'xxx.js',
    library: 'xxx', // 指定的就是你使用require时的模块名
    libraryTarget: 'umd', // libraryTarget会生成不同umd的代码,可以只是commonjs标准的,也可以是指amd标准的,也可以只是通过script标签引入的
    umdNamedDefine: true //
  },
  module: {
    rules: [{
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ],
      },
      {
        test: /\.less$/,
        use: [
          'vue-style-loader',
          'postcss-loader',
          'css-loader',
          'less-loader'
        ],
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {}
          // other vue-loader options go here
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: [
            ['@babel/preset-env', { targets: "defaults" }]
          ]
        }
      },
      {
        test: /\.svg$/,
        loader: 'svg-sprite-loader',
        include: [resolve('src/navigation/icons')],
        options: {
          symbolId: 'icon-[name]'
        }
      },
      {
        test: /\.(png|jpg|gif|svg|woff2?|eot|ttf|otf)$/,
        type: 'asset',
        exclude: [resolve('src/navigation/icons')]
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', '.json']
  },
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    historyApiFallback: true,
    allowedHosts: 'all',
    host: 'localhost',
    compress: true,
    port: '8002',
    client: {
      overlay: true
    }
  },
  performance: {
    hints: false
  },
  devtool: 'eval-source-map',
  plugins: [
    // make sure to include the plugin!
    new VueLoaderPlugin()
  ]
}

导出组件

// 导入导航组件
import xxx from './xxx'

// 存储组件列表
const components = [
  xxx
]

// 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册
const install = function (Vue) {
  // 判断是否安装
  if (install.installed) return
  // 遍历注册全局组件
  components.map(component => Vue.component(component.name, component))
}

// 判断是否是直接引入文件
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

export default {
  // 导出的对象必须具有 install,才能被 Vue.use() 方法安装
  install,
  // 以下是具体的组件列表
  xxx
}

打包后部分代码

!function(V,l){"object"==typeof exports&&"object"==typeof module?module.exports=l():"function"==typeof define&&define.amd?define("xxx",[],l):"object"==typeof exports?exports["xxx"]=l():V["xxx"]=l()}(self,(function(){return function(){var __webpack_modules__={482:function(__unused_webpack_module,__webpack_exports__,__webpack_require__){"use strict";eval('// ESM COMPAT FLAG\n__webpack_require__.r(__webpack_exports__);\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n  "default": function() { return /* binding */ src; }\n});\n\n;// CONCATENATED MODULE: ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/x/x.vue?vue&type=template&id=862ecfc0&\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(\'div\',{staticClass:"xxx",class:[_vm.transparent ? \'transparent\' : \'\', _vm.themeClass]}

报错位置

var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h

项目结构

.vue,.less,.js,font,svg,png
image.png

期待的结果和实际看到的错误信息

期望组件只用维护一套代码。目前在Vue3上使用报错信息如下:
image.png
image.png
我大概了解是因为Vue单文件template模板编译不兼容导致的,但是实在不知道怎么解决。

解决方案:最终采用卡卡军的方法

  1. 增加一个package.json和对应打包配置
    image.png
  2. 增加postinstall配置,用来下载时自动切换版本,内容copy的卡卡军的,然后同时构建生成两个版本
    image.png
    感觉如果不想构建两个版本的,只能在最初就全部用render(h)的形式写,(或者打包时不编译.vue?这种不会)
阅读 21.1k
2 个回答

这个问题,我已经找到解决方法了,使用vue-demi, 但前提是封装好的组件需要重新用vue3书写, 然后打包成vue2和vue3都适用的版本, 但维护只需要维护一套,具体操作查看我写的这篇文章
https://juejin.cn/post/705526...

发布到npm的demo插件
https://github.com/kakajun/vu...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏