Vue3 は、defineAsyncComponent とコンポーネント タグを使用して動的レンダリング コンポーネントを実装します。

内容は少し長めで、その時に遭遇したバグや問題を解決するためのアイデアを記録した内容となっています。

ビジネス シナリオの簡単な説明:
フロントエンドは構成コンポーネントであり、バックエンド SQL に URL の一意の識別子を介してページ構成情報を取得するよう要求し、フロントエンドはクエリ フォーム、エクスポート、構成情報によるテーブル、ツールバー、動的ポップアップ ウィンドウ、動的レンダリング コンポーネントの機能は、レンダリングされたツールバーのボタンです、タイプはカスタム ポップアップ ウィンドウ、ポップアップ ウィンドウのパスは背面です-end 構成データ ボタンをクリックすると、フロントエンドは指定されたパスの下にポップアップ ウィンドウ コンポーネントを開きます。

以前使用していた vue2 の動的マウント コンポーネントは vue3 ほど面倒ではありません。公式 Web サイトの例もインターネット上の例も、どのコンポーネントをインポートするかをフロントエンドが知っていて、後はプログラムを直接インポートするだけです。ロジックダイナミックスイッチ。これは私にとっては異なります。マウントする必要があるコンポーネントが不明です。したがって、実装するのは少し難しいです。

1. 基本的な動的インポート コンポーネント:
単純な動的インポートとは、フロント エンドがどのコンポーネントをインポートするかを認識し、複数のコンポーネントを親コンポーネントにインポートしますが、レンダリングはせず、特定の条件が満たされた場合にのみ特定の位置でレンダリングすることを意味します。コンポーネント。

<template>
	 <custom-modal ref="custom"></custom-modal>
</template>
<script>
 import {
    
    
    reactive,
    ref,
    shallowReactive,
    onActivated,
    defineAsyncComponent,
  } from 'vue';
 const customModal = defineAsyncComponent(() => import('./modal/CustomModal.vue'));
 const custom = ref();
 </script>

上記の例は、defineAsyncComponentVue の実装を通じてコン​​ポーネントをマウントし、customModalテンプレートに割り当てます。<custom-modal>テンプレート内のラベルとして直接使用することも、コンポーネントの is 属性に割り当てることもできます。is 属性はアタッチされます。ビジネス ロジックを通じて動的に変数に変換できます。この変数の値を変更することで、複数のコンポーネントを前後にレンダリングできます。

<template>
<component :is="componentKey" ref="custom"></component>
</template>
 import {
    
    
    reactive,
    ref,
    shallowReactive,
    onActivated,
    defineAsyncComponent,
  } from 'vue';
 const componentKey = ref(null);
 const components: any = shallowReactive({
    
    });
 const customModal = defineAsyncComponent(() => import('./modal/CustomModal.vue'));
 componentKey  = customModal

2. 複雑な導入: どのコンポーネントをインポートすればよいかわかりません。コンポーネントのパスはバックエンドによって返されます。
上記のコードをプロジェクト コードに追加することは実現できません。導入ではエラーが報告されませんが、参照は常に定義されていないため、動的コンポーネントを呼び出すことができません。open 関数。
何度も試した結果、次のような結論に達しました

1.起初是在按钮的click函数内去挂载自定义组件并调用ref函数的,ref为undefined。
尝试多次不能实现功能(这里是挂载与调用最合适的位置),
2.接着又在初始化配置数据时(查询后端sql),axios的then函数内挂载组件,然后点击按钮的地方调用ref内的函数,ref依旧为null。
3. 接着在最外层,调用初始化时挂载,也就是生命周期函数体内,测试还是一样的结果。
4. 接着发现带有async函数体内挂载组件,也无法完成。
5.单独写个函数,不加async,函数内挂载组件,然后再生命周期外调用该函数,按钮内调用ref内的方法,成功弹窗。这并不是我想要的,因为路径不是固定的,它要等到后端sql放回结果,才能执行。

要約: 上記の複数のテストの後、次の結論が得られます. 動的コンポーネントの ref オブジェクトは値
1 を持つことができません. コンポーネントのイベント関数にマウントすることはできません.
ここに画像の説明を挿入

2. axiosのthen関数本体には実装できません
ここに画像の説明を挿入

3. async宣言をした関数本体にはマウントできません
ここに画像の説明を挿入

