【Vue.js】Vue官方文档学习-组件

一、注册组件

Vue.component('my-component-name', { / ... / })

组件名称

在组件的祖册中,第一个参数为组件的名称。

命名方案:

  1. 串联式命名
  2. 驼峰式命名

在引用其自定义元素时,两种方案都可以使用。但直接在DOM中引用自定义元素时串联式命名时唯一有效的方式。

全局注册方式

全局注册的组件可以在之后通过new Vue创建的Vue根实例的模板中引用。

Vue.component('my-component-name', {
  // ... options ...
})

局部注册方式

先将组件定义为纯JavaScript对象。

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

然后在创建Vue根实例的时候,components选项中定义所需要用到的组件。
对于components对象的每个属性,对象的key是自定义元素的名称,而value包含着组件的选项对象。

new Vue({
    el:'#app',
    components: {
    'component-a': ComponentA,
    'component-b': ComponentB 
    }
})

局部注册的组件在子组件中无法访问,如果想在ComponentB中访问ComponentA,则应该

var ComponentA = { /* ... */ }
var ComponentB = {
    components: {
        'component-a': ComponentA
    },
    ......
}

如果使用ES2015模块,则类似这样

import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

在模块系统中局部注册组件

建议创建一个component目录,每个组件都定义在这个文件中。
在局部注册这些组件之前,需要预先导入每个需要的组件。

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

这样就可以在componentB组件的模板内部引用ComponentA和ComponentB了。

自动化全局注册基本组件

基本组件:许多相对通用的组件(内部可能只含有一个 input 或 button 元素)并且往往在其他组件中频繁使用的一类组件。
这导致的结果是许多组件会列出很长的基础组件清单,然后在components选项中进行逐个引用:

import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

如果使用 webpack(或者内置 webpack 的 Vue CLI 3+),就可以只通过 require.context 来全局注册这些常用基础组件。
在应用程序入口文件(例如 src/main.js)中,通过全局方式导入基础组件。
全局注册方式必须在Vue根实例创建之前置入组件。

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  // components 文件夹的相对路径
  './components',
  // 是否查找子文件夹
  false,
  // 用于匹配组件文件名的正则表达式
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  // 获取组件配置
  const componentConfig = requireComponent(fileName)

  // 取得组件的 Pascal 式命名
  const componentName = upperFirst(
    camelCase(
      // 将文件名前面的 `'./` 和扩展名剥离
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )

  // 以全局方式注册组件
  Vue.component(
    componentName,
    // 如果组件是通过 `export default` 导出,
    // 则在 `.default` 中,查找组件选项,
    // 否则回退至模块根对象中,查找组件选项
    componentConfig.default || componentConfig
  )
})

二、props

用于父组件对子组件传递信息。

命名方案

HTML 属性名称对大小写不敏感,因此浏览器会将所有大写字符解释为小写字符。
当在DOM 模板中书写 prop 时,应当将驼峰式转写为等价的串联式。
如果是在使用字符串模板的场景,则没有这些限制。

Vue.component('blog-post', {
  // 在 JavaScript 中使用驼峰式(camelCase)
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中使用串联式(kebab-case) -->
<blog-post post-title="hello!"></blog-post>

传递静态props字符串

静态Prop通过为子组件在父组件中的占位符添加特性的方式来达到传值的目的

<div id="example">
  <parent></parent>
</div>

<script type="text/javascript">
    var childNode = {
        template:`
        <div>{{ message }}</div>`,
        props: ['message']
    }
    var parentNode = {
        template:`
        <div class="parent">
            <child message="aaa"></child>
            <child message="bbb"></child>
        </div>`,
        components: {
            'child':childNode
        }
    }

    var app1 = new Vue({
        el: '#example',
        components: {
            'parent':parentNode
        }
    })

传递动态props字符串

通过v-bind给props分配动态的值,每当父组件的数据变化时,该变化也会传导给子组件:

<div id="app2">
      <parent2></parent2>
    </div>
    //    动态prop
    var childNode2 ={
        template:`<div>{{myMessage}}</div>`,
        props:['myMessage']
    }

    var parentNode2 ={
        template:`
        <div>
            <child :myMessage="data1"></child>
            <child :myMessage="data2"></child>
        </div>`,
        components:{
            'child':childNode2
        },
        data(){
            return{
                'data1':'动态aaa',
                'data2':'动态bbb'
            }
        }
    }

    var app2 = new Vue({
        el:'#app2',
        components:{
            'parent2':parentNode2
        }
    })

在这两个例子中我们向prop传递的都是字符串,实际上也可以传递任意类型的值。

传递数字

传递数字时如果使用“1”字面量则传递的是字符串而非数字。
需要使用 v-bind传递一个实际的 number,从而让它的值被当作JS表达式计算.

    <!--传递数值-->
    <div id="app3">
          <my-parent></my-parent>
    </div>
    //    传递数值
    var childNode3 = {
        template:`<div>{{myMessage}}的类型是{{type}}</div>`,
        props:['myMessage'],
        computed:{
            type(){
                return typeof this.myMessage
            }
        }
    }

    var parentNode3 = {
        template:`
        <div>
            <my-child :my-message="1"></my-child>
        </div>`,
        components:{
            'myChild':childNode3
        }
    }

    var app3 = new Vue({
        el:'#app3',
        components:{
            'MyParent':parentNode3
        }
    })

或者可以使用动态props,在data属性中设置对应的数字1。

传递布尔值

<!-- 没有设定值得属性默认值为‘true’ -->
<blog-post favorited></blog-post>

<!-- `false` 是静态的,这就需要我们使用 v-bind, -->
<!-- 来告诉 Vue 它是以 JavaScript 表达式表现,而不是一个字符串 -->
<base-input v-bind:favorited="false">

<!-- 将一个变量,动态地分配到属性值上 -->
<base-input v-bind:favorited="post.currentUserFavorited">

传递数组

<!-- array 是静态的,这就需要我们使用 v-bind, -->
<!-- 来告诉 Vue 它是以 JavaScript 表达式表现,而不是一个字符串 -->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>

<!-- 将一个变量,动态地分配到属性值上 -->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>

传递object对象

<!-- object 是静态的,这就需要我们使用 v-bind, -->
<!-- 来告诉 Vue 它是以 JavaScript 表达式表现,而不是一个字符串 -->
<blog-post v-bind:comments="{ id: 1, title: '我的 Vue 旅程' }"></blog-post>

<!-- 将一个变量,动态地分配到属性值上 -->
<blog-post v-bind:post="post"></blog-post>

传递一个对象的所有属性

post: {
  id: 1,
  title: '我的 Vue 旅程'
}

//    以下模板:
<blog-post v-bind="post"></blog-post>

//    等价于:
<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

单向数据流

prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解

  另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着不应该在子组件内部改变 prop。如果这么做了,Vue 会在控制台给出警告

  下面是一个典型例子

    <!--单向数据流-->
    <div id="app4">
        <parent4></parent4>
    </div>

    //    单向数据流
    var childNode4 = {
        template:`
        <div class="child">
            <div>
                <span>子组件数据</span>
                <input v-model="childMsg">
            </div>
            <p>{{childMsg}}</p>
        </div>`,
        props:['childMsg']
    }

    var parentNode4 = {
        template:`
        <div class="parent">
            <div>
                <span>父组件数据</span>
                <input v-model="Msg">
            </div>
            <p>{{Msg}}</p>
            <child :child-msg="Msg"></child>
        </div>`,
        components:{
            'child':childNode4
        },
        data(){
            return{
                'Msg':'August'
            }
        }
    }

    var app4 = new Vue({
        el:'#app4',
        components:{
            'parent4':parentNode4
        }
    })

父组件数据变化时,子组件数据会相应变化;而子组件数据变化时,父组件数据不变,并在控制台显示警告。

修改prop数据

  修改prop中的数据,通常有以下两种原因

  1、prop 作为初始值传入后,子组件想把它当作局部数据来用

  2、prop 作为初始值传入,由子组件处理成其它数据输出

  [注意]JS中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态

  对于这两种情况,正确的应对方式是

  1、定义一个局部变量,并用 prop 的值初始化它

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

  但是,定义的局部变量counter只能接受initialCounter的初始值,当父组件要传递的值发生变化时,counter无法接收到最新值

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  data(){
    return{
      temp:this.childMsg
    }
  },
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

除初始值外,父组件的值无法更新到子组件中。

 2、定义一个计算属性,处理 prop 的值并返回

<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  computed:{
      temp(){
        return this.childMsg
      }
  },
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

由于子组件使用的是计算属性,所以,子组件的数据无法手动修改。

  3、更加妥帖的方案是,使用变量储存prop的初始值,并使用watch来观察prop的值的变化。发生变化时,更新变量的值

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>
      <span>子组件数据</span>
      <input v-model="temp">
    </div>
    <p>{{temp}}</p>
  </div>
  `,
  props:['childMsg'],
  data(){
    return{
      temp:this.childMsg
    }
  },
  watch:{
    childMsg(){
      this.temp = this.childMsg
    }
  }
};
var parentNode = {
  template: `
  <div class="parent">
    <div>
      <span>父组件数据</span>
      <input v-model="msg">
    </div>
    <p>{{msg}}</p>
    <child :child-msg="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':'match'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

prop验证

可以为组件的 props 指定验证规格。如果传入的数据不符合规格,Vue会发出警告。当组件给其他人使用时,这很有用

  要指定验证规格,需要用对象的形式,而不能用字符串数组

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    propA: Number,
    
    // 多种类型
    propB: [String, Number],
    
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})

  type 可以是下面原生构造器

String
Number
Boolean
Function
Object
Array
Symbol

type 也可以是一个自定义构造器函数,使用 instanceof 检测。

  当 prop 验证失败,Vue 会在抛出警告 (如果使用的是开发版本)。props会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用

  下面是一个简单例子,如果传入子组件的message不是数字,则抛出警告

<div id="example">
  <parent></parent>
</div>

<script>
var childNode = {
  template: '<div>{{message}}</div>',
  props:{
    'message':Number
  }
}
var parentNode = {
  template: `
  <div class="parent">
    <child :message="msg"></child>
  </div>`,
  components: {
    'child': childNode
  },
  data(){
    return{
      msg: '123'
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

  传入数字123时,则无警告提示。传入字符串'123'时,就有警告。

将上面代码中,子组件的内容修改如下,可自定义验证函数,当函数返回为false时,则输出警告提示

var childNode = {
  template: '<div>{{message}}</div>',
  props:{
    'message':{
      validator: function (value) {
        return value > 10
      }
    }
  }
}

在父组件中传入msg值为1,由于小于10,则输出警告提示

var parentNode = {
  template: `
  <div class="parent">
    <child :message="msg"></child>
  </div>`,
  components: {
    'child': childNode
  },
  data(){
    return{
      msg:1
    }
  }
};

三、自定义事件

  父组件使用props传递数据给子组件,子组件怎么跟父组件通信呢?这时,Vue的自定义事件就派上用场了。

事件绑定

  每个 Vue 实例都实现了事件接口 (Events interface),即

使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件

父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

    <!--自定义事件-->
    <div id="app5">
        <parent5></parent5>
    </div>

    //    自定义事件
    var childNode5 = {
        template:`
        <button @click="incrementCounter">{{counter}}</button>`,
        data(){
            return{
                counter:0
            }
        },
        methods:{
            incrementCounter(){
                this.counter++;
                this.$emit('increment');
            }
        }
    }

    var parentNode5 = {
        template:`
        <div class="parent">
            <p>{{total}}</p>
            <child @increment="incrementTotal"></child>
            <child @increment="incrementTotal"></child>
        </div>`,
        data(){
            return{
                'total':0
            }
        },
        methods:{
            incrementTotal(){
                this.total++;
            }
        },
        components:{
            'child':childNode5
        }
    }

    var app5 = new Vue({
        el:'#app5',
        components:{
            'parent5':parentNode5
        }
    })

命名约定

  自定义事件的命名约定与组件注册及props的命名约定都不相同,由于自定义事件实质上也是属于HTML的属性,所以其在HTML模板中,最好使用中划线形式

<child @pass-data="getData"></child>

而子组件中触发事件时,同样使用中划线形式

 this.$emit('pass-data',this.childMsg)

数据传递

  子组件通过$emit可以触发事件,第一个参数为要触发的事件,第二个事件为要传递的数据

  父组件通过$on监听事件,事件处理函数的参数则为接收的数据

    <!--数据传递-->
    <div id="app6">
        <parent6></parent6>
    </div>

    //    数据传递
    var childNode6 = {
        template:`
        <div class="child">
            <div>
                <span>子组件数据</span>
                <input v-model="childMsg" @input="data">
            </div>
            <p>{{childMsg}}</p>
        </div>`,
        data(){
            return{
                childMsg:''
            }
        },
        methods:{
            data(){
                this.$emit('pass-data',this.childMsg)
            }
        }
    }

    var parentNode6 = {
        template:`
        <div class="parent">
            <div>
                <span>父组件数据</span>
                <input v-model="msg">
            </div>
            <p>{{msg}}</p>
            <child @pass-data="getData"></child>
        </div>`,
        components:{
            'child':childNode6
        },
        data(){
            return{
                'msg':'August'
            }
        },
        methods:{
            getData(value){
                this.msg = value;
            }
        }
    }

    var app6 = new Vue({
        el:'#app6',
        components:{
            'parent6':parentNode6
        }
    })

修改子组件中的input值,则父组件到接收到相同值,则显示出来

sync修饰符

在一些情况下,可能会需要对一个 prop 进行双向绑定。事实上,这正是Vue1.x中的 .sync修饰符所提供的功能。当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定的值。这很方便,但也会导致问题,因为它破坏了单向数据流的假设。

由于子组件改变 prop 的代码和普通的状态改动代码毫无区别,当光看子组件的代码时,完全不知道它何时悄悄地改变了父组件的状态。这在 debug 复杂结构的应用时会带来很高的维护成本,上面所说的正是在 2.0 中移除 .sync 的理由

  从 2.3.0 起重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 侦听器。

<comp :foo.sync="bar"></comp>

会被扩展为

<comp :foo="bar" @update:foo="val => bar = val"></comp>

  当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

this.$emit('update:foo', newValue)

  因此,可以使用.sync来简化自定义事件的操作,实现子组件向父组件的数据传递

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <div>子组件数据:{{childMsg}}</div>
    <input v-model="childMsg">
    <button @click=add >+1</button>
  </div>
  `,
  data(){
    return{
      childMsg: 0
    }
  },
  methods:{
    add(){
      this.childMsg++;
      this.$emit('update:foo',this.childMsg);
    }
  }
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件数据:{{msg}}</p>
    <child :foo.sync="msg"></child>
  </div>
  `,
  components: {
    'child': childNode
  },
  data(){
    return {
      'msg':0
    }
  }
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

四、slot

  为了让组件可以组合,需要一种方式来混合父组件的内容子组件自己的模板。这个过程被称为 内容分发。

Vue实现了一个内容分发 API,参照了当前 Web 组件规范草案,使用特殊的 <slot> 元素作为原始内容的插槽。

编辑作用域

在深入内容分发API之前,先明确内容在哪个作用域里编译。假定模板为

<child-component>
        {{ message }}
</child-component>

message应该是绑定到父组件的数据还是子组件的数据?答案是父组件。
组件作用域简单地来说就是:父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译。

一个常见的错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法:

<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>

如果要绑定作用域内的指令到一个组件的根节点,应当在组件自己的模板上做:

Vue.component('child-component', {
  // 有效,因为是在正确的作用域内
  template: '<div v-show="someChildProperty">Child</div>',
  data: function () {
    return {
      someChildProperty: true
    }
  }
})

类似的,分发内容是在父作用域内编译。

默认丢弃

一般地,如果子组件模板不包含<slot>插口,父组件的内容将会被丢弃

<div id="example">
  <parent></parent>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
  </div>
  `,
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p>测试内容</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};
// 创建根实例
new Vue({
  el: '#example',
  components: {
    'parent': parentNode
  }
})
</script>

如下图所示,<child>所包含的<p>测试内容</p>被丢弃
图片描述

内联模板

如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而忽略真实的模板内容

  但是 inline-template 让模板的作用域难以理解

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
  </div>
  `,
};

var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child inline-template>
      <p>测试内容</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

匿名slot

当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
    <slot></slot>
  </div>
  `,
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p>测试内容</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

如果出现多于1个的匿名slot,vue将报错

默认值
  最初在 <slot> 标签中的任何内容都被视为备用内容,或者称为默认值。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容

  当slot存在默认值,且父元素在<child>中没有要插入的内容时,显示默认值。

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
    <slot><p>我是默认值</p></slot>
  </div>
  `,
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child></child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

  当slot存在默认值,且父元素在<child>中存在要插入的内容时,则显示设置值

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
    <slot><p>我是默认值</p></slot>
  </div>
  `,
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p>我是设置值</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

具名slot

<slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。
多个 slot 可以有不同的名字。
具名 slot 将匹配内容片段中有对应 slot 特性的元素

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
    <slot name="my-header">头部默认值</slot>
    <slot name="my-body">主体默认值</slot>
    <slot name="my-footer">尾部默认值</slot>
  </div>
  `,
};

var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p slot="my-header">我是头部</p>
      <p slot="my-footer">我是尾部</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

  仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的备用插槽。

匿名slot只能作为没有slot属性的元素的插槽,有slot属性的元素如果没有配置slot,则会被抛弃。
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p slot="my-body">我是主体</p>
      <p>我是其他内容</p>
      <p slot="my-footer">我是尾部</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p slot="my-body">我是主体</p>
      <p>我是其他内容</p>
      <p slot="my-footer">我是尾部</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

<p slot="my-body">插入<slot name="my-body">中,<p>我是其他内容</p>插入<slot>中,而<p slot="my-footer">被丢弃
图片描述

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
    <slot name="my-body">主体默认值</slot>
  </div>
  `,
};

var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <p slot="my-body">我是主体</p>
      <p>我是其他内容</p>
      <p slot="my-footer">我是尾部</p>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

  <p>我是其他内容</p>和<p slot="my-footer">都被抛弃

图片描述

作用域插槽

  作用域插槽是一种特殊类型的插槽,用作使用一个 (能够传递数据到) 可重用模板替换已渲染元素。

  在子组件中,只需将数据传递到插槽,就像将 props 传递给组件一样

<div class="child">
  <slot text="hello from child"></slot>
</div>

  在父级中,具有特殊属性 scope 的 <template> 元素必须存在,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 props 对象

var childNode = {
  template: `
  <div class="child">
    <p>子组件</p>
      <slot xxx="hello from child"></slot>
  </div>
  `,
};
var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <template scope="props">
        <p>hello from parent</p>
        <p>{{ props.xxx }}</p>
      </template>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

  如果渲染以上结果,得到的输出是

图片描述

列表组件
作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项

var childNode = {
  template: `
  <ul>
    <slot name="item" v-for="item in items" :text="item.text">默认值</slot>
  </ul>
  `,
  data(){
    return{
      items:[
        {id:1,text:'第1段'},
        {id:2,text:'第2段'},
        {id:3,text:'第3段'},
      ]
    }
  }
};

var parentNode = {
  template: `
  <div class="parent">
    <p>父组件</p>
    <child>
      <template slot="item" scope="props">
        <li>{{ props.text }}</li>
      </template>
    </child>
  </div>
  `,
  components: {
    'child': childNode
  },
};

图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值