🌞

element-plus2.2.27和vue3.2.45组合引发的血案

背景element-plus(2.2.27)中的DatePicker搭配vue3.2.45,在开发环境和线上环境出现不一致的情况。开发环境中一切正常,上线之后日历无法渲染。最新版的element-pl

文链接在语雀:https://www.yuque.com/wumingshi/rkh1qq/

背景

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>,
      ])

业务侧也临时在业务代码中,去掉了插槽

这大概就是开源的力量~

updatedupdated2025-03-242025-03-24