Web フロントエンド インタビュー高頻度テスト サイト - Vue コンポーネント間の通信と高度な機能 (複数コンポーネント間の通信、カスタム v-model、nextTick、スロット)

シリーズ記事ディレクトリ

コンテンツ 参考リンク
Vueの基本的な使い方 Vue の基本的な使い方 (Vue の最も基本的な知識ポイントをマスターするための 1 つの記事)
Vue通信と高度な機能 Vue コンポーネントと高度な機能間の通信 (さまざまなコンポーネント、カスタム v-model、nextTick、スロット間の通信)
Vue の高度な機能 Vue の高度な機能 (動的コンポーネント、非同期ロード、キープアライブ、ミックスイン、Vuex、Vue-Router)
ビューの原則 1 Vue の原理 (MVVM モデルの理解、データ変更の詳細/監視、配列変更の監視、仮想 DOM の詳細な理解)
ビューの原則 2 Vue の原則 (diff アルゴリズム、テンプレートのコンパイル、コンポーネントのレンダリングと更新、JS 実装のルーティング)
Vue インタビューの質問 Web フロントエンド面接高頻度テストサイト - Vue 面接の質問


1. Vue コンポーネント間の通信

1. props と $emit

  • props一般的に使用される父から息子へデータを渡す
  • this.$emit一般的に使用される息子から父へデータを渡す
  • event.$emit一般的に使用される兄弟部屋データを渡す

例: 2 つのサブコンポーネント (入力ボックス コンポーネントとリスト コンポーネント) が動的に追加および削除される

親コンポーネント (index.vue)

  • @add親コンポーネントのバインドと子コンポーネント ラベルの@deleteカスタム イベント
<template>
  <div>
    <Input @add="addHandler" />
    <List :list="list" @delete="deleteHandler" />
  </div>
</template>

<script>
import Input from "./Input";
import List from "./List";

export default {
    
    
  components: {
    
    
    Input,
    List,
  },
  data() {
    
    
    return {
    
    
      list: [
        {
    
    
          id: "id-1",
          title: "标题1",
        },
        {
    
    
          id: "id-2",
          title: "标题2",
        },
      ],
    };
  },
  methods: {
    
    
    // 添加项目
    addHandler(title) {
    
    
      this.list.push({
    
    
        id: `id-${
      
      Date.now()}`,
        title,
      });
    },
    // 删除项目
    deleteHandler(id) {
    
    
      this.list = this.list.filter((item) => item.id !== id);
    },
  },
  // 创建
  created() {
    
    
    console.log("index created");
  },
  // 挂载
  mounted() {
    
    
    console.log("index mounted");
  },
  // 更新前
  beforeUpdate() {
    
    
    console.log("index before update");
  },
  // 更新
  updated() {
    
    
    console.log("index updated");
  },
};
</script>

子コンポーネント (input.vue)

  • ボタンバインディング addTitle イベント
  • を使用して親コンポーネントのイベントをthis.$emit('add', this.title)呼び出し
  • event.$emit("onAddTitle", this.title)カスタム イベント (兄弟コンポーネントによって定義されたイベント)を呼び出すために使用します。
<template>
  <div>
    <input type="text" v-model="title" />
    <button @click="addTitle">add</button>
  </div>
</template>

<script>
import event from "./event";

export default {
    
    
  data() {
    
    
    return {
    
    
      title: "",
    };
  },
  methods: {
    
    
    addTitle() {
    
    
      // 调用父组件的事件
      if (this.title.trim() !== "") {
    
    
        this.$emit("add", this.title);

        // 调用自定义事件
        event.$emit("onAddTitle", this.title);

        this.title = "";
      } else {
    
    
        alert('输入内容不能为空')
      }
    },
  },
};
</script>

