«Изучение исходного кода Vue» вы действительно знаете, как слот «затыкается»

Vue.js
«Изучение исходного кода Vue» вы действительно знаете, как слот «затыкается»

image.png

Всем привет, это Лин Сансин, Vue реализовал набор API для распространения контента,<slot>Элементы действуют как выходы для переноса распределенного контента, это указано в документации Vue. Конкретно,slotЭто «пробел», который позволяет вам добавлять контент в компонент.Вы действительно знаете, как «вставляется» слот? Я надеюсь, что вы можете быть таким же простым, как я, и честно прочитать эту статью.

image.png

Основное использование слота Vue

одиночный слот | анонимный слот

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      
  </div>
</template>

//父组件:(引用子组件 child)
<template>
  <div class= 'app'>
     <child> 
        林三心
     </child>
  </div>
</template>

Мы знаем, что если вы добавите содержимое «Lin Sanxin» непосредственно в родительский компонент, текст «Lin Sanxin» не будет отображаться на странице. Так как же сделать так, чтобы добавленный контент отображался? Просто добавьте слот в подкомпонент.

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      <slot></slot>
  </div>
</template>

область компиляции (родительский компонент в дочернем компоненте<slot></slot>вставьте данные)

Как мы узнали выше, слот на самом деле является «пространством», которое позволяет нам добавлять контент к дочернему компоненту в родительском компоненте. Мы можем добавить любое значение данных в родительский компонент, например:

//父组件:(引用子组件 child)
<template>
  <div class= 'app'>
     <child> {{ parent }}</child>
  </div>
</template>

new Vue({
  el:'.app',
  data:{
    parent:'父组件'
  }
})

Синтаксис использования данных совсем не изменился, но можем ли мы использовать данные напрямую из дочерних компонентов? Очевидно нет! !

// 子组件 : (假设名为:child)
<template>
  <div class= 'child'>
       <slot></slot>
  </div>
</template>

new Vue({
  el:'child',
  data:{
    child:'子组件'
  }
})

// 父组件:(引用子组件 child)

<template>
  <div class= 'app'>
     <child> {{ child }}</child>
  </div>
</template>

Невозможно передавать данные непосредственно в дочерние компоненты. Потому что: все в родительском шаблоне компилируется в родительской области, все в дочернем шаблоне компилируется в дочерней области.

запасной контент (дочерние компоненты<slot></slot>установить значение по умолчанию)

Так называемый задний контент на самом деле является значением слота по умолчанию.Иногда я не добавляю контент в родительский компонент, тогда слот будет отображать значение по умолчанию, например:

//子组件 : (假设名为:child)
<template>
  <div class='child'>
      <slot>这就是默认值</slot>
  </div>
</template>

именованный слот (несколько подкомпонентов<slot></slot>соответствующий вставленному контенту)

Иногда в дочернем компоненте может быть более одного слота, так как же вставить соответствующий контент в родительский компонент точно в нужное место? Просто дайте слоту имя, то есть добавьте атрибут name.

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      <slot name='one'> 这就是默认值1</slot>
      <slot name='two'> 这就是默认值2 </slot>
      <slot name='three'> 这就是默认值3 </slot>
  </div>
</template>

родительский компонент передан, илиslot="name"(旧语法),v-slot:nameили#name(新语法)добавить контент таким образом:

//父组件:(引用子组件 child)
<template>
  <div class= 'app'>
     <child> 
        <template v-slot:"one"> 这是插入到one插槽的内容 </template>
        <template v-slot:"two"> 这是插入到two插槽的内容 </template>
        <template v-slot:"three"> 这是插入到three插槽的内容 </template>
     </child>
  </div>
</template>

Слоты с заданной областью (родительский компонент в дочернем компоненте<slot></slot>использовать данные подкомпонента)

пройти черезslotМы можем добавить содержимое к дочернему компоненту в родительском компоненте, указавslotКак мы его называем, мы можем добавлять контент из более чем одного места. Но данные, которые мы добавляем, находятся в родительском компоненте. Выше мы сказали, что не можем использовать данные в подкомпоненте напрямую, но есть ли у нас другие методы, позволяющие использовать данные подкомпонента? На самом деле, мы также можем использоватьslot-scopeПуть:

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      <slot name= 'one' :value1='child1'> 这就是默认值1</slot>    //绑定child1的数据
      <slot :value2='child2'> 这就是默认值2 </slot>  //绑定child2的数据,这里我没有命名slot
  </div>           
</template>

new Vue({
  el:'child',
  data:{
    child1:'数据1',
    child2:'数据2'
  }
})

//父组件:(引用子组件 child)
<template>
  <div class='app'>
     <child> 
        <template v-slot:one='slotone'>  
           {{ slotone.value1 }}    // 通过v-slot的语法 将子组件的value1值赋值给slotone 
        </template>
        <template v-slot:default='slotde'> 
           {{ slotde.value2 }}  // 同上,由于子组件没有给slot命名,默认值就为default
        </template>
     </child>
  </div>
</template>

Как "затыкается" слот (популярная версия)

обычный слот

//子组件 : (假设名为:child)
<template>
  <div class='child'>
      我在子组件里面
      <slot></slot>
      <slot name="one"></slot>
  </div>