4. vueのライフサイクル内ではマウントできない
ここに画像の説明を挿入

5. 最外層に実装することでのみ実現でき、ref はオブジェクトになります。

幸いなことに、前例のない道はありません。私の頭の中にアイデアがあります。
ページが初期化されるときに、プロジェクト内でグローバルにマウントされているすべてのビュー コンポーネントをオブジェクトにスローし、そのコンポーネント コンポーネントを使用します。これは、で指定されたコンポーネント オブジェクトに対応します。オブジェクトを指定して、バックエンド データを渡します。このとき、バックエンドはコンポーネントのパスを指定する必要はありませんが、コンポーネント名を指定します。オブジェクトからマウントされたコンポーネントを見つけて、そのオブジェクトを is に渡します。
const modules = import.meta.glob('@/views/*/**.vue');// すべてのプロジェクト パスを取得します。
モジュールはビュー内のすべての Vue の相対パスです。それからループし、ループ本体でマウントを実現し、オブジェクトに保存します。キーは相対パスのプロジェクト名です (インターセプトできます)。以下)。

上記の考え方をもとに、テストと実装を繰り返すことで、最終的な機能が実現されます。

<template>
<component :is="componentKey" ref="custom"></component>
</template>
<script>
 import {
    
    
    reactive,
    ref,
    shallowReactive,
    onActivated,
    defineAsyncComponent,
  } from 'vue';
	
	//声明componentkey,用于告诉component当前挂载什么组件,components为一个对象,存放多个不确定的自定义组件。
  const componentKey = ref(null);
  const components: any = shallowReactive({
    
    });

  // 组件挂载
  const initTableConfig = (gridId, type) => {
    
     
   queryTableConfig({
    
     gridId }).then(({
    
     data }) => {
    
    
      if (type === 'main') {
    
    
        Object.assign(mainConfig, data);
        tabsKey.value = -1;
      } else {
    
    
        tabsDetail.value.push(data);
        tabsKey.value = tabsDetail.value.length - 1;
      }
      // 涉及到自定义组件的部分,这里需要提前挂载,在用到时不至于ref为null
      XEUtils.objectEach(data.action, (action, key) => {
    
    
        if (
          action.modalCfg &&
          action.modalCfg.type === 'CustomModal' &&
          action.modalCfg.src
        ) {
    
    
          components[action.actionId] = defineAsyncComponent(
            () => import(`../../../${
      
      action.modalCfg.src}`)
          );
          //注意:这里的路径后端只能返回相对路径,不能使用@/xxx/xxx.vue ,不能使用src/xxx/xxx.vue,只能./xxx.vue或者../../xxx/xxx.vue。由于并不确定组件在什么位置,避免容易出错的原则,我在前端通过../../../的形式将路径回退到src下,后端只需要从src下配置路径即可,不用考虑那么多了。如后端src的值为src/xxx/xxx/xxx.vue 则在前端合成的路径就为../../../src/xx/xxx/xxx.vue
          componentKey.value = components[action.actionId];
          // 为什么componentKey.vue在这里赋值,在后面点击窗口后又赋值,这里能不能省略。
		//	答:这里省略的话,到点击按钮触发时会报错,第一次点击会报错,第二次点击不会报错,窗口正常弹出。可能是因为,组件挂载时并没有引入组件,只在使用时才引入,如果上面不提前将挂载好的组件引入进来,后面触发事件触发时引入在调用ref,执行太快,costom就会报错,所以才会点两次才弹窗。
        }
      });
    });
  };
 </script>

ボタンのクリックによりイベントがトリガーされ、ポップアップ ウィンドウにどのコンポーネントをポップアップするかを決定します。

		} else if (action.modalCfg.type === 'CustomModal') {
    
    
  		// 这里的actionid和组件是对应的,所以在按钮触发后,通过按钮携带的actionid能取到对应的组件。
          componentKey.value = components[action.actionId];
          custom.value.init(row);
        }

上記の方法により、どこにマウントしてもエラーは報告されず、完璧なソリューションになります。
注: マウントと ref の使用を同じメソッド本体内で行うことはできません。可能であれば、ページがロードされるときにマウントが実行され、ref を呼び出す必要があるときにエラーは報告されません。

おすすめ

転載: blog.csdn.net/weixin_43865196/article/details/129286052