子コンポーネント (List.vue)

  • props を使用して親コンポーネントからリストを受け取り、型制限とデフォルト値を作成します
  • ボタンは、親コンポーネントをthis.$emit("delete", id)呼び出すれ、id に従って削除されます
  • event.$on("onAddTitle", this.addTitleHandler)マウント時にカスタム イベントをバインドする
  • event.$off("onAddTitle", this.addTitleHandler)破棄前にカスタム イベントを破棄する (beforeDestroy)
  • 注: カスタム イベントをバインドして破棄する場合、2 番目のパラメーターは渡される通常の関数です。アロー関数を記述しないでください (この点は変更されます)。
<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item.id">
        {
    
    {
    
     item.title }}
        <button @click="deleteItem(item.id)">删除</button>
      </li>
    </ul>
  </div>
</template>

<script>
import event from "./event";

export default {
    
    
  // props: ['list']
  props: {
    
    
    // prop 类型和默认值
    list: {
    
    
      type: Array,
      default() {
    
    
        return []
      }
    },
  },
  methods: {
    
    
    deleteItem(id) {
    
    
      this.$emit("delete", id);
    },
    addTitleHandler(title) {
    
    
      console.log("on add title", title);
    },
  },
  created() {
    
    
    console.log("list created");
  },
  mounted() {
    
    
    console.log("list mounted");

    // 绑定自定义事件
    event.$on("onAddTitle", this.addTitleHandler);
  },
  beforeUpdate() {
    
    
    console.log("list before update");
  },
  updated() {
    
    
    console.log("list updated");
  },
  beforeDestroy() {
    
    
    // 及时销毁,否则可能造成内存泄露
    event.$off("onAddTitle", this.addTitleHandler);
  },
};
</script>

event.js ファイル

  • vue インスタンスを作成します。 、 などのメソッドを使用$emitできます$off$on
import Vue from 'vue'

export default new Vue()

境界線 - - - - - - - - - - - - - - - - - - - - - - - - -------------------------------------------------- ----------

ここに画像の説明を挿入

境界線 - - - - - - - - - - - - - - - - - - - - - - - - -------------------------------------------------- ----------

ここに画像の説明を挿入

境界線 - - - - - - - - - - - - - - - - - - - - - - - - -------------------------------------------------- ----------

ここに画像の説明を挿入

2.ライフサイクル

詳細なライフ サイクル [参照リンク]

  • beforeCreate: vue インスタンスが作成され、el とデータが初期化されておらず、データとメソッドにアクセスできず、通常、この段階では動作しません。通常、この段階では動作しません
  • created: vue インスタンスのデータとメソッドが初期化され、プロパティがバインドされました。しかし、現時点ではまだ仮想 DOM であり、実際の DOM は生成されておらず、$el はまだ利用できません。データは通常ここで初期化されます
  • beforeMount: テンプレートはコンパイルされていますが、ページにレンダリングされていません (つまり、仮想 DOM が実際の DOM に読み込まれています)。
  • マウント: テンプレートは実際の DOM にレンダリングされており、ユーザーはレンダリングされたページを既に見ることができます。マウントを実行すると、インスタンスが完全に作成されたことを意味します
  • beforeUpdate: 再レンダリングの前にトリガーされ、vue の仮想 dom メカニズムが仮想 dom を再構築し、最後の仮想 dom ツリーを diff アルゴリズムと比較した後に再レンダリングします。ビューのデータ変更のみが beforeUpdate および updated をトリガーし、データのデータ変更のみがトリガーされません。
  • updated: データが変更され、dom が再レンダリングされました。
  • beforeDestroy: 破棄の前に実行されます ($destroy メソッドが呼び出されたとき)。通常はここで: タイマーのクリア、カスタム バインド イベントのクリアなど...
  • destroy: 破棄後 (Dom 要素は存在しますが、vue によって制御されなくなります)、ウォッチャー、イベント リスナー、および子コンポーネントをアンインストールします。

サンプル props と $emit の結果を参照すると、親子コンポーネントのライフサイクルは次のように要約されます。

01.父组件 before create
02.父组件 created
03.父组件 before mount
04.子组件 before create
05.子组件 created
06.子组件 before mount
07.子组件 mounted
08.父组件 mounted
09.父组件 before update
10.子组件 before update
11.子组件 updated
12.父组件 updated
13.父组件 before destroy
14.子组件 before destroy
15.子组件 destroyed
16.父组件 destroyed

次に、Vue の高度な機能

カスタム v-model、$nextTick、スロット、動的、非同期コンポーネント、キープアライブ、ミックスイン

1. v-model のカスタマイズ

例: カスタム実装 v-model

CustomVModel.vue 子コンポーネント

  • 使用される入力: v-model の代わりに値
  • change1 属性に対応
  • text1 属性に対応
  • Prop は、コンポーネントを呼び出す親コンポーネントで v-model ディレクティブを使用してバインドされるプロパティです。
  • event は prop で指定されたプロパティの値を変更する関数に対応します
<template>
  <input
    type="text"
    :text="text1"
    @input="$emit('change1', $event.target.value)"
  />
</template>

<script>
export default {
    
    
  name: "CustomVModel",
  model: {
    
    
    prop: "text1", // 对应 props text1
    event: "change1",
  },
  props: {
    
    
    type: String,
    default() {
    
    
      return "";
    },
  },
};
</script>

index.vue 親コンポーネント

  • 子コンポーネントのラベルで v-model 双方向データ バインディング名を使用する
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />

    <p>{
    
    {
    
     name }}</p>
    <CustomVModel v-model='name'/>
  </div>
</template>

<script>
import CustomVModel from "./CustomVModel.vue";
export default {
    
    
  name: "index",
  components: {
    
     CustomVModel },
  data() {
    
    
    return {
    
    
        name: '杂货铺'
    }
  }
};
</script>

ここに画像の説明を挿入

2、ネクストティック

  • Vue は非同期レンダリングです
  • データが変更された後、DOM はすぐにはレンダリングされません
  • $nextTick は、最新の DOM ノードを取得するために DOM レンダリング後に起動されます

例: 子ノードを追加し、子ノードの全長を取得します

NextTick.vue コンポーネント

  • ref はマーキングに使用されます
  • refs は DOM 要素を取得するために使用されます
  • this.$nextTick(() => {...})非同期レンダリングの場合、DOM がレンダリングされた後にコールバックします
  • 追加しない場合$nextTick、出力結果は 3 です。
<template>
  <div>
    <ul ref="ul1">
      <li v-for="(item, index) in list" :key="index">
        {
    
    {
    
     item }}
      </li>
    </ul>
    <button @click="addItem">添加一项</button>
  </div>
</template>

<script>
export default {
    
    
  name: "NextTick",
  data() {
    
    
    return {
    
    
      list: ["a", "b", "c"],
    };
  },
  methods: {
    
    
    addItem() {
    
    
      this.list.push(`${
      
      Date.now()}`);
      this.list.push(`${
      
      Date.now()}`);
      this.list.push(`${
      
      Date.now()}`);

      // 异步渲染,$nextTick 待 DOM 渲染完再回调
      // 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
      this.$nextTick(() => {
    
    
        // 获取 DOM 元素
        const ulElem = this.$refs.ul1;
        console.log(ulElem.childNodes.length); // 6
      });
    },
  },
};
</script>

ここに画像の説明を挿入

3.スロットスロット

(1) デフォルトスロット

  • 親コンポーネントが子コンポーネントの指定された位置にhtml構造
  • <solt>ラベルの本文はデフォルトのコンテンツです。つまり、親コンポーネントがコンテンツを設定していない場合は、ここに表示されます

例: デフォルトスロットの基本的な使い方

index.vue 親コンポーネント

  • 親コンポーネントの子コンポーネント タグ内に{ { website.title }}は、子コンポーネント スロットでレンダリングされるコンテンツがあります。
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />

    <SlotDemo :url="website.url">
      {
    
    {
    
     website.title }}
    </SlotDemo>
  </div>
</template>

<script>
import SlotDemo from "./SlotDemo.vue";
export default {
    
    
  components: {
    
     SlotDemo },
  data() {
    
    
    return {
    
    
      website: {
    
    
        url: "http://baidu.com/",
        title: "Baidu",
        subTitle: "百度",
      },
    };
  },
};
</script>

SlotDemo.vue サブコンポーネント

  • 親コンポーネントの子コンポーネント タグにコンテンツがある場合、対応するコンテンツがレンダリングされます。
  • 親の子コンポーネントのタグ内にコンテンツがない場合、スロットのデフォルトのコンテンツがレンダリングされます
<template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>

<script>
export default {
    
    
    props: ['url'],
};
</script>

ここに画像の説明を挿入

境界線 - - - - - - - - - - - - - - - - - - - - - - - - -------------------------------------------------- ----------

ここに画像の説明を挿入

(2) スコープスロット

  • シナリオ: スロットのコンテンツは、親のドメインと子のドメインの両方からのデータを使用する必要がある場合があります

例: 子コンポーネントのタイトルを表示、親コンポーネントのリンクを使用

index.vue 親コンポーネント

  • 親コンポーネントの子コンポーネント タグ内で定義<template>
  • <template>v-slot を使用して、ラベル内のカスタム プロパティ slotProps をバインドします
  • 次に、slotProps.soltData.title を介してサブコンポーネントのタイトル値を取得します。
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />

    <ScopedSlot:url="website.url">
      <template v-slot="slotProps">
        {
    
    {
    
     slotProps.slotData.title }}
      </template>
    </ScopedSlot>
  </div>
</template>

<script>
import ScopedSlot from "./ScopedSlot.vue";
export default {
    
    
  components: {
    
     ScopedSlot},
  data() {
    
    
    return {
    
    
      website: {
    
    
        url: "http://baidu.com/",
        title: "Baidu",
        subTitle: "百度",
      },
    };
  },
};
</script>

ScopedSlot.vue サブコンポーネント

  • <slot>タグ内の動的属性 slotData をカスタマイズし、データ内の Web サイトをバインドします
  • タグ内の補間構文を使用して、子コンポーネントのタイトルを表示します
<template>
  <a :href="url">
    <slot :slotData="website">
      {
    
    {
    
     website.subTitle }}
    </slot>
  </a>
</template>

<script>
export default {
    
    
  props: ["url"],
  data() {
    
    
    return {
    
    
      website: {
    
    
        url: "http://bilibili.com/",
        title: "Bilibili",
        subTitle: "哔哩哔哩",
      },
    };
  },
};
</script>

ここに画像の説明を挿入

(3) 名前付きスロット

  • <slot>subcomponent で使用しname='xxx'て、スロットに名前を付けます
  • <template>コンポーネントv-slot:xxxまたは#xxx対応する子コンポーネントのスロットで使用します

例: 名前付きスロットの使用

index.vue 親コンポーネント

  • <template>コンポーネントv-slot:xxxまたは#xxx対応する子コンポーネントのスロットで使用します
<template>
  <div>
    <p>vue 高级特性</p>
    <hr />

    <NamedSlot>
      <template v-slot:header>
        <h4>将插入到 header slot 中</h4>
      </template>

      <p>将插入到 main slot 中,即未命名的 slot</p>

      <template #footer>
        <p>将插入到 footer slot 中</p>
      </template>
    </NamedSlot>
  </div>
</template>

<script>
import NamedSlot from "./NamedSlot.vue";
export default {
    
    
  components: {
    
     NamedSlot }
};
</script>

NamedSlot.vue サブコンポーネント

  • <slot>subcomponent で使用しname='xxx'て、スロットに名前を付けます
<template>
  <div>
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer">默认尾部</slot>
    </footer>
  </div>
</template>

<script>
export default {
    
    };
</script>

ここに画像の説明を挿入

不积跬步无以至千里 不积小流无以成江海

クリックしてフォローし、迷子にならないように、更新を続けてください...

おすすめ

転載: blog.csdn.net/qq_45902692/article/details/126453192