</template>

//父组件:(引用子组件 child)
<template>
  <div class= 'app'>
     <child> 
        这是插入到默认插槽的内容 {{parent}}
        <template v-slot:"one"> 这是插入到one插槽的内容 {{parent}}</template>
     </child>
  </div>
</template>

new Vue({
  el:'.app',
  data:{
    parent:'父组件的值'
  }
})
  1. Сначала анализируется родительский компонент, аchildРассматривать как дочерний элемент, рассматривать слот какchildДочерний элемент обрабатывается, и значение родительской переменной извлекается в области действия родительского компонента, в результате чего получается такой узел:
{    
  tag: "div",    
   children: [{        
      tag: "child",        
      children: ['这是插入到默认插槽的内容 父组件的值', 
                  '这是插入到one插槽的内容 父组件的值']
  }]
}
  1. Подкомпонентский разбор,slotВ качестве заполнителя он будет проанализирован в функцию, что примерно означает, что он будет проанализирован в следующем
{    
    tag: "div",    
    children: [
        '我在子组件里面',
        _t('default'), // 匿名插槽,默认名称为default
        _t('one') // 具名插槽,名称为one
    ]
}
  1. Функция _t должна передавать имя слота, по умолчаниюdefault, именованный слот передается вname, функция этой функции состоит в том, чтобы получить узел слота, проанализированный на первом шаге, а затем вернуть проанализированный узел, после чего узел подкомпонента завершен, и слот успешно распознал отца——divЭтикетка
{    
    tag: "div",    
    children: ['我在子组件里面', 
                '这是插入到默认插槽的内容 父组件的值', 
                '这是插入到one插槽的内容 父组件的值']
}

слот с прицелом

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      <slot :value1='child1' :value2='child1'></slot>
      <slot name='one' :value1='child2' :value2='child2'></slot>
  </div>           
</template>

new Vue({
  el:'child',
  data:{
    child1: '子数据1',
    child2: '子数据2'
  }
})

//父组件:(引用子组件 child)
<template>
  <div class='app'>
     <child> 
         <template v-slot:default='slotde'> 
            插入默认 slot 中{{ slotde.value1 }}{{ slotde.value2 }}
        </template>
        <template v-slot:one='slotone'> 
            插入one slot 中{{ slotone.value1 }}{{ slotone.value2 }}
        </template>
     </child>
  </div>
</template>
  1. Процесс очень сложный, вот популярный момент, родительский компонент анализируется первым, и когда он сталкивается со слотом области видимости, он инкапсулирует слот в функцию и сохраняет его в дочернем элементе.childВниз
{    
 tag: "div",    
  children: [{        
     tag: "child"
     scopeSlots:{            
         default (data) { // 记住这个data参数               
             return ['插入one slot 中插入默认 slot 中' + data.value1 + data.value2]
         },
         one (data) { // 记住这个data参数             
             return ['插入one slot 中' + data.value1 + data.value2]
         }
     }
    }]
}

2. Наступает очередь подкомпонента парсить, в это время снова появляется функция _t, а подкомпонент оборачивает соответствующие данные слота в объект и передает их в функцию _t

{    
 tag: "div",    
   children: [
     '我在子组件里面',
      _t('default',{value1: '子数据1', value2: '子数据1'}),
      _t('one',{value1: '子数据2', value2: '子数据2'})
      
    ]
  }

Далее выполняется внутреннее выполнение _t.Обёрнутый объект передается в соответствующие функции в scopeSlots в качестве параметра данных и анализируется в:

{    
  tag: "div",    
   children: [
      '我在子组件里面', 
      '插入默认 slot 中 子数据1 子数据1',
      '插入one slot 中 子数据2 子数据2'
   ]
}

$slots

Увидев это, я считаю, что вы уже поняли про процесс (хотя он и не очень подробный), тогда возникает другой вопрос, эти разобранные节点VNodeГде существует объект? Вы не можете разобрать его и выбросить, верно? Должен найти место, чтобы сохранить его и сделать это真实dom, это место$slots

//子组件 : (假设名为:child)
<template>
  <div class= 'child'>
      <slot></slot>
      <slot name='one'></slot>
      <slot name='two'></slot>
      <slot name='three'></slot>
  </div>
</template>

new Vue({
  el:'.child',
  created () {
      console.log(this.$slots) // 看看里面有啥
  }
})
//父组件:(引用子组件 child)
<template>
  <div class= 'app'>
     <child> 
        <template> 这是插入到默认插槽的内容 </template>
        <template v-slot:"one"> 这是插入到one插槽的内容 </template>
        <template v-slot:"two"> 这是插入到two插槽的内容 </template>
        <template v-slot:"three"> 这是插入到three插槽的内容 </template>
     </child>
  </div>
</template>

Результат console.log:

image.png

Все здесь понимают,$slotsЯвляетсяMap,keyимя каждого слота (анонимный слотkeyдляdefault), значение, соответствующее ключу, — это узел VNode под каждым слотом, а конкретный объект VNode — это то, как он выглядит. Вы можете вывести его сами. В нем слишком много всего, поэтому я не буду его здесь показывать. Эй-эй.