背景
element-plus(2.2.27)中的DatePicker搭配vue3.2.45,在开发环境和线上环境出现不一致的情况。
开发环境中一切正常,上线之后日历无法渲染。
最新版的element-plus已不存在这个问题和最新版的Vue也不存在这个问题
开发
上线
问题分析
第一反应是不是子组件在构建过程中没有正确挂载,导致无法渲染,排查发现dom节点并没有组件内容。最终定位是在ElDatePickerCell这个组件中
核心问题代码在element-plus中
export default defineComponent({
name: 'ElDatePickerCell',
props: basicCellProps,
setup(props) {
const ns = useNamespace('date-table-cell')
const { slots } = inject(ROOT_PICKER_INJECTION_KEY)!
return () => {
const { cell } = props
if (slots.default) {
const list = slots.default(cell).filter((item) => {
return (
item.patchFlag !== -2 && item.type.toString() !== 'Symbol(Comment)'
)
})
if (list.length) {
return list
}
}
return (
<div class={ns.b()}>
<span class={ns.e('text')}>{cell?.text}</span>
</div>
)
}
},
})
开发环境能够正常渲染div元素,线上环境确无法出现,明明数据一致,为什么会出现条件不同?
最后还是到了核心问题代码:
item.patchFlag !== -2 && item.type.toString() !== 'Symbol(Comment)'
最后排查Vue3.2.45源码,Vue3针对开发和构建的时候,生成的vNode.type并不完全一致
Vue3针对Comment节点,生成的type采用了不同的变量。所以在DatePicker进行条件判断的时候,出现了不一致的情况。
问题根源
element-plus2.2.27中对于条件判断过分依赖了vode.type,而Vue3在生产环境可能为了节省一定的体积,对一些非必要性的代码进行删减。
问题不是关键,这个锅应该甩给谁?
这个问题看似不大,但确实给实际业务开发带来了线上问题,三方都针对自己的场景,都做出了看似正确的应对。
- 业务侧-给DatePicker生成了一个null的插槽,看似没啥问题
- element-plus 基于节点的类型进行是否是空插槽的判断,尽管最新版的element-plus使用了更规范的renderSlots的方式,避免了这种判断条件。规范性存在问题,但毕竟是依赖确认的对象进行判断的
- Vue3.2.45在开发、生产环境中,区分环境进行构建产物压缩。而最新Vue(3.5.13),也此处逻辑修改了
而我认为,这个问题还是应该甩给Vue3,无它,只在于我们对于Symbol的态度!
我们都知道Symbol()本身都不等于Symbol(),更何况Symbol(xxx)和Symbol的关系。
不应该在开发和生产环境中对于某个具体的值产生影响。
最后皆大欢喜
Vue3.5.13针对问题修复了
export const Comment: unique symbol = Symbol.for('v-cmt')
element-plus在 commit 就行了修复
return renderSlot(slots, 'default', { ...cell }, () => [
<div class={ns.b()}>
<span class={ns.e('text')}>{cell?.text}</span>
</div>,
])
业务侧也临时在业务代码中,去掉了插槽
这大概就是开源的力量~