做者:Milos Proticcss
譯者:前端小智html
來源:medium前端
阿里雲最近在作活動,低至2折,真心以爲很划算了,能夠點擊本條內容或者連接進行參與: promotion.aliyun.com/ntms/yunpar…vue
騰訊雲最近在作活動,百款雲產品低至 1 折,能夠點擊本條內容或者連接進行參與node
狀態管理一般在較小的項目並不須要,可是當涉及到更大的範圍時,如企業級的應用大部分須要它了。簡單的說,狀態是一個包含應用程序使用的最新值的對象。可是,若是我們從結構的、更抽象的角度來看待它,就會清楚地看到,狀態是複雜應該中重要一塊,它使可以構建乾淨的體系結構,並將關注點強有力地分離開來。git
一般,缺少經驗的開發人員沒法預測對狀態管理的需求,以及如何實現狀態管理,所以很難了解狀態管理的重要性。若是基於狀態的組件堆積起來,它們之間的數據管理和共享將成爲一場噩夢。從長遠來看,擁有的基於狀態的組件越多,出現的問題就越多。github
若是沒有使用外部包進行狀態管理,那麼最好儘量少地使用基於狀態的組件,而展現組件則使用圍繞它們構建的狀態。編程
Vue 中的無狀態組件其實就是函數組件。但函數組件又是啥呢? 要回答這個問題,我們首先必須理解什麼是函數式編程。數組
與將程序分解爲對象的面向對象方法不一樣,函數式編程鼓勵將程序分解爲小函數,這些小函數用於造成更高級的程序。咱們建立的函數不依賴於或能夠改變任何外部狀態,這致使另外一個觀察結果,對於給定的輸入,它們老是返回相同的輸出。微信
所以,函數組件是沒有狀態的組件,而且能夠更改它。函數組件輸出老是基於給定的輸入。在 Vue 方面,這類組件會根據給定的props
給出不一樣的輸出。
Vue 提供了一種定義函數組件的簡單方法。我們只須要給個 functional
關鍵字就能夠。在 2.5.0 及以上版本中,若是使用了單文件組件,那麼基於模板的函數式組件能夠這樣聲明::
<template functional>
<div> 函數/無狀態組件 </div>
</template>
複製代碼
或者
export default {
functional: true,
props: {
// ...
},
render(createElement, context) {
return createElement(
'div', '函數/無狀態組件'
)
}
}
複製代碼
注意:在 2.3.0 以前的版本中,若是一個函數式組件想要接收
prop
,則props
選項是必須的。在 2.3.0 或以上的版本中,你能夠省略props
選項,全部組件上的特性都會被自動隱式解析爲prop
。
當使用函數式組件時,該引用將會是
HTMLElement
,由於他們是無狀態的也是無實例的。
須要注意的是,傳遞給函數組件的唯一數據是props
。這些組件是徹底無狀態的(沒有響應數據),它們忽略傳遞給它們的任何狀態,而且不觸發任何生命週期方法(created
、mounted
等等)。
並且,我們也不能經過使用 this
關鍵字來訪問實例,由於這些組件也是不實例化的。相反,組件須要的全部東西都是經過context
提供的。在render
函數中,它做爲createElement
方法的第二個參數傳遞。
組件須要的一切都是經過 context
參數傳遞,它是一個包括以下字段的對象:
props
:提供全部 prop 的對象
children
: VNode 子節點的數組
slots
: 一個函數,返回了包含全部插槽的對象
scopedSlots
: (2.6.0+) 一個暴露傳入的做用域插槽的對象。也以函數形式暴露普通插槽。
data
:傳遞給組件的整個數據對象,做爲 createElement 的第二個參數傳入組件
parent
:對父組件的引用
listeners
: (2.3.0+) 一個包含了全部父組件爲當前組件註冊的事件監聽器的對象。這是 data.on 的一個別名。
injections
: (2.3.0+) 若是使用了 inject 選項,則該對象包含了應當被注入的屬性。
到目前爲止,我們已經瞭解到函數組件是無狀態的,在它們的核心中,它們只是可執行的函數,接受一些輸入並根據其提供輸出。
就它們的用法而言,由於函數式組件只是函數,因此渲染開銷也低不少,這也意味着它們是很是高效的,不須要花太多時間渲染。同時,考慮高階組件,它們不須要任何狀態,它們所要作的就是用額外的邏輯或樣式包裝給定的子組件。
接下來,通例事例展現同樣啥時使用函數組件,函數組件很是適合此類任務。
在這個示例中,我們建立一個panel
組件,它充當一個包裝器,並提供所需的樣式。子組件將在panel
主體中渲染:
export default {
name: 'panel',
functional: true,
props: {
title: String
},
render(createElement, context) {
const slots = context.slots();
const header = createElement('header', {
attrs: { class: 'panel-header'}
}, context.props.title);
const body = createElement('main', {
attrs: { class: 'panel-body'}
}, slots.default);
return createElement('section', {
attrs: { class: 'panel' }
}, [header, body]);
}
}
複製代碼
如上所述,此組件的惟一目的是提供相似於面板(卡片)的樣式,它有header
和main
元素,分別保存面板標題和HTML
內容。整個過程是經過使用render
函數中的createElement
參數在中完成。createElement
是 Vue 核心中實現的虛擬 Dom 系統的一部分。
虛擬 DOM
Vue 經過創建一個虛擬 DOM 來追蹤本身要如何改變真實 DOM。請仔細看這行代碼:
return createElement('h1', this.blogTitle)
複製代碼
createElement
到底會返回什麼呢?其實不是一個實際的 DOM
元素。它更準確的名字多是 createNodeDescription
,由於它所包含的信息會告訴 Vue 頁面上須要渲染什麼樣的節點,包括及其子節點的描述信息。咱們把這樣的節點描述爲「虛擬節點 (virtual node)」,也常簡寫它爲「VNode」。「虛擬 DOM」是咱們對由 Vue 組件樹創建起來的整個 VNode
樹的稱呼。
createElement 參數
接下來你須要熟悉的是如何在 createElement 函數中使用模板中的那些功能。這裏是 createElement 接受的參數:
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一個 HTML 標籤名、組件選項對象,或者
// resolve 了上述任何一種的一個 async 函數。必填項。
'div',
// {Object}
// 一個與模板中屬性對應的數據對象。可選。
{
// (詳情見下一節)
},
// {String | Array}
// 子級虛擬節點 (VNodes),由 `createElement()` 構建而成,
// 也可使用字符串來生成「文本虛擬節點」。可選。
[
'先寫一些文字',
createElement('h1', '一則頭條'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
複製代碼
面板 CSS 樣式以下:
.panel {
margin-bottom: .5rem
}
.panel, .panel-header {
border: 1px solid #d3d3d3;
border-radius: 4px;
}
.panel-header, .panel-body, .panel {
padding: .5rem;
}
.panel-header {
background-color:#efefef;
color: #eeeee
}
複製代碼
這是一個簡單直接的 CSS,提供了一些padding
和color
。
如今,爲了讓例子更加生動爲此,我們再建立兩個附加組件,一個顯示汽車列表,另外一個只是一個簡單lorem-ipsum
的文本組件,要求它們具備相同的面板樣式和外觀。
列表組件:
export default {
name: 'cars',
props: {
data: Array
}
}
複製代碼
template:
<template>
<ul>
<li v-for="car in data" :key="car">{{car}}</li>
</ul>
</template>
複製代碼
文本組件:
export default {
name: 'lorem-ipsum'
}
複製代碼
template:
<template>
<p>
終身學習者,終身學習者,終身學習者,終身學習者,終身學習者
</p>
</template>
複製代碼
如今,有了可用的子組件,我們所須要作的就是用panel
組件將它們封裝到應用程序中,以下所示:
<div class="vue-app">
<panel :title="'Car Manufacturers'">
<cars :data="['Mazda', 'Ford', 'Mercedes']"></cars>
</panel>
<panel :title="'Lorem Ipsum'">
<lorem-ipsum></lorem-ipsum>
</panel>
</div>
複製代碼
請注意,使用這些組件是由於示例比較簡單。在實際應用中,它能夠是任何類型的組件。
hmtl
<div class="vue-app">
<panel :title="'Car Manufacturers'">
<cars :data="['Mazda', 'Ford', 'Mercedes']"></cars>
</panel>
<panel :title="'Lorem Ipsum'">
<lorem-ipsum></lorem-ipsum>
</panel>
</div>
<script type="text/x-template" id="cars">
<template>
<ul>
<li v-for="car in data" :key="car">{{car}}</li>
</ul>
</template>
</script>
<script type="text/x-template" id="lorem-ipsum">
<template>
<p>前端小智, 終身學習者,終身學習者,終身學習者,終身學習者,終身學習者</p>
</template>
</script>
複製代碼
css
body {
padding: .5rem
}
* {
padding: 0;
margin:0;
box-sizing: border-box;
}
.panel {
margin-bottom: .5rem
}
.panel, .panel-header {
border: 1px solid #d3d3d3;
border-radius: 4px;
}
.panel-header, .panel-body, .panel {
padding: .5rem;
}
.panel-header {
background-color:#efefef;
color: #eeeee
}
ul {
list-style: none;
}
ul > li {
padding: .5rem .2rem
}
複製代碼
js
// the wrapper panel
const panel = {
functional: true,
name: "panel",
props: {
title: String
},
render(createElement, context) {
const slots = context.slots();
const header = createElement('header', {
attrs: { class: 'panel-header'}
}, context.props.title);
const body = createElement('main', {
attrs: { class: 'panel-body'}
}, slots.default);
return createElement('section', {
attrs: { class: 'panel' }
}, [header, body]);
}
}
// sample components
const cars = {
name: 'cars',
template: '#cars',
props: {
data: Array
}
}
const loremIpsum = {
name: 'lorem-ipsum',
template: '#lorem-ipsum'
}
new Vue({
el: '.vue-app',
components: {
panel,
cars,
'lorem-ipsum': loremIpsum
}
});
複製代碼
運行效果:
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
由於篇幅的限制,今天的分享只到這裏。若是你們想了解更多的內容的話,能夠去掃一掃每篇文章最下面的二維碼,而後關注我們的微信公衆號,瞭解更多的資訊和有價值的內容。
每次整理文章,通常都到2點才睡覺,一週4次左右,挺苦的,還望支持,給點鼓勵