高阶组件(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译站”