Amalan Terbaik: Cara melampirkan komponen pada DOM dalam Vue 3
P粉362071992
P粉362071992 2023-08-24 19:51:03
0
2
597
<p>Saya mahu mencipta komponen secara dinamik dalam aplikasi Vue 3 saya, di dalam Komponen Fail Tunggal (SFC) dan menambahkannya pada DOM. Saya menggunakan <kod><persediaan skrip></code> dan itu satu lagi masalah. </p> <p>Ini nampaknya tidak semestinya sukar. </p> <p>Ini kira-kira perkara yang saya mahu lakukan:</p> <ol> <li>Dapatkan beberapa data. Telah selesai. </li> <li>Buat contoh komponen Vue: Foo.vue. </li> <li>Haruskan data kepadanya sebagai sifat. </li> <li>Tambahkannya di tempat yang saya mahukan. </li> </ol> <p>Masalahnya ialah, saya tidak boleh menggunakan <komponen :is="Foo:> dalam templat kerana saya tidak tahu di mana ia akan lama selepas templat dipaparkan.</p> <p>Adakah terdapat sebarang amalan terbaik? Adakah ada orang baik yang boleh memberikan contoh yang mudah, saya akan sangat berterima kasih. </p> <p>Saya kadangkala tidak dapat memahami dokumentasi Vue separuh masa. Maaf, benci untuk mengatakannya, tetapi untuk pemula Vue, mereka agak kabur dan membuatkan saya berasa bodoh. </p> <p>Berikut ialah beberapa kod palsu untuk perkara yang saya cuba lakukan: </p> <pre class="brush:php;toolbar:false;">import Foo daripada "../components/Foo.vue" fungsi makeAFoo(p, data){ // Instantiate Foo.vue saya (tidak pasti cara melakukan inline ini) dan hantarkan data yang diperlukan let foo = new Foo(data); // Jika semudah itu, bukan? //Tambahkannya pada p (iaitu elemen HTML) p.appendChild(foo) }</pre> <p><br /></p>
P粉362071992
P粉362071992

membalas semua(2)
P粉004287665

Cara yang lebih mudah ialah menggunakan v-if atau v-for.

Daripada mengendalikan komponen secara langsung, mari kita kendalikan keadaan komponen dan biarkan sihir responsif Vue berfungsi

Ini adalah contoh, tambah komponen secara dinamik (Toast), cuma kendalikan keadaan komponen

Toast.vue file: V-for di sini adalah reaktif, apabila ralat baharu ditambahkan pada objek ralat, ia akan dipaparkan

<script setup lang="ts">
import { watch } from 'vue';
import { ref, onUpdated } from 'vue';
import { Toast } from 'bootstrap';

const props = defineProps({
  errors: { type: Array, default: () => [] },
});

onUpdated(() => {
  const hiddenToasts = props.errors.filter((obj) => {
    return obj.show != true;
  });
  hiddenToasts.forEach(function (error) {
    var errorToast = document.getElementById(error.id);
    var toast = new Toast(errorToast);
    toast.show();
    error.show = true;
    errorToast.addEventListener('hidden.bs.toast', function () {
      const indexOfObject = props.errors.findIndex((item) => {
        return item.id === error.id;
      });
      if (indexOfObject !== -1) {
        props.errors.splice(indexOfObject, 1);
      }
    });
  });
});
</script>
<script lang="ts">
const TOASTS_MAX = 5;
export function push(array: Array, data): Array {
  if (array.length == TOASTS_MAX) {
    array.shift();
    array.push(data);
  } else {
    array.push(data);
  }
}
</script>

<template>
  <div
    ref="container"
    class="position-fixed bottom-0 end-0 p-3"
    style="z-index: 11"
  >
    <div
      v-for="item in errors"
      v-bind:id="item.id"
      class="toast fade opacity-75 bg-danger"
      role="alert"
      aria-live="assertive"
      aria-atomic="true"
      data-bs-delay="15000"
    >
      <div class="toast-header bg-danger">
        <strong class="me-auto text-white">Error</strong>
        <button
          type="button"
          class="btn-close"
          data-bs-dismiss="toast"
          aria-label="Close"
        ></button>
      </div>
      <div class="toast-body text-white error-body">{{ item.msg }}</div>
    </div>
  </div>
