NervJS / taro

开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。 https://taro.zone/
https://docs.taro.zone/
Other
35.31k stars 4.77k forks source link

更新到 taro3.1.5 之后 slot 插槽无法使用 #9018

Closed jsLyLeeHi closed 3 years ago

jsLyLeeHi commented 3 years ago

相关平台

微信小程序

小程序基础库: 2.16.0 使用框架: Vue 3

复现代码

https://github.com/jsLyLeeHi/taro-slot-issue-.git

复现步骤

使用 taro3.1.5 新建一个 vue3.0 的项目然后使用插槽slot

期望结果

插槽slot能正常使用

实际结果

插槽slot的内容无法正确显示

环境信息

 Taro v3.1.5

  Taro CLI3.1.5 environment info:
    System:
      OS: Windows 10
    Binaries:
      Node: 14.7.0 - C:\Program Files\nodejs\node.EXE       
      Yarn: 1.22.4 - C:\Users\1\AppData\Roaming\npm\yarn.CMD
      npm: 6.14.7 - C:\Program Files\nodejs\npm.CMD
Chen-jj commented 3 years ago

@jsLyLeeHi 提供一下详细代码

b2nil commented 3 years ago

提供一点额外的信息。

这个问题 h5 不受影响,只有小程序存在这个问题。跟 taro 版本更新没直接关系,taro 3.1.4 存在同样的问题。 使用 @vue/compiler-sfc@3.0.9 编译,slot 表现正常。 使用 @vue/compiler-sfc@^3.0.10 编译,slot 节点会被编译成静态节点。

例如:

<view>
    <slot name="header">default content</slot>
 </view>

使用 @vue/compiler-sfc@3.0.9 编译的结果:

var _hoisted_1 = /*#__PURE__*/Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createVNode */ "i"])("view", null, "slot test", -1
/* HOISTED */
);

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* openBlock */ "r"])(), Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createBlock */ "e"])("view", null, [_hoisted_1, Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* renderSlot */ "s"])(_ctx.$slots, "header", {}, function () {
    return ["default content"];
  })]);
}

使用 @vue/compiler-sfc@^3.0.10 编译的结果:

var _hoisted_1 = /*#__PURE__*/Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createVNode */ "i"])("view", null, "slot test", -1
/* HOISTED */
);

var _hoisted_2 = /*#__PURE__*/Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createVNode */ "i"])("slot", {
  name: "header"
}, "default content", -1
/* HOISTED */
);

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* openBlock */ "r"])(), Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createBlock */ "e"])("view", null, [_hoisted_1, _hoisted_2]);
}

使用 vue 3.0 render function 不受影响,例如,下面的写法 slot 表现正常:

h('view', null, {default: () => [
        h('view', null, { default: () => 'another slot check'}),
        slots.default?.() || 'default content'
]})

@vue/compiler-sfc 固定在 3.0.9, Taro 升级到最新版本是可以正常运行的。

Chen-jj commented 3 years ago

问题在于 Vue 创建了 slot 节点去实现此功能:

Object(vue__WEBPACK_IMPORTED_MODULE_0__[/* createVNode */ "i"])("slot", {
  name: "header"
}, "default content", -1
/* HOISTED */
);

而小程序的 slot 和 H5 的表现不一致,比较难搞

b2nil commented 3 years ago

@Chen-jj 这个问题是 vue-loadercompilerOptions.nodeTransforms 设置导致的。

@vue/compiler-sfc 编译后的 node 是正常的:

{
  type: 1,
  ns: 0,
  tag: 'slot',
  tagType: 2,
  props: [],
  isSelfClosing: false,
  children: [ { type: 2, content: 'default content', loc: [Object] } ],
  loc: {
    start: { column: 5, line: 3, offset: 16 },
    end: { column: 33, line: 3, offset: 44 },
    source: '<slot>default content</slot>'
  },
  codegenNode: {
    type: 14,
    loc: {
      start: [Object],
      end: [Object],
      source: '<slot>default content</slot>'
    },
    callee: Symbol(renderSlot),
    arguments: [ '_ctx.$slots', '"default"', '{}', [Object] ]
  }
}

但在 vue-loader 的 compilerOptions.nodeTransforms 中,slot 节点的 codegenNode 传丢了。

// taro-mini-runner/src/webpack/vue3.ts  line 49 -59
compilerOptions: {
        // https://github.com/vuejs/vue-next/blob/master/packages/compiler-core/src/options.ts
        nodeTransforms: [(node: RootNode | TemplateChildNode) => {
          if (node.type === 1 /* ELEMENT */) {
            node = node as ElementNode
            const nodeName = node.tag

            if (capitalize(toCamelCase(nodeName)) in internalComponents) {
              node.tagType = 0  // Root Cause ???
              componentConfig.includes.add(nodeName)
            }
   //...
}

如果不把带有 codegenNode 的节点交给 nodeTransforms 处理,slot 就能正常使用了,但不知道是否会对其他情况产生副作用或造成新问题?

if (node.type === 1 /* ELEMENT */ && !node.codegenNode) {
   // ...
}
hyccpq commented 3 years ago

目前,应该是最近@compiler-sfc升级到3.0.11出现的类似问题,暂时最简单的解决办法是把@compiler-sfc版本固定到3.0.9即可!!

Chen-jj commented 3 years ago

感谢大佬 @b2nil 贡献,3.2.1 修复~