單文件組件
- 簡單來說,單文件組件就是組件的行爲、數據、結構等等統一放在一個文件裏由Vue的構建系統識別並渲染的
*.vue
文件; - 隨着vue cli 的更新迭代,任然使用new Vue的方式建立全局組件並在各個頁面選擇容器進行投放顯然已經落伍了,單文件組件開發在大型項目中已經非常的普遍!請看下圖:(這是官方文檔裏給出的單文件組件示例圖)
- 或者說這樣的結構
<template>
<div>
<h1>demo</h1>
<h1> {{name}} </h1>
<demoComp/>
</div>
</template>
<script>
import demoComp from './view/democomp'
export default {
name:'sub-menu',
components:{demoComp},
props: ['menuInfo'],
data:{
return:{
name:'haha'
}
},
methods:{
hello(){
console.log('hello')
}
},
created(){
}
}
</script>
<style>
*{
margin: 0;
padding: 0;
}
</style>
單文件遞歸的函數式組件
- 注意點1:聲明
functional
單文件組件如此聲明可以不用再js裏增加functional:true
屬性值
單文件組件聲明方式:
<template functional>
<div>{{ props.foo }}</div>
</template>
舊聲明方式:
Vue.component('smart-list', {
//標記爲函數式組件
functional: true,
//render函數
render: function (createElement, context) {
//規定組件的渲染規則
function appropriateListComp() {
}
//生成模板
return createElement(
//模板標記爲渲染規則函數返回值
},
props: {
items: {
type: Array,
required: true
},
isOrdered: Boolean
}
})
- 在自己的組件裏複用自己:如果是普通組件,需要設置name屬性,name的值就是該普通組件的名稱,使用該普通組件需要這個名稱
<template>
<democom/>
<template/>
export default{
name:'democom'
}
如果是函數式組件就不需要另外在name屬性中聲明瞭!
- 注意點2:組件的數據傳遞
由於函數式組件都是在渲染函數上下文中進行求值,所以,不能通過data,不能通過this對數據進行訪問,官方文檔也做了詳細的闡述:
組件需要的一切都是通過上下文傳遞,包括:
- props:提供所有 prop 的對象
- children: VNode 子節點的數組
- slots: 返回所有插槽的對象的函數
- scopedSlots: (2.6.0+) 一個暴露傳入的作用域插槽以及函數形式的普通插槽的對象。
- data:傳遞給組件的數據對象,作爲 createElement 的第二個參數傳入組件
- parent:對父組件的引用
- listeners: (2.3.0+) 一個包含了所有在父組件上註冊的事件偵聽器的對象。這只是一個指向 data.on 的別名。
- injections: (2.3.0+) 如果使用了 inject 選項,則該對象包含了應當被注入的屬性。
- 在添加
functional: true
之後,錨點標題組件的 render 函數之間簡單更新增加 context 參數,this.$slots.default
更新爲context.children
,之後this.level 更新爲context.props.level
。
因爲函數式組件只是一個函數,所以渲染開銷也低很多。然而,對持久化實例的缺乏也意味着函數式組件不會出現在 Vue devtools 的組件樹裏。
以我的一個例子來講:例子來源於ant-design-vue的導航組件,很多大型單頁項目裏,導航會跟着用戶的操作進行動態的變化,如果冗雜的手動書寫導航菜單並用顯隱控制顯然是開銷巨大的!那麼根據設定好的數據進行遞歸渲染明顯是一個可行的好辦法!
組件地址
忽略我是用的pug語法,使用了一些事件方法,我們看重點:
首先,a-menu是ant的導航組件容器,子組件在a-menu下不能用其他盒子包裹,子組件包含兩種:普通導航a-menu-item
和摺疊導航 a-sub-menu
,我這裏選擇將摺疊作爲依據進行判斷然後重複遞歸渲染:
.content
.leftnav
a-menu(
:defaultSelectedKeys="['1']"
:defaultOpenKeys="['2']"
:mode="'inline'"
:inlineCollapsed="collapsed"
@click="handleClick")
template(v-for="item in navigations")
a-menu-item(
v-if="!item.children"
:key="item.key")
a-icon(type="pie-chart")
span {{item.title}}
sub-menu(
v-else
:menu-info="item"
:key="item.key")
引入自定義函數式組件:
import SubMenu from '../components/subMenu'
components:{
cub-menu:SubMenu
}
<template lang='pug' functional>
a-sub-menu(
:key="props.menuInfo.key")
span(slot="title")
a-icon(type="mail")
span {{ props.menuInfo.title }}
template(v-for="item in props.menuInfo.children")
a-menu-item(
v-if="!item.children"
:key="item.key")
a-icon(type="pie-chart")
span {{ item.title }}
sub-menu(
v-else
:key="item.key"
:menu-info="item")
</template>
<script>
export default {
// name:'sub-menu',
props: ['menuInfo'],//遵守規範,html不適用大寫字母,駝峯命名會被解析成短橫線
}
</script>
看一下我的數據:
navigations: [{
key: '1',
title: 'Option 1',
},{
key: '2',
title: 'Navigation 2',
children: [{
key: '2.1',
title: 'Navigation 3',
children: [
{ key: '2.1.1',
title: 'Option 2.1.1',
},
]},
],
},{
key: '3',
title: 'Navigation 3',
children: [{
key: '3.1',
title: 'Navigation 4',
children: [
{ key: '3.1.1',
title: 'Option 3.1.1',
},
],
}],
}],
渲染出來的效果:
父組件通過v-bind:menu-info:"item"
想子組件傳遞了含有children的數據,子組件通過props接受,並賦值到menuInfo
中,數據通過props.menuInfo
調用,注意的是,vue的數據傳遞模板裏使用短橫線({{ props.menu-info.title }}
)是不行的,所以在props裏使用menuInfo命名!雖然:menuInfo="item"
也不會報錯,但是並不符合html的規範,不建議在html的標籤裏使用駝峯命名(組件會被js先解析之後再添加到DOM裏渲染,故組件名是可以使用駝峯命名的)!