vue-demi:编写同时适配 Vue 2 和 3 的公共库

Vue 2 在 2023 年 12 月 31[1] 日已经中止官方支持,由于 Vue 2 跟 Vue 3 之间比较多的不兼容,导致代码迁移比较困难,现在 Vue 2 的使用群体仍然占据主流。

不过,随着 Vue 2 结束支持,迁移 Vue 3 以及面向 Vue 3 开发已经是大势所趋,特别是在 Vue 2.7 内置了 Composition API[2] 支持之后。

关于项目迁移,大家可以参考官方《迁移指南》[3]对项目进行逐步迁移。而对于广泛地基于 Vue 2 代码库的迁移,现在有了 antfu 大佬开源的 vue-demi 包[4]后,会方便很多。

接下里就来介绍它的简单使用。

安装

npm install vue-demi
# or
pnpm install vue-demi

在你库的 package.json 文件添加 vue@vue/composition-api 作为 peer 依赖。

{
  "dependencies": {
    "vue-demi""latest"
  },
  "peerDependencies": {
    "@vue/composition-api""^1.0.0-rc.1",
    "vue""^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional"true
    }
  },
  "devDependencies": {
    "vue""^3.0.0" // or "^2.6.0" base on your preferred working environment
  },
}

vue-demi 使用使用最新版本,避免使用本地的缓存版本。另外,实际开发中使用的 vue 版本,可以根据你的工作环境决定,这里使用了 Vue 3。

接下来,直接从 vue-demi 中引入 API 进行开发就 OK 了。

import { ref, reactive, defineComponent } from 'vue-demi'

下面我们来举一个案例说明 vue-demi 的使用。

案例

我们会创建一个 useMouse 的 composition api,返回当前鼠标光标在页面种的坐标位置。

首先,创建项目 vue-use-mouse。

$ mkdir vue-use-mouse
cd vue-use-mouse
$ code . # code use VS Code
$ pnpm init

向 package.json 文件中添加 vue@vue/composition-api 作为 peer 依赖。

{
  // ...
  "dependencies": {
    "vue-demi""latest"
  },
  "peerDependencies": {
    "@vue/composition-api""^1.0.0-rc.1",
    "vue""^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional"true
    }
  },
  "devDependencies": {
    "vue""^3.0.0" // or "^2.6.0" base on your preferred working environment
  },
}

还要安装 typescript 依赖,因为我们的源代码是 TS 文件,待会打包也是用 tsc

$ pnpm install typescript

源代码位于 src/index.ts,很简单。

import { ref, onMounted, onUnmounted } from 'vue-demi'

export function useMouse({
  const x = ref(0)
  const y = ref(0)

  const update = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return {
    x,
    y
  }
}

package.json 文件中添加构建脚本。‘

{
  // ...
  "main""dist/cjs/index.js",
  "module""dist/esm/index.js",
  "types""dist/esm/index.d.ts",
  "scripts": {
    "build""pnpm run build:esm && pnpm run build:cjs",
    "build:esm""tsc src/index.ts --module es2015 --moduleResolution node --lib "es2015, dom" --outDir dist/esm -d",
    "build:cjs""tsc src/index.ts --module commonjs --moduleResolution node --lib "es2015, dom" --outDir dist/cjs"
  },
  // ...
}

我们最终会将源代码打包成 esm 和 cjs 两种格式供调用方使用。这样,我们就完成了 use-mouse 的开发。

package.json 的最终内容如下:

{
  "name""vue-use-mouse",
  "version""1.0.0",
  "description""",
  "main""dist/cjs/index.js",
  "module""dist/esm/index.js",
  "types""dist/esm/index.d.ts",
  "scripts": {
    "build""pnpm run build:esm && pnpm run build:cjs",
    "build:esm""tsc src/index.ts --module es2015 --moduleResolution node --lib "es2015, dom" --outDir dist/esm -d",
    "build:cjs""tsc src/index.ts --module commonjs --moduleResolution node --lib "es2015, dom" --outDir dist/cjs"
  },
  "dependencies": {
    "typescript""^5.3.3",
    "vue-demi""latest"
  },
  "peerDependencies": {
    "@vue/composition-api""^1.0.0-rc.1",
    "vue""^2.0.0 || >=3.0.0"
  },
  "peerDependenciesMeta": {
    "@vue/composition-api": {
      "optional"true
    }
  },
  "devDependencies": {
    "vue""^3.0.0"
  },
  "keywords": [],
  "author""",
  "license""ISC"
}

内部实现

在 vue-demi 包中,package.json 中导出的文件是位于 lib/ 目录下的。

{
  "main""lib/index.cjs",
  "module""lib/index.mjs",
  "exports": {
    ".": {
      "types""./lib/index.d.ts",
      "require""./lib/index.cjs",
      "import""./lib/index.mjs",
      "browser""./lib/index.mjs"
    },
  },
}

lib/ 之下还有 3 个目录。

vue-demi:编写同时适配 Vue 2 和 3 的公共库

这 3 个目录主要做的事情:一是为了弥合各版本之间差异,提供统一的 Composition API 支持(Vue 2.6- 会自动提醒你安装 @vue/composition-api 依赖);二是你使用 Vue 2 中弃用、但在 Vue 3 中新增的功能时,会给你提示说不能用,否则无法同时兼容两个版本。

vue-demi 包的 package.json 文件中,还定义了 postinstall 脚本。

{
  "scripts": {
    "postinstall""node ./scripts/postinstall.js",
  },
}

在宿主库安装完 vue-demi 后,会触发这个 hook,进而调用 postinstall.js 脚本。内容如下:

const { switchVersion, loadModule } = require('./utils')

const Vue = loadModule('vue')

if (!Vue || typeof Vue.version !== 'string') {
  console.warn('[vue-demi] Vue is not found. Please run "npm install vue" to install.')
}
else if (Vue.version.startsWith('2.7.')) {
  switchVersion(2.7)
}
else if (Vue.version.startsWith('2.')) {
  switchVersion(2)
}
else if (Vue.version.startsWith('3.')) {
  switchVersion(3)
}
else {
  console.warn(`[vue-demi] Vue version v${Vue.version} is not suppported.`)
}

switchVersion() 函数的内部代码如下:

function switchVersion(version, vue{
  copy('index.cjs', version, vue)
  copy('index.mjs', version, vue)
  copy('index.d.ts', version, vue)

  if (version === 2)
    updateVue2API()
}

总结下来,会检查宿主项目中安装的 Vue 版本,再根据版本把 lib/ 对应目录下的文件(v2.7、v2、vue3)复制到 lib/ 根目录下,这样在宿主项目中就能引入正确的版本了。

总结

本文我们介绍了如何使用 vue-demi 来完成同时支持 vue 2 和 vue 3 的版本包的开发。同时,也介绍了 vue-demi 内部的实现原理。

希望对你能有所帮助,感谢阅读,再见。

参考资料

[1]

2023 年 12 月 31: https://blog.vuejs.org/posts/vue-2-eol

[2]

Composition API: https://vuejs.org/guide/extras/composition-api-faq.html

[3]

《迁移指南》: https://v3-migration.vuejs.org/

[4]

antfu 大佬开源的 vue-demi 包: https://antfu.me/posts/make-libraries-working-with-vue-2-and-3


原文始发于微信公众号(写代码的宝哥):vue-demi:编写同时适配 Vue 2 和 3 的公共库

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/243640.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!