ホームページ > 記事 > ウェブフロントエンド > Vue3 スロット Slot の実装原理は何ですか?
Vue は、一連のコンテンツ配布 API を実装しています。この API の設計は、Web コンポーネント仕様草案からインスピレーションを受けており、<slot></slot>
要素は機能します。分散コンテンツをホストするためのアウトレットとして。
それでは、スロットとは一体何なのでしょうか?スロットは実際には、親コンポーネントによって渡されたスロットのコンテンツを受け取り、VNode を生成してそれを返す関数です。
通常は <slot></slot>
を使用します。このタグのペアは、親コンポーネントによって渡されたコンテンツを受け取ります。このタグのペアの最終的なコンパイルは、次の関数です。 VNode を作成します。これをスロット VNode を作成する関数と呼ぶことができます。
// <slot></slot>标签被vue3编译之后的内容 export function render(_ctx, _cache, $props, $setup, $data, $options) { return _renderSlot(_ctx.$slots, "default") }
<slot></slot>
タグは、Vue3 によってコンパイルされた後、_renderSlot
という関数になることが明確にわかります。
スロットを使用するには、親子コンポーネントが存在する必要があります。
親コンポーネントに次のコンテンツがあるとします。
<todo-button> Add todo </todo-button>
親コンポーネントで todo-button
子コンポーネントを使用し、 Add todo## を渡します。 # のスロットの内容。
<button class="btn-primary"> <slot></slot> </button>コンポーネントがレンダリングされると、
<slot></slot> は「Add todo」に置き換えられます。 。
export function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_todo_button = _resolveComponent("todo-button") return (_openBlock(), _createBlock(_component_todo_button, null, { default: _withCtx(() => [ _createTextVNode(" Add todo ") ], undefined, true), _: 1 /* STABLE */ })) }Slot コンポーネントの子のコンテンツがオブジェクト型であることがわかります。これは次のコードです。
{ default: _withCtx(() => [ _createTextVNode(" Add todo ") ], undefined, true), _: 1 /* STABLE */ }すると、このコンポーネントのVNodeが作成されると、その子がObject型かどうかが判定され、Object型であれば、そのVNodeのshapeFlagにSlotコンポーネントのタグが付けられます。コンポーネント。 テンプレートを通じてコンパイルされている場合、これは標準スロットの子であり、
__ 属性を持ち、コンポーネント インスタンスに直接配置できます。 。
ユーザー自身が作成したスロット オブジェクトの場合は、
normalizeObjectSlots に進みます。
ユーザーの動作が仕様に従っていない場合は、
normalizeVNodeSlots
スロットの内容を分析する
export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("button", { class: "btn-primary" }, [ _renderSlot(_ctx.$slots, "default") ])) }
<slot></slot>
タグは vue3 によってコンパイルされると、_renderSlot という関数になります。
renderSlot
この関数は 5 つのパラメータを受け取ります。1 つ目はインスタンス上のスロット関数オブジェクトslots で、2 つ目はスロットの名前、つまりスロットのコンテンツを指定された場所にレンダリングします。3 番目はスロット スコープによって受信された
props、4 番目はスロットのデフォルトのコンテンツ レンダリング関数、5 番目は Notそれが何を意味するかはまだわかりません。
スコープ スロットの原則
<slot username="coboy"></slot>コンパイルされたコード
export function render(_ctx, _cache, $props, $setup, $data, $options) { return _renderSlot(_ctx.$slots, "default", { username: "coboy" }) }親コンポーネント テンプレート
<todo-button> <template v-slot:default="slotProps"> {{ slotProps.username }} </template> </todo-button>コンパイルされたコード
export function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_todo_button = _resolveComponent("todo-button") return (_openBlock(), _createBlock(_component_todo_button, null, { default: _withCtx((slotProps) => [ _createTextVNode(_toDisplayString(slotProps.username), 1 /* TEXT */) ]), _: 1 /* STABLE */ })) }上記の通り renderSlot 関数を通じてこれは次のコードに簡単に要約できます。
export function renderSlots(slots, name, props) { const slot = slots[name] if (slot) { if (typeof slot === 'function') { return createVNode(Fragment, {}, slot(props)) } } }
slots
はコンポーネント インスタンスによってアップロードされたスロット コンテンツであり、実際にはこのコンテンツです。{ default: _withCtx((slotProps) => [ _createTextVNode(_toDisplayString(slotProps.username), 1 /* TEXT */) ]), _: 1 /* STABLE */ }
name はデフォルトです。次に、slots[name] が取得するのは次の関数です
_withCtx((slotProps) => [ _createTextVNode(_toDisplayString(slotProps.username), 1 /* TEXT */) ])slot(props) は明らかにslot({ username: "coboy" }) であり、子コンポーネントのデータを親コンポーネントに転送します。スロットが入っています。
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout></base-layout>
组件:
<div class="container"> <header> <!-- 我们希望把页头放这里 --> </header> <main> <!-- 我们希望把主要内容放这里 --> </main> <footer> <!-- 我们希望把页脚放这里 --> </footer> </div>
对于这样的情况,<slot></slot>
元素有一个特殊的 attribute:name
。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方:
<!--子组件--> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
一个不带 name
的 <slot></slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template></template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<!--父组件--> <base-layout> <template v-slot:header> <h2>header</h2> </template> <template v-slot:default> <p>default</p> </template> <template v-slot:footer> <p>footer</p> </template> </base-layout>
父组件编译之后的内容:
export function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_base_layout = _resolveComponent("base-layout") return (_openBlock(), _createBlock(_component_base_layout, null, { header: _withCtx(() => [ _createElementVNode("h2", null, "header") ]), default: _withCtx(() => [ _createElementVNode("p", null, "default") ]), footer: _withCtx(() => [ _createElementVNode("p", null, "footer") ]), _: 1 /* STABLE */ })) }
子组件编译之后的内容:
export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("div", { class: "container" }, [ _createElementVNode("header", null, [ _renderSlot(_ctx.$slots, "header") ]), _createElementVNode("main", null, [ _renderSlot(_ctx.$slots, "default") ]), _createElementVNode("footer", null, [ _renderSlot(_ctx.$slots, "footer") ]) ])) }
通过子组件编译之后的内容我们可以看到这三个Slot渲染函数
_renderSlot(_ctx.$slots, "header")
_renderSlot(_ctx.$slots, "default")
_renderSlot(_ctx.$slots, "footer")
然后我们再回顾一下renderSlot渲染函数
// renderSlots的简化 export function renderSlots(slots, name, props) { const slot = slots[name] if (slot) { if (typeof slot === 'function') { return createVNode(Fragment, {}, slot(props)) } } }
这个时候我们就可以很清楚的知道所谓具名函数是通过renderSlots渲染函数的第二参数去定位要渲染的父组件提供的插槽内容。父组件的插槽内容编译之后变成了一个Object的数据类型。
{ header: _withCtx(() => [ _createElementVNode("h2", null, "header") ]), default: _withCtx(() => [ _createElementVNode("p", null, "default") ]), footer: _withCtx(() => [ _createElementVNode("p", null, "footer") ]), _: 1 /* STABLE */ }
我们可能希望这个 <button></button>
内绝大多数情况下都渲染“Submit”文本。为了将“Submit”作为备用内容,我们可以将它放在 <slot></slot>
标签内
<button type="submit"> <slot>Submit</slot> </button>
现在当我们在一个父级组件中使用 <submit-button></submit-button>
并且不提供任何插槽内容时:
<submit-button></submit-button>
备用内容“Submit”将会被渲染:
<button type="submit"> Submit </button>
但是如果我们提供内容:
<submit-button> Save </submit-button>
则这个提供的内容将会被渲染从而取代备用内容:
<button type="submit"> Save </button>
这其中的原理是什么呢?我们先来看看上面默认内容插槽编译之后的代码
export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("button", { type: "submit" }, [ _renderSlot(_ctx.$slots, "default", {}, () => [ _createTextVNode("Submit") ]) ])) }
我们可以看到插槽函数的内容是这样的
_renderSlot(_ctx.$slots, "default", {}, () => [ _createTextVNode("Submit") ])
我们再回顾看一下renderSlot函数
renderSlot
函数接受五个参数,第四个是插槽的默认内容渲染函数。
再通过renderSlot函数的源码我们可以看到,
第一步,先获取父组件提供的内容插槽的内容,
在第二个步骤中,若父组件已提供插槽内容,则使用该插槽内容,否则执行默认的内容渲染函数以获取默认内容。
以上がVue3 スロット Slot の実装原理は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。