如果让你编写一个 jssdk,且里面要用到 vue,你会如何在 jssdk 中加载 vue?
你可能会想到使用 webpack 的 external
将 vue 依赖排除,以减少 jssdk 自身的体积。然而,这可能还会存在一些问题。
external 的问题
- 他要求你的宿主 window 环境,或者宿主预编译环境下提前要有 vue。于是你要通过 peerDependencies 告诉对方。
- 如果你不让宿主安装,那你得自己装到 jssdk 下,体积又会增大。最可怕的是:可能宿主他又安装了,此时你俩就存在重复冗余代码的问题了。
这是个两难的选择。如果宿主并没有 vue,则浏览器中运行 sdk 时候会报错。这就导致有些场景下,你对宿主环境有了一个“强迫”行为。例如人家宿主根本就用不到 vue,结果你反而要让他装一个 vue。 宿主可能会质疑你:“你 jssdk 想用 vue,凭什么让我给你装,你 jssdk 内部自己装不就行了”。
但反过来,如果你 jssdk 内部自己装了 vue,又会导致万一用到了某个“已经携带 vue”的宿主环境下,你反而 jssdk 内部又多打了一套 vue。造成冗余代码。
明确目标
于是,我们的目标应该是这样的:
- 当预编译的宿主环境(或浏览器宿主环境)有 vue 时候,我们就直接用宿主环境下的 vue module,跟宿主项目共用 vue 模块。
- 当宿主环境没有 vue 的时候,我们就使用自己的 vue(例如通过 webpack 的 import 动态懒加载来获取 vue)
增强兜底
所以经过实操,我总结出最佳实践是这样的:
- 将 vue 声明成 external,且不要声明成 peerDependencies。
- 配置 jssdk 的导出模式为 umd,且配置好 external 在不同情形下的获取方式。
- 如果宿主环境没有,我们通过 import 懒加载方式来远程加载自己所需要的 vue
external 的配置代码如下:
externals: {
vue: {
module: 'vue',
commonjs2: "vue",
commonjs: "vue",
root: "Vue", // indicates global variable
},
},
output 的配置如下:
output: {
filename: "guitar-[name]-[fullhash:8].js", // 输出文件名
path: path.join(import.meta.dirname, "dist"), // 输出文件目录
libraryTarget: "umd",
globalObject: "this",
library: "GdRender",
},
有了上述配置后,我们还要解决宿主环境缺少 vue 时候的主动加载问题。具体代码如下:
// common/vue.js
import externalVue from "vue";
export async function resolveVue() {
if (!externalVue && !resolveVue.resolvePromising) {
resolveVue.resolvePromising = import("vue/dist/vue.esm-bundler.js").then(
(Vue) => {
externalVue = Vue;
return externalVue;
}
);
return resolveVue.resolvePromising;
}
return externalVue || resolvePromising;
}
代码中但凡使用到 vue 的地方,就这样引用 vue 即可:
import { resolveVue } from "./common/vue.js";
export default async function render() {
const vueRes = await resolveVue();
const { createApp } = vueRes;
const app = createApp(App);
}
不声明 peerDependencies 的原因:
或许宿主项目根本没有用到 vue,而仅仅是你自己的 jssdk 里面需要用 vue 来渲染东西而已。
当然了,对于某些明知道宿主肯定会有 vue 环境的情形,我们还是建议采用传统套路:直接 peerDependencies 告诉宿主安装。
原理分析
我们看看编译产物直接截图分享下原理:
那如果宿主环境没有呢。则会看到我们 jssdk 中对应调用的地方,就是直接走 webpack 动态懒加载了:
效果收益
基于此,我可以保持暴露的主体 jssdk 的体积足够小。如下图是核心 main.js 的体积(未压缩):
然后万一任何使用我的 jssdk 的宿主环境下有引入 vue(无论是编译宿主,还是浏览器宿主)。则我们的 jssdk 就不会再去加载自身的 vue。
最大的好处是:此方案不需要宿主环境有任何感知。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。