问题现象
在组件库中有一个封装组件 ElConfigProvider
:
vue
<template>
<el-config-provider v-bind="$attrs">
<slot></slot>
</el-config-provider>
</template>
在组件库自己的 demo 项目中使用时一切正常:
vue
<ElConfigProvider namespace="otd" :size="'default'">
<el-button>465</el-button>
</ElConfigProvider>
但当组件库打包后,通过 npm link
链接到另一个业务项目中使用时,控制台出现报错:
js
runtime-core.esm-bundler.js:2984 Uncaught (in promise) TypeError:
Cannot read properties of null (reading 'ce')
at renderSlot (runtime-core.esm-bundler.js:2984)
初步分析
-
报错位置在 Vue 3 内部的
renderSlot
,意味着插槽 vnode 是null
。 -
一开始怀疑是
$attrs
透传问题或参数类型不对(如locale
)。 -
经过多次验证,发现:
- 在组件库 demo 项目中正常
- 在业务项目中通过
npm link
使用时报错
这说明问题与业务逻辑本身关系不大,而是运行环境差异造成的。
核心原因
npm link
会在业务项目中产生两个 Vue 实例:
markdown
projectA/ (业务项目)
node_modules/
vue/ ← 业务项目的 Vue
otd-ui/ → symlink 到组件库
node_modules/
vue/ ← 组件库自己的 Vue
当:
- 插槽 vnode 是由 业务项目的 Vue 渲染生成的
el-config-provider
(来自组件库)是由 组件库的 Vue 渲染的
此时 vnode 跨 Vue 实例传递,Vue 内部类型检查不匹配,renderSlot
收到的 vnode.component 是 null
,访问 .ce
就会报错。
在组件库 demo 项目中,Vue 实例只有一个,所以不报错
解决方案
目标:保证组件库和业务项目运行时只使用同一个 Vue 实例。
json
{
"peerDependencies": {
"vue": "^3.2.0",
"element-plus": "^2.0.0",
"pinia": "^2.0.0"
},
"devDependencies": {
"vue": "^3.2.0",
"element-plus": "^2.0.0",
"pinia": "^2.0.0"
}
}
bash
rm -rf node_modules/vue node_modules/element-plus node_modules/pinia
npm install
方法 2:npm link 时软链业务项目的 Vue
bash
cd your-lib
npm link ../your-project/node_modules/vue
npm link ../your-project/node_modules/element-plus
方法 3:用 npm pack
代替 npm link
bash
cd your-lib
npm pack
cd ../your-project
npm install ../your-lib/your-lib-0.0.1.tgz
这样依赖结构与正式发布一致,不会重复安装 Vue。
总结
这个错误的根源是 多 Vue 实例导致的 vnode 跨实例传递 。
在 Vue 3 中,不同实例的 vnode 互不兼容,一旦跨实例传递,就可能触发 renderSlot
中的 .ce
空值报错。
避免这个问题的关键是:组件库运行时使用业务项目的 Vue 实例。