</template>

ErrorTrigger.vue: Setiap kali peristiwa klik berlaku, kami menolak ralat ke objek ralat

<script setup lang="ts">
import { ref, reactive } from 'vue';
import toast from './Toast.vue';
import { push } from './Toast.vue';

const count = ref(0);
const state = reactive({ errors: [] });

function pushError(id: int) {
  push(state.errors, { id: id, msg: 'Error message ' + id });
}
</script>

<template>
  <toast :errors="state.errors"></toast>

  <button type="button" @click="pushError('toast' + count++)">
    Error trigger: {{ count }}
  </button>
</template>

Contoh penuh: https://stackblitz.com/edit/vitejs-vite-mcjgkl

P粉598140294

Pilihan 1: Gunakan createVNode(component, props)render(vnode, container)

Buat: Gunakan createVNode()创建一个带有props的组件定义的VNode(例如,从*.vue导入的SFC),可以将其传递给render() untuk membuat pada elemen bekas yang diberikan.

Hancurkan: Panggil cangkuk render(null, container)会销毁附加到容器的VNode。当父组件卸载时(通过unmountedkitaran hayat) Kaedah ini harus dipanggil untuk pembersihan.

// renderComponent.js
import { createVNode, render } from 'vue'

export default function renderComponent({ el, component, props, appContext }) {
  let vnode = createVNode(component, props)
  vnode.appContext = { ...appContext }
  render(vnode, el)

  return () => {
    // destroy vnode
    render(null, el)
    vnode = undefined
  }
}

Nota: Kaedah ini bergantung pada kaedah dalaman (createVNoderender) yang mungkin difaktorkan semula atau dikeluarkan dalam keluaran akan datang.

Demo 1

Pilihan 2: Gunakan createApp(component, props)app.mount(container)

Buat: Buat createApp()createApp()创建一个应用实例。该实例具有mount() ="noreferrer">Contoh aplikasi . Contoh ini mempunyai kaedah mount()

yang boleh digunakan Menjadikan komponen pada elemen bekas yang diberikan.

Pemusnahan: Aplikasi aplikasi mempunyai unmount()方法来销毁应用和组件实例。当父组件卸载时(通过unmountednyahlekap()

kaedah untuk memusnahkan contoh aplikasi dan komponen. Kaedah ini harus dipanggil untuk membersihkan apabila komponen induk dinyahlekapkan (melalui cangkuk kitar hayat unmounted).

// renderComponent.js
import { createApp } from 'vue'

export default function renderComponent({ el, component, props, appContext }) {
  let app = createApp(component, props)
  Object.assign(app._context, appContext) // 必须使用Object.assign在_context上
  app.mount(el)

  return () => {
    // 销毁app/component
    app?.unmount()
    app = undefined
  }
}
Nota: Kaedah ini mencipta contoh aplikasi untuk setiap komponen Jika berbilang komponen perlu digunakan dalam dokumen pada masa yang sama, ia boleh menyebabkan overhed yang besar.

Demo 2

Contoh penggunaan🎜
<script setup>
import { ref, onUnmounted, getCurrentInstance } from 'vue'
import renderComponent from './renderComponent'

const { appContext } = getCurrentInstance()
const container = ref()
let counter = 1
let destroyComp = null

onUnmounted(() => destroyComp?.())

const insert = async () => {
  destroyComp?.()
  destroyComp = renderComponent({
    el: container.value,
    component: (await import('@/components/HelloWorld.vue')).default
    props: {
      key: counter,
      msg: 'Message ' + counter++,
    },
    appContext,
  })
}
</script>

<template>
  <button @click="insert">插入组件</button>
  <div ref="container"></div>
</template>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan