introducción
<< revisión >> periodos pasados
- Análisis de código fuente de vue3: paquete acumulativo monsterpo
- Análisis de código fuente de vue3: implemente el proceso de montaje de componentes
- Análisis de código fuente de vue3: implementa accesorios, emite, maneja eventos, etc.
En este problema, ranura - ranura, dividida en ranuras normales, ranuras con nombre, ranuras con alcance, todo el código fuente por favor vea
cuerpo
Utilice las ranuras en las plantillas de la siguiente manera:
<todo-button>
Add todo
</todo-button>
El contenido de la plantilla eventualmente se convertirá en un función de procesamiento, y la función de procesamiento llamará a la función h para convertirla en vnode, que se usa en vnode de la siguiente manera:
render() {
return h(TodoButton, {}, this.$slots.default)
},
Después de leer el uso básico de las tragamonedas, implementemos una tragamonedas juntos, ¡para que puedas entender el principio de las tragamonedas!
Implementar el uso básico
Donde se usan las ranuras es this.slots, y la propiedad llamada es default, luego slots, y la propiedad llamada es default, luego slots es un objeto, el objeto tiene el nombre de la ranura, si el usuario no pasa, puede ser accedido por defecto.
Casos de prueba
¡Atención! Dado que la prueba es dom, primero debe escribir html, etc., y aquí debe crear primero el nodo correspondiente
let appElement: Element;
beforeEach(() => {
appElement = document.createElement('div');
appElement.id = 'app';
document.body.appendChild(appElement);
})
afterEach(() => {
document.body.innerHTML = '';
})
Las pruebas de este caso han comenzado oficialmente.
test('test basic slots', () => {
const Foo = {
name: 'Foo',
render() {
return h('div', { class: 'foo' }, [h('p', {}, this.count), renderSlots(this.$slots)]);
}
}
const app = createApp({
render() {
return h('div', { class: 'container' }, [
h(Foo, { count: 1 }, { default: h('div', { class: 'slot' }, 'slot1') }),
h(Foo, { count: 2 }, { default: [h('p', { class: 'slot' }, 'slot2'), h('p', { class: 'slot' }, 'slot2')] }),
])
}
})
const appDoc = document.querySelector('#app')
app.mount(appDoc);
const container = document.querySelector('.container') as HTMLElement;
expect(container.innerHTML).toBe('<div class="foo"><p>1</p><div><div class="slot">slot1</div></div></div><div class="foo"><p>2</p><div><p class="slot">slot2</p><p class="slot">slot2</p></div></div>'
)
})
Análisis de requerimientos
Con el caso de prueba anterior, puede analizar lo siguiente:
- La forma en que el componente principal usa el componente secundario para pasar la ranura está en el tercer parámetro de h, y lo que se pasa es un objeto, y el valor valor puede ser un objeto o una matriz
- Cuando las ranuras se utilizan en subcomponentes, se obtenido en this.$slots
- Y también implementa un método renderSlot, renderSlot es para convertir esto. $slots llama a h en vnode
Resolución de problemas:
- Si necesita vincularse a esto, agregue juicio al proxy de la función setupStatefulComponent, pase $slots;
- Determine si $slot está en el proxy del componente y luego el proxy necesita unir las ranuras a la integración y vincular el valor a convertir los objetos entrantes en matrices;
- El método renderSlot llama a la función h para convertir un dato a vnode
Implementación de codificación
function setupStatefulComponent(instance: any) {
instance.proxy = new Proxy({ }, {
get(target,key){
else if(key in instance.slots){
return instance.slots[key]
}
}
})
}
export function setupComponent(instance) {
const { props, children } = instance.vnode
const slots = {}
for (const key in children) {
slots[key] = Array.isArray(children[key]) ? children[key] : [children[key]]
}
instance.slots = slots
}
export function renderSlots(slots) {
const slot = slots['default']
if (slot) {
return createVNode('div', {}, slot)
}
}
Con la codificación anterior, el caso de prueba se puede pasar perfectamente
ranura con nombre
Las ranuras con nombre son, además de múltiples ranuras, y además de las predeterminadas, puede agregar otros nombres, ver casos de prueba
Casos de prueba
test('slots test', () => {
const Foo = {
name: 'Foo',
render() {
return h('div', { class: 'foo' },
[
renderSlots(this.$slots, 'header'),
h('div', { class: 'default' }, 'default'),
renderSlots(this.$slots, 'footer')
]
);
}
}
const app = createApp({
name: 'App',
render() {
return h('div', { class: 'container' }, [h(Foo, {}, {
header: h('h1', {}, 'header'),
footer: h('p', {}, 'footer')
})])
}
})
const appDoc = document.querySelector('#app')
app.mount(appDoc);
const container = document.querySelector('.container') as HTMLElement
expect(container.innerHTML).toBe('<div class=\"foo\"><div><h1>header</h1></div><div class=\"default\">default</div><div><p>footer</p></div></div>')
})
analizar
A través del caso de prueba anterior, se encontró lo siguiente:
- renderSlot pasa el segundo argumento, y luego puede obtener las ranuras para eso
Resolución de problemas
Simplemente pase el segundo parámetro directamente en el renderSlot
codificar
export function renderSlots(slots, name = 'default') {
const slot = slots[name]
if (slot) {
return createVNode('div', {}, slot)
}
}
Este paso no es relativamente simple, en relación con el frente, solo se llama así, se considera el frente, la parte posterior es cómoda y luego se implementa la ranura del alcance
Ranuras con alcance
La ranura del alcance es esa cada ranura puede pasar datos, y los datos solo son válidos en la ranura actual, ver el caso de prueba
Casos de prueba
test('slots test', () => {
const Foo = {
name: 'Foo',
render() {
return h('div', { class: 'foo' },
[
renderSlots(this.$slots, 'header', { children: 'foo' }),
h('div', { class: 'default' }, 'default'),
renderSlots(this.$slots, 'footer')
]
);
}
}
const app = createApp({
name: 'App',
render() {
return h('div', { class: 'container' }, [h(Foo, {}, {
header: ({ children }) => h('h1', {}, 'header ' + children),
footer: h('p', {}, 'footer')
})])
}
})
const appDoc = document.querySelector('#app')
app.mount(appDoc);
const container = document.querySelector('.container') as HTMLElement
expect(container.innerHTML).toBe('<div class=\"foo\"><div><h1>header foo</h1></div><div class=\"default\">default</div><div><p>footer</p></div></div>')
})
Análisis de requerimientos
A través del caso de prueba anterior, se analizan los siguientes:
- Al pasar una ranura, se pasa una función y la función puede tomar los parámetros pasados por el subcomponente
- renderSlots puede pasar un tercer parámetro, props, para recibir los parámetros pasados por el componente secundario al componente principal
Resolución de problemas:
- Problema 1: solo necesita hacer un juicio cuando pasa la ranura, si es una función, necesita ejecutar la función y pasar los parámetros
- Problema 2: también es el juicio del contenido entrante, y la función realiza el procesamiento de parámetros entrantes
codificar
export function renderSlots(slots, name = 'default', props = {}) {
const slot = slots[name];
if (slot) {
if (isFunction(slot)) {
return createVNode('div', {}, slot(props))
}
return createVNode('div', {}, slot)
}
}
const slots = {}
for (const key in children) {
if (isFunction(children[key])) {
slots[key] = (props) => Array.isArray(children[key](props)) ? children[key](props) : [children[key](props)]
} else {
slots[key] = Array.isArray(children[key]) ? children[key] : [children[key]]
}
}
instance.slots = slots
¡En este punto, todo el caso de prueba puede pasar perfectamente!
Deje un comentario