高階組件(HOC)是一種架構模式,在React中很是常見,但也能夠在Vue中使用。它能夠被描述爲一種在組件之間共享公共功能而不須要重複代碼的方法。HOC的目的是加強組件的功能。它容許在項目中實現可重用性和可維護性。vue
只要你向一個方法傳入組件,而後返回一個新的組件,這就是一個HOC。git
高階組件在如下方面很是有用:github
在咱們開始教程以前,須要瞭解如下幾點:web
在開始本教程以前,請確保安裝了Node和npm。vue-cli
雖然高階組件一般與React相關聯,可是爲Vue組件建立高階組件是頗有可能的。在Vue中建立高階組件的模式以下所示。npm
// hocomponent.js
import Vue from 'vue'
import ComponentExample from '@/components/ComponentExample.vue'
const HoComponent = (component) => {
return Vue.component('withSubscription', {
render(createElement) {
return createElement(component)
}
}
}
const HoComponentEnhanced = HoComponent(ComponentExample);
複製代碼
如上面的代碼塊所示,HoComponent
函數接受一個組件做爲參數,並建立一個新組件來渲染傳進來的組件。bash
在本教程中,咱們將介紹一個使用高階組件的示例。在介紹高階組件以前,咱們將瞭解在沒有高階組件的狀況下,當前的代碼庫是如何工做的,而後瞭解如何進行抽象。 codesandbox.io/embed/llvq0…微信
正如上面的CodeSandbox所示,該應用程序會顯示一個紙業公司及其淨資產的列表,以及《辦公室》(美國)中的人物及其獲獎狀況。架構
咱們得到應用程序所需的全部數據來源只有一個,那就是mockData.js
文件。app
// src/components/mockData.js
const staff = [
{
name: "Michael Scott",
id: 0,
awards: 2
},
{
name: "Toby Flenderson",
id: 1,
awards: 0
},
{
name: "Dwight K. Schrute",
id: 2,
awards: 10
},
{
name: "Jim Halpert",
id: 3,
awards: 1
},
{
name: "Andy Bernard",
id: 4,
awards: 0
},
{
name: "Phyllis Vance",
id: 5,
awards: 0
},
{
name: "Stanley Hudson",
id: 6,
awards: 0
}
];
const paperCompanies = [
{
id: 0,
name: "Staples",
net: 10000000
},
{
id: 1,
name: "Dundler Mufflin",
net: 5000000
},
{
id: 2,
name: "Michael Scott Paper Company",
net: 300000
},
{
id: 3,
name: "Prince Family Paper",
net: 30000
}
];
export default {
getStaff() {
return staff;
},
getCompanies() {
return paperCompanies;
},
increaseAward(id) {
staff[id].awards++;
},
decreaseAward(id) {
staff[id].awards--;
},
setNetWorth(id) {
paperCompanies[id].net = Math.random() * (5000000 - 50000) + 50000;
}
};
複製代碼
在上面的代碼片斷中,有幾個const
變量保存了公司和員工列表的信息。咱們也導出了一些函數,實現如下功能:
接下來,咱們看看 Staff.vue
和 Companies.vue
組件。
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in staffList" :key="i">
{{ staff.name }}: {{ staff.awards }} Salesman of the year Award 🎉
<button @click="increaseAwards(staff.id);">+</button>
<button @click="decreaseAwards(staff.id);">-</button>
</div>
</main>
</template>
<script>
import mockData from "./mockData.js";
export default {
data() {
return {
staffList: mockData.getStaff()
};
},
methods: {
increaseAwards(id) {
mockData.increaseAward(id);
this.staffList = mockData.getStaff();
},
decreaseAwards(id) {
mockData.decreaseAward(id);
this.staffList = mockData.getStaff();
}
}
};
</script>
複製代碼
在上面的代碼塊中,數據實例變量staffList
被賦值爲函數mockData.getStaff()
返回的內容。咱們也有increaseAwards
和decreaseAwards
函數,分別調用mockData.increaseAward
和 mockData.decreaseAward
。傳遞給這些函數的id
是從渲染的模板中得到的。
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in companies" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button @click="setWorth(companies.id);">Set Company Value</button>
</div>
</main>
</template>
<script>
import mockData from "./mockData.js";
export default {
data() {
return {
companies: mockData.getCompanies()
};
},
methods: {
setWorth(id) {
mockData.setNetWorth(id);
this.companies = mockData.getCompanies();
}
}
};
</script>
複製代碼
在上面的代碼塊中,數據實例變量companies
被賦值爲函數mockData.getCompanies()
的返回內容。咱們還有setWorth
函數,它經過將公司的 id
傳遞給mockData.setNetWorth
來設置一個隨機值做爲淨值。傳遞給函數的id
是從渲染的模板中得到的。
如今咱們已經看到了這兩個組件是如何工做的,咱們能夠找出它們之間的共同點,並將其抽象以下:
咱們來看看如何將上面的操做放到高階組件中,以免代碼重複並確保可重用性。
你可使用Vue-cli 繼續建立一個新的Vue項目。Vue CLI是一個用於快速開發Vue.js項目的完整系統,它確保你有一個可用的開發環境,而不須要構建配置。你可使用下面的命令安裝vue-cli
。
npm install -g @vue/cli
複製代碼
安裝完成後,你可使用下面的命令建立一個新項目,其中vue-hocomponent
是應用程序的名稱。請確保選擇默認預設,由於不須要自定義選項。
vue create vue-hocomponent
複製代碼
安裝完成後,你能夠繼續建立如下文件,而後使用上面共享的片斷內容進行編輯。
src/components
目錄下的Staff.vue
文件。src/components
目錄下的 Companies.vue
文件。src/components
目錄下的mockData.js
文件。或者,你也能夠直接 fork CodeSandbox 裏的應用跟着本教程操做。
下一步是建立一個用於抽象的高階組件文件。在src
文件夾中建立一個名爲HoComponent.js
的文件,編輯如下內容:
// src/components/HoComponent.js
import Vue from "vue";
import mockData from "./mockData.js";
const HoComponent = (component, fetchData) => {
return Vue.component("HoComponent", {
render(createElement, context) {
return createElement(component, {
props: {
returnedData: this.returnedData
}
});
},
data() {
return {
returnedData: fetchData(mockData)
};
}
});
};
export default HoComponent;
複製代碼
在上面的代碼中,Vue 和 mockData
文件中的數據被導入組件。
HoComponent
函數接受兩個參數,一個組件和fetchData
。fetchData
方法用於肯定要在表示組件中顯示什麼。這意味着不管在哪裏使用高階組件,做爲fetchData
傳遞的函數都將被用來從mockData
中獲取數據。
而後將數據實例returnedData
設置爲fetchData
的內容,而後做爲props
傳遞給在高階組件中建立的新組件。
讓咱們看看新建立的高階組件如何在應用程序中使用。咱們須要編輯Staff.vue
和Companies.vue
。
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in returnedData" :key="i">
{{ staff.name }}: {{ staff.awards }} Salesman of the year Award 🎉
<button @click="increaseAwards(staff.id);">+</button>
<button @click="decreaseAwards(staff.id);">-</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]
};
</script>
複製代碼
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in returnedData" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button @click="setWorth(companies.id);">Set Company Value</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]
};
</script>
複製代碼
正如你在上面的代碼塊中看到的,對於這兩個組件,咱們去掉了函數和數據實例變量,顯示內容所需的全部數據如今都將從這些props中得到。對於刪掉的函數,咱們將很快會講到。
在 App.vue
組件中,用如下代碼編輯script
標籤中的現有內容:
// src/App.vue
<script>
// import the Companies component
import Companies from "./components/Companies";
// import the Staff component
import Staff from "./components/Staff";
// import the higher order component
import HoComponent from "./components/HoComponent.js";
// Create a const variable which contains the Companies component wrapped in the higher order component
const CompaniesComponent = HoComponent(Companies, mockData => mockData.getCompanies()
);
// Create a const variable which contains the Staff component wrapped in the higher order component
const StaffComponent = HoComponent(Staff, mockData => mockData.getStaff());
export default {
name: "App",
components: {
CompaniesComponent,
StaffComponent
}
};
</script>
複製代碼
在上面的代碼塊中,HoComponent
用於包裝 Staff
和 Companies
組件。每一個組件做爲HoComponent
的第一個參數傳入,第二個參數是一個函數,它返回另外一個函數,指定應該從mockData
獲取什麼數據。這是咱們以前建立的高階組件(HoComponent.js)中的fetchData
函數。
若是你如今刷新應用程序,你應該仍然能夠看到來自mockData
文件的數據像往常同樣呈現。惟一的區別是,這些按鈕沒法工做,由於它們尚未綁定到任何函數。讓咱們解決這個問題。
咱們先修改Staff.vue
和 Companies.vue
這兩個文件:
// src/components/Staff.vue
<template>
<main>
<h3>Staff List</h3>
<div v-for="(staff, i) in returnedData" :key="i">
{{ staff.name }}: {{ staff.awards }} Salesman of the year Award 🎉
<button @click="$emit('click', { name: 'increaseAward', id: staff.id });">
+
</button>
<button @click="$emit('click', { name: 'decreaseAward', id: staff.id });">
-
</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]
};
</script>
複製代碼
// src/components/Companies.vue
<template>
<main>
<h3>Paper Companies</h3>
<div v-for="(companies, i) in returnedData" :key="i">
{{ companies.name }} - ${{ companies.net
}}<button
@click="$emit('click', { name: 'setNetWorth', id: companies.id });"
>
Set Company Value
</button>
</div>
</main>
</template>
<script>
export default {
props: ["returnedData"]
};
</script>
複製代碼
在上面的兩個代碼片斷中,咱們發送了事件,這些事件將在父組件App.vue
中使用。咱們發送了一個對象,它包含值、與試圖執行的操做相關聯的函數名以及被單擊元素的id
。別忘了mockData.js
文件中定義了increaseAward
, decreaseAward
和setNetWorth
函數。
接下來,咱們開始編輯父組件App.vue
,讓其對子組件發送過來的數據進行響應。App.vue
更改以下:
// src/App.vue
<template>
<div id="app">
<CompaniesComponent @click="onEventHappen" />
<StaffComponent @click="onEventHappen" />
</div>
</template>
<script>
// import the Companies component
import Companies from "./components/Companies";
// import the Staff component
import Staff from "./components/Staff";
// import the higher order component
import HoComponent from "./components/HoComponent.js";
// import the source data from mockData only to be used for event handlers
import sourceData from "./components/mockData.js";
// Create a const variable which contains the Companies component wrapped in the higher order component
const CompaniesComponent = HoComponent(Companies, mockData =>
mockData.getCompanies()
);
// Create a const variable which contains the Staff component wrapped in the higher order component
const StaffComponent = HoComponent(Staff, mockData => mockData.getStaff());
export default {
name: "App",
components: {
CompaniesComponent,
StaffComponent
},
methods: {
onEventHappen(value) {
// set the variable setFunction to the name of the function that was passed iin the value emitted from child component i.e. if value.name is 'increaseAward', setFunction is set to increaseAward()
let setFunction = sourceData[value.name];
// call the corresponding function with the id passed as an argument.
setFunction(value.id);
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
複製代碼
在上面的代碼塊中,咱們在App.vue
組件中添加了一個事件監聽器。vue的組件。每當Staff.vue
或Companies.vue
組件被點擊時,onEventHappen
方法將會被調用。
在onEventHappen
方法中,咱們將變量setFunction
設置爲從子組件發出的值中傳遞的函數名,也就是說,若是value.name
是'increaseAward',那麼setFunction
設置爲increaseAward()
。setFunction
將以id做爲參數執行。
最後,爲了將事件監聽器傳遞給封裝在高階組件中的組件,咱們須要在 HoComponent.js
文件中添加下面這行代碼。
props: {
returnedData: this.returnedData
},
on: { ...this.$listeners }
複製代碼
你如今能夠刷新應用程序,全部的按鈕均可以正常工做。
或者,您可使用vue-hoc庫來幫助建立高階組件。vue-hoc幫助你輕鬆地建立高階組件,你要作的就是傳遞基本組件、應用於HOC的一系列組件選項和在渲染階段傳遞給組件的數據屬性。
vue-hoc 可用以下命令安裝:
npm install --save vue-hoc
複製代碼
vue-hoc插件有一些例子可讓你開始建立更高階的組件,你能夠查看這裏.
在本教程中咱們瞭解到,高階組件的主要用途是加強應用程序中表現類組件的可重用性和邏輯。 另外還了解到,高階組件在如下方面有用處:
而後咱們看了一個如何在 Vue 中建立和使用高階組件的例子。例子的源碼可在GitHub上查看。
歡迎關注微信公衆號「1024譯站」