Vue 实现了一套内容分发的 API, 将
元素作为承载分发内容的出口。
简单的说就是将一段html元素作为参数传递到自定义组件中。
使用
元素来预定义插槽的位置,后面可以将内容直接插在插槽位置上即可。
中也可以有默认值,当使用组件没有提供插槽内容时就会使用默认值。
官方文档:https://cn.vuejs.org/v2/api/#slot
默认插槽没有名字的插槽,一般默认插槽只有一个,自定义一个组件(导航链接组件NavLink.vue),组件的内容比较简单只有一个a标签,在a标签中使用了
元素。
<template>
<a v-bind:href="'#' + url">
<slot>插槽默认值:当没有提供插槽内容时使用这里的值slot>
a>
template>
<script>
export default {
name: 'NavLink',
props: {
url: String
}
}
script>
<template>
<div>
<nav-link url="/foo">
<span>Navigation Link To Foo Componentspan>
<br>
<span> 插槽的作用就是将一段html元素(也可以是其它自定义的组件)传参到自定义的组件中, 然后将slot部分替换掉 span>
nav-link>
<br><br>
<nav-link url="/foo">nav-link>
div>
template>
<script>
import NavLink from './NavLink'
export default {
name: 'HelloWorld',
components: {NavLink}
}
script>
查看源码可以看到
标签内的内容传递到NavLink组件,并将
元素给替换掉了。简单来说slot就是组件的一种传参方式,只不过传的是html元素。
当组件中有多个插槽位置时,可以给每个插槽命一个名字,在使用时指定插槽名称即可。
使用组件时在传递插槽内容时可以将slot属性作用在标签上,也可以作用在普通标签上。
NavLink.vue
<template>
<a v-bind:href="'#' + url">
<slot name="slot1">slot>
<slot name="slot2">slot>
<slot>slot>
a>
template>
<script>
export default {
name: 'NavLink',
props: {
url: String
}
}
script>
HelloWorld.vue
<template>
<div>
<nav-link url="/foo">
<template slot="slot1">
Navigation
template>
<h1 slot="slot2">Toh1>
<span> Foo span>
<span> Componentspan>
nav-link>
div>
template>
<script>
import NavLink from './NavLink'
export default {
name: 'HelloWorld',
components: {NavLink}
}
script>
中的内容会被当做普通字符串作用域插槽:父组件(使用自定义组件的组件,即HelloWorld.vue)访问子组件(自定义组件,即TodoList.vue)中的值。
元素上也可以用于普通元素上。TodoList.vue
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<slot v-bind:item="todo">
{{ todo.text }}
slot>
li>
ul>
template>
<script>
export default {
name: 'TodoList',
props: ['todos']
}
script>
item:用于定义一个外层组件能够通过scope访问的属性名,随便写。item的值就是v-for中的todo。
HelloWorld.vue
<template>
<div>
<todo-list :todos="todos">
<template slot-scope="scope">
<span v-if="scope.item.isComplete">√span>
{{ scope.item.text }}
template>
todo-list>
div>
template>
<script>
import TodoList from './TodoList'
export default {
name: 'HelloWorld',
components: {TodoList},
data () {
return {
todos: [
{id: 1, text: '做饭', isComplete: true},
{id: 2, text: '吃饭', isComplete: true},
{id: 3, text: '刷锅', isComplete: false}
]
}
}
}
script>
注意:scope和slot-scope功能差不多,老版本中使用scope属性,scope属性只能用在中,在新版本中使用slot-scope来替代scope,slot-scope可以用在任意元素上。新版本推荐使用slot-scope。
组件导入方式
HelloWorld.vue
<template>
<div style="width: 300px">
HelloWorld Vue
div>
template>
<script>
export default {
name: 'HelloWorld'
}
script>
Foo.vue
<template>
<div>
Foo Component
div>
template>
<script>
export default {
name: 'Foo'
}
script>
src/router/index.js import方式
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Foo from '@/components/Foo'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: HelloWorld
},
{
path: '/foo',
component: Foo
}
]
})
src/router/index.js require方式
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: resolve => require(['@/components/HelloWorld'], resolve)
},
{
path: '/foo',
component: resolve => require(['@/components/Foo'], resolve)
}
})
我们看到使用require方式,当访问哪个路由就会加载哪个js文件,按需加载,懒加载。
emit 用于自定义组件事件。
this.$emit('handleClick', value)
用于注册自定义事件,事件名称为“handleClick”,参数为value。
<template>
<div>
<button @click="handleChildClick('mengday')">自定义组件事件(子组件调用父组件的方法、子组件像父组件传值)button>
div>
template>
<script>
export default {
name: 'Emit',
methods: {
handleChildClick (value) {
this.$emit('handleClick', value)
}
}
}
script>
child元素中的handleClick事件就是Child.vue中使用emit注册的事件名称,值为当前实例的方法。
<template>
<div>
{{username}}
<child @handleClick="handleClick">child>
div>
template>
<script>
import Child from '@/components/Child'
export default {
name: 'Parent',
components: {Child},
data () {
return {
username: '--'
}
},
methods: {
handleClick (value) {
console.log('处理业务逻辑...')
// 赋值:达到子组件向父组件传参的目的
this.username = value
}
}
}
script>
import Vue from 'vue'
import Router from 'vue-router'
import Parent from '@/components/Parent'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/parent',
component: Parent
}
]
})
当点击按钮时Child会调用自己的方法handleChildClick,进而向子组件注册事件handleClick,而父组件已经定义了handleClick的值为父组件的handleClick方法,从而调用该方法。
emit: 准确的来说是用来自定义组件事件的,很多博客用来把它当成子组件像父组件传值使用,当然子组件都能调用父组件的方法了,把参数传进去就起到了子组件向父组件传值的作用,这只是emit的一种作用,仅仅把emit作为子组件向父组件传值的描述不够准确,既然子组件能够调用父组件的方法,方法中当然也可以写一些业务逻辑,所以为我认为emit用来自定义组件事件更加准确些。
父组件向子组件传值:通过props来实现
子组件向父组件传值:通过emit来实现
兄弟组件之间互相传值:通过Vuex来实现
<template>
<div>
<button @click="handleClick">自定义事件button>
div>
template>
<script>
export default {
name: 'Parent',
created: function () {
// 先注册事件
this.$on('customEvent', function (msg) {
console.log('自定义事件:' + msg)
})
},
methods: {
handleClick () {
// 触发事件:调用事件对应的处理函数
this.$emit('customEvent', '参数')
}
}
}
script>
$parent: 获取父组件的引用。拿到父组件的引用就可以操作父组件了,如修改父组件中的data,调用父组件中的方法,或者通过在子组件调用父组件用于子组件向父组件传值等。
Child.vue
<template>
<div>
<button @click="handleClick">this.$parentbutton>
div>
template>
<script>
export default {
name: 'Child',
methods: {
handleClick () {
this.$parent.msg = 'Child'
this.$parent.foo()
}
}
}
script>
<template>
<div>
{{ msg }}
<child>child>
div>
template>
<script>
import Child from '@/components/demo/Child'
export default {
name: 'Parent',
components: {Child},
data () {
return {
msg: 'Parent'
}
},
methods: {
foo () {
console.log('父组件方法...')
}
}
}
script>