Home > Web Front-end > JS Tutorial > body text

Using slots/scoped slots in Vue in actual projects

php中世界最好的语言
Release: 2018-06-11 15:31:44
Original
1479 people have browsed it

This time I will bring you the use of slots/scoped slots in Vue in practical projects. What are the precautions for using slots/scoped slots in Vue in practical projects? The following is a practical case, let’s take a look.

I have always been interested in slot slots in Vue. Here are some simple understandings of mine. I hope it can help everyone better understand slot slots.

Below Combined with an example, briefly explain the working principle of slots

The template of the dx-li subcomponent is as follows:

  •      你好!  
  • dx-ul父组件的template如下:
          hello juejin!  
    结合上述例子以及vue中相关源码进行分析 dx-ul父组件中template编译后,生成的组件render函数: module.exports={  render:function (){   var _vm=this;   var _h=_vm.$createElement;   var _c=_vm._self._c||_h;   // 其中_vm.v为createTextVNode创建文本VNode的函数   return _c('ul',      [_c('dx-li', [_vm._v("hello juejin!")])],     1)  },  staticRenderFns: [] }
    Copy after login

    The passed slot content 'hello juejin!' will be Compiled into a child node of the dx-li subcomponent VNode node.

    Render the dx-li subcomponent, in which the render function of the subcomponent:

    module.exports={
     render:function (){
      var _vm=this;
      var _h=_vm.$createElement;
      var _c=_vm._self._c||_h;
      // 其中_vm._v 函数为renderSlot函数
      return _c('li', 
        {staticClass: "dx-li" }, 
        [_vm._t("default", [_vm._v("你好 掘金!")])], 
        2
       )
      },
     staticRenderFns: []
    }
    Copy after login

    During the initialization of the dx-li subcomponent vue instance, the initRender function will be called:

    function initRender (vm) {
     ...
     // 其中_renderChildren数组,存储为 'hello juejin!'的VNode节点;renderContext一般为父组件Vue实例
     这里为dx-ul组件实例
     vm.$slots = resolveSlots(options._renderChildren, renderContext);
     ...
    }
    Copy after login

    The resolveSlots function is:

    /**
     * 主要作用是将children VNodes转化成一个slots对象.
     */
    export function resolveSlots (
     children: ?Array,
     context: ?Component
    ): { [key: string]: Array } {
     const slots = {}
     // 判断是否有children,即是否有插槽VNode
     if (!children) {
     return slots
     }
     // 遍历父组件节点的孩子节点
     for (let i = 0, l = children.length; i < l; i++) {
     const child = children[i]
     // data为VNodeData,保存父组件传递到子组件的props以及attrs等
     const data = child.data
     /* 移除slot属性
     *  
     * 编译成span的VNode节点data = {attrs:{slot: "abc"}, slot: "abc"},所以这里删除该节点attrs的slot
     */
     if (data && data.attrs && data.attrs.slot) {
      delete data.attrs.slot
     }
     /* 判断是否为具名插槽,如果为具名插槽,还需要子组件/函数子组件渲染上下文一致。主要作用:
     *当需要向子组件的子组件传递具名插槽时,不会保持插槽的名字。
     * 举个栗子:
     * child组件template: 
     * 

     * 

     * 

     * 

     * parent组件template:  *   * main组件template:  * foo  * 此时main渲染的结果:  * 

     * 

    foo

       

     * 

     */  if ((child.context === context || child.fnContext === context) &&   data && data.slot != null  ) {   const name = data.slot   const slot = (slots[name] || (slots[name] = []))   // 这里处理父组件采用template形式的插槽   if (child.tag === 'template') {   slot.push.apply(slot, child.children || [])   } else {   slot.push(child)   }  } else {   // 返回匿名default插槽VNode数组   (slots.default || (slots.default = [])).push(child)  }  }  // 忽略仅仅包含whitespace的插槽  for (const name in slots) {  if (slots[name].every(isWhitespace)) {   delete slots[name]  }  }  return slots }
    Copy after login

    Then when the dx-li component is mounted, the dx-li component render function will be called. In the process, the renderSlot function will be called:

    export function renderSlot (
      name: string, // 子组件中slot的name,匿名default
      fallback: ?Array, // 子组件插槽中默认内容VNode数组,如果没有插槽内容,则显示该内容
      props: ?Object, // 子组件传递到插槽的props
      bindObject: ?Object // 针对 obj必须是一个对象
     ): ?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)
      }
      // 传入props生成相应的VNode
      nodes = scopedSlotFn(props) || fallback
      } else {
      // 如果父组件没有传递作用域插槽
      const slotNodes = this.$slots[name]
      // warn duplicate slot usage
      if (slotNodes) {
       if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) {
       warn(
        `Duplicate presence of slot "${name}" found in the same render tree ` +
        `- this will likely cause render errors.`,
        this
       )
       }
       // 设置父组件传递插槽的VNode._rendered,用于后面判断是否有重名slot
       slotNodes._rendered = true
      }
      // 如果没有传入插槽,则为默认插槽内容VNode
      nodes = slotNodes || fallback
      }
      // 如果还需要向子组件的子组件传递slot
      /*举个栗子:
      * Bar组件: 

      * Foo组件:

      * main组件:

    hello

      * 最终渲染:

    hello

      */   const target = props && props.slot   if (target) {   return this.$createElement('template', { slot: target }, nodes)   } else {   return nodes   }  }
    Copy after login

    scoped slots understanding

    The template of the dx-li subcomponent is as follows:

  •       hello juejin!  
  • dx-ul父组件的template如下:
              {{scope.str}}     
    结合例子和Vue源码简单作用域插槽 dx-ul父组件中template编译后,产生组件render函数: module.exports={  render:function (){   var _vm=this;   var _h=_vm.$createElement;   var _c=_vm._self._c||_h;    return _c('ul', [_c('dx-li', {    // 可以编译生成一个对象数组    scopedSlots: _vm._u([{     key: "default",     fn: function(scope) {     return _c('span',       {},      [_vm._v(_vm._s(scope.str))]     )     }    }])    })], 1)   },  staticRenderFns: []  }
    Copy after login

    The _vm._u function:

    function resolveScopedSlots (
     fns, // 为一个对象数组,见上文scopedSlots
     res
    ) {
     res = res || {};
     for (var i = 0; i < fns.length; i++) {
      if (Array.isArray(fns[i])) {
       // 递归调用
       resolveScopedSlots(fns[i], res);
      } else {
       res[fns[i].key] = fns[i].fn;
      }
     }
     return res
    }
    Copy after login

    The subsequent rendering process of the subcomponent is similar to slots. The principle of scoped slots is basically the same as that of slots. The difference is that when compiling the parent component template, a function that returns a VNode will be generated. When the child component matches the scope slot function passed by the parent component, the function is called to generate the corresponding VNode.

    I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

    Recommended reading:

    How to use vue to make a single-page application front-end routing

    Local environment operation node server cross-domain Usage summary

    The above is the detailed content of Using slots/scoped slots in Vue in actual projects. For more information, please follow other related articles on the PHP Chinese website!

    Related labels:
    vue
    source:php.cn
    Statement of this Website
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact [email protected]
    Popular Tutorials
    More>
    Latest Downloads
    More>
    Web Effects
    Website Source Code
    Website Materials
    Front End Template
    About us Disclaimer Sitemap
    php.cn:Public welfare online PHP training,Help PHP learners grow quickly!