Vue Slotの実現方式(ソースコード~)
4863 ワード
普段は会社でコードを書く業務が通用しないため、elementUIを使うBエンドのプロジェクトを除いて、slotを使う機会は少ない.この間、アリさんは面接を受けました.ソースコードを見たことがないので、Vnodeのようなレンダリング論理解析に基づいているはずです.
ソースコードvue 2バージョンのvue-devブランチの2.6.12から
ソース位置
まずsrcファイルの下でslotを検索して、ものはとても雑で、あまり位置付けがよくなくて、
この関数の機能は、挿入されたnodesノードを返す/ターゲットテンプレートにこのnodesノードを挿入する
render-helpersディレクトリの下のファイルは名前からrenderを補助するものと理解でき、これらの関数は
検索.tの呼び出しは検索できません.ここの_t関数には、render関数で使用される他の関数もあります.例を挙げると、cliはdemoのhelloWorldコンポーネントを持ってスロットを作ります(親コンポーネント挿入123)、彼のrender関数は値長という徳行を返します:
具体的な実装
サブコンポーネントのslot
サブコンポーネントはどのようにしてslotがあることを知っていますか?実はtemplate解析あるいはrender関数解析で、1つの_になりましたt(renderSlot)関数,t関数は自分で$slotsの中にスロットの内容を取りに行きます
親コンポーネントのslotコンテンツ
親コンポーネントはどうしてサブコンポーネントにslotがあることを知っていますか?実は親コンポーネントはスロットがあるかどうかあまり気にしないで、子コンポーネントは親コンポーネントのノードとして、親コンポーネントはスロットの中の内容を子コンポーネントのchildrenとして伝えるだけでいいのです
親子コンポーネントslotコンテンツの転送
この問題は、親コンポーネントがどのようにサブコンポーネントの$slotsに付与されたかということになります.
initInternalComponentは_renderChildrenはoptionsに掛けられていますここのoptions.renderChildrenは前述の
ここではrenderChildrenはslotsになります.ここでは
完~
ソースコードvue 2バージョンのvue-devブランチの2.6.12から
ソース位置
まずsrcファイルの下でslotを検索して、ものはとても雑で、あまり位置付けがよくなくて、
src/core/intance/render-helpers/render-slot.js
に行って見るしかなくて、この中に1つの関数がありますexport function renderSlot(
name: string,
fallback: ?Array,
props: ?Object,
bindObject: ?Object
): ?Array {
const scopedSlotFn = this.$scopedSlots[name]
let nodes
if (scopedSlotFn) { // scoped slot
props = props || {}
if (bindObject) {
if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
warn(
'slot v-bind without argument expects an Object',
this
)
}
props = extend(extend({}, bindObject), props)
}
nodes = scopedSlotFn(props) || fallback
} else {
nodes = this.$slots[name] || fallback
}
const target = props && props.slot
if (target) {
return this.$createElement('template', { slot: target }, nodes)
} else {
return nodes
}
}
この関数の機能は、挿入されたnodesノードを返す/ターゲットテンプレートにこのnodesノードを挿入する
render-helpersディレクトリの下のファイルは名前からrenderを補助するものと理解でき、これらの関数は
src/core/instance/render-helpers/index.js
によってVueに縛られる.FunctionalRenderContextexport function installRenderHelpers (target: any) {
...
target._t = renderSlot
...
}
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
installRenderHelpers(FunctionalRenderContext.prototype)
検索.tの呼び出しは検索できません.ここの_t関数には、render関数で使用される他の関数もあります.例を挙げると、cliはdemoのhelloWorldコンポーネントを持ってスロットを作ります(親コンポーネント挿入123)、彼のrender関数は値長という徳行を返します:
return _c("div", { staticClass: "hello" }, [_vm._t("default")], 2)
、親コンポーネントの長という徳行return _c( "div", { attrs: { id: "app" } }, [_c("HelloWorld", [_vm._v("123")])], 1)
、その中_c代表createElement,tはrenderSlot,_を表すv代表createTextVNode具体的な実装
サブコンポーネントのslot
サブコンポーネントはどのようにしてslotがあることを知っていますか?実はtemplate解析あるいはrender関数解析で、1つの_になりましたt(renderSlot)関数,t関数は自分で$slotsの中にスロットの内容を取りに行きます
親コンポーネントのslotコンテンツ
親コンポーネントはどうしてサブコンポーネントにslotがあることを知っていますか?実は親コンポーネントはスロットがあるかどうかあまり気にしないで、子コンポーネントは親コンポーネントのノードとして、親コンポーネントはスロットの中の内容を子コンポーネントのchildrenとして伝えるだけでいいのです
親子コンポーネントslotコンテンツの転送
この問題は、親コンポーネントがどのようにサブコンポーネントの$slotsに付与されたかということになります.
Vue.prototype._init = function (options?: Object) {
if (options && options._isComponent) {
initInternalComponent(vm, options)
}
...
initRender(vm);
...
}
function initInternalComponent(vm: Component, options: InternalComponentOptions) {
...
const parentVnode = options._parentVnode
...
const vnodeComponentOptions = parentVnode.componentOptions
...
opts._renderChildren = vnodeComponentOptions.children
...
}
function initRender(vm) {
...
vm.$slots = resolveSlots(options._renderChildren, renderContext);
...
}
initInternalComponentは_renderChildrenはoptionsに掛けられていますここのoptions.renderChildrenは前述の
[_vm._v("123")]
に対応する[Vnode]function resolveSlots(
children: ?Array,
context: ?Component
): { [key: string]: Array } {
if (!children || !children.length) {
return {}
}
const slots = {}
for (let i = 0, l = children.length; i < l; i++) {
const child = children[i]
const data = child.data
// remove slot attribute if the node is resolved as a Vue slot node
if (data && data.attrs && data.attrs.slot) {
delete data.attrs.slot
}
// named slots should only be respected if the vnode was rendered in the
// same context.
if ((child.context === context || child.fnContext === context) &&
data && data.slot != null
) {
const name = data.slot
const slot = (slots[name] || (slots[name] = []))
if (child.tag === 'template') {
slot.push.apply(slot, child.children || [])
} else {
slot.push(child)
}
} else {
(slots.default || (slots.default = [])).push(child)
}
}
// ignore slots that contains only whitespace
for (const name in slots) {
if (slots[name].every(isWhitespace)) {
delete slots[name]
}
}
return slots
}
ここではrenderChildrenはslotsになります.ここでは
の内容があります.つまり、slotのnameが値があるかデフォルトのdefaultかです.の完~