一、插槽slot
Vue 實現了一套內容分發的 API, 將<slot>
元素作爲承載分發內容的出口。
簡單的說就是將一段html元素作爲參數傳遞到自定義組件中。
使用<slot></slot>
元素來預定義插槽的位置,後面可以將內容直接插在插槽位置上即可。
<slot></slot>
中也可以有默認值,當使用組件沒有提供插槽內容時就會使用默認值。
官方文檔:https://cn.vuejs.org/v2/api/#slot
1、默認插槽
默認插槽沒有名字的插槽,一般默認插槽只有一個,自定義一個組件(導航鏈接組件NavLink.vue),組件的內容比較簡單隻有一個a標籤,在a標籤中使用了<slot></slot>
元素。
<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 Component</span>
<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>
查看源碼可以看到<nav-link>
標籤內的內容傳遞到NavLink組件,並將<slot></slot>
元素給替換掉了。簡單來說slot就是組件的一種傳參方式,只不過傳的是html元素。
2、具名插槽
當組件中有多個插槽位置時,可以給每個插槽命一個名字,在使用時指定插槽名稱即可。
使用組件時在傳遞插槽內容時可以將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">To</h1>
<span> Foo </span>
<span> Component</span>
</nav-link>
</div>
</template>
<script>
import NavLink from './NavLink'
export default {
name: 'HelloWorld',
components: {NavLink}
}
</script>
<template>
中的內容會被當做普通字符串- 插槽位置即可以匿名(默認插槽)也可以命名(具名插槽),傳遞插槽內容時需要指定名稱,如果沒有指定名稱剩下沒有匹配到的內容將全部作爲匿名插槽的內容
3、作用域插槽 slot-scope
作用域插槽:父組件(使用自定義組件的組件,即HelloWorld.vue)訪問子組件(自定義組件,即TodoList.vue)中的值。
作用域插槽一般用在組件中使用v-for循環的。
slot-scope即可用於<template>
元素上也可以用於普通元素上。
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屬性只能用在<template>
中,在新版本中使用slot-scope來替代scope,slot-scope可以用在任意元素上。新版本推薦使用slot-scope。
二、路由導入組件
組件導入方式
- require方式: 用require這種方式引入的時候,會將你的component分別打包成不同的js,加載的時候也是按需加載,只用訪問這個路由網址時纔會加載這個js。
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文件,按需加載,懶加載。
三、vm.$emit( eventName, […args] )
emit 用於自定義組件事件。
·this.$emit(‘handleClick’, value)·用於註冊自定義事件,事件名稱爲“handleClick”,參數爲value。
Child.vue
<template>
<div>
<button @click="handleChildClick('mengday')">自定義組件事件(子組件調用父組件的方法、子組件像父組件傳值)</button>
</div>
</template>
<script>
export default {
name: 'Emit',
methods: {
handleChildClick (value) {
this.$emit('handleClick', value)
}
}
}
</script>
Parent.vue
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>
src/router/index.js
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來實現
四:$on(‘eventName’, function(args){ })
- $on(‘eventName’, function(args){ }) 用於註冊事件
- $emit(‘event’, arg) 用於觸發事件
<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
$parent: 獲取父組件的引用。拿到父組件的引用就可以操作父組件了,如修改父組件中的data,調用父組件中的方法,或者通過在子組件調用父組件用於子組件向父組件傳值等。
Child.vue
<template>
<div>
<button @click="handleClick">this.$parent</button>
</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>