在render函数内部动态载入组件(dynamic import in render function)

问题描述

想实现一个组件X,根据传入的prop mode,动态载入组件c1/组件c2/…/组件c10000 中的其中一个
要求不能载入多余chunk,实现懒加载
意即 若prop传入要求载入c1,绝不能把其他组件如c2的js chunk载入,直到prop改变要求显示c2的时候才允许异步加载c2对应的chunk(有点cmd的味道)。故而不可饿汉式地载完全部chunk再按需显示

问题出现的环境背景及自己尝试过哪些方法

第一时间想到render里的createElement的第一参可以是async function 以及webpack的dynamic import
文档
clipboard.png

源码里也有相关支持
https://github.com/vuejs/vue/…

我的相关代码

目录结构

.
├── Bar.vue
├── Baz.vue
├── Fail.vue
├── Foo.vue
└── X.vue

我的代码

<script>
export default {
    name: "X",
    props: {
        mode: String,
    },
    render(createElement) {
        let child;
        switch (this.mode) {
            case "Foo":
            case "Bar":
            // ... some other case
            case "Baz":
                child = () => import(`./${this.mode}`);
                break;
            default:
                child = () => import("./Fail");
                break;
        }
        
        return createElement(child);
    },
};
</script>

但是

结果程序不断执行render,陷入死循环
若改成return createElement(()=>child().then(o=>o.defalut));
不会死循环,仅执行一次,但是创建了空节点

至此,问题可以变为如下最小可重现模型,仍然是死循环执行render

export default {
    name: "X",
    render(createElement) {
        debugger;
        return createElement(
            () => new Promise(resolve => {
                setTimeout(
                    () => resolve("p"),
                2000);
            })
        );
    },
}; 

你期待的结果是什么?实际看到的错误信息又是什么?

正常render。不希望使用template来实现,要求用render函数硬刚
若不能实现,惠请指出不能的理据

回答:

不能这样做,因为是一个函数,每次一有变动都会重新执行,你可以用个键值对来保存你的异步组件

const comps = {
    a: () => import('./a')
}

回答:

既然组件加载依赖props,那试试把判断逻辑放computed。

export default {
  name: "X",
  data() {
    return {
      mode: 'Foo'
    }
  },
  computed: {
    com() {
      let child;
      switch (this.mode) {
        case "Foo":
        case "Bar":
        case "Baz":
          child = () => import(`./${this.mode}`);
          break;
        default:
          child = () => import("./Fail");
          break;
      }
      return child;
    },
  },
  methods: {
    clickHandler(e) {
      this.mode = e;
    },
  },
  render(createElement) {
    return createElement('div', {}, [
      createElement('button', {
        on: {
          click: () => this.clickHandler('Foo'),
        },
      }, 'Foo'),
      createElement('button', {
        on: {
          click: () => this.clickHandler('Bar'),
        },
      }, 'Bar'),
      createElement('button', {
        on: {
          click: () => this.clickHandler('Baz'),
        },
      }, 'Baz'),
      createElement(this.com),
    ]);
  },
}

暂无评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注