下面是详细讲解“从零开始封装自己的自定义Vue组件”的完整攻略:
1. 确定组件需求及功能
在封装自定义Vue组件之前,需要先确定需要开发哪些组件,以及组件需要实现哪些功能。对于网站中需要复用的UI元素,可以考虑封装成组件,例如轮播图、瀑布流布局等。
在确定组件需求及功能后,需要根据组件类型及功能,采用不同的基础组件。例如,若需要实现一个表单组件,可以基于Input组件来开发。
2. 创建Vue组件
创建Vue组件可以采用以下两种方式:
2.1 使用Vue.extend
使用Vue.extend可以创建一个扩展Vue构造器,然后可以基于该构造器创建组件。例如:
Vue.component('my-component', Vue.extend({
// 组件选项
}))
2.2 单文件组件
单文件组件是一个以.vue文件扩展名的文件,其中集中了模板、脚本和样式。开发人员可以直接编写Vue代码,然后通过Webpack等打包工具进行打包。例如:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'hello world'
}
}
}
</script>
<style scoped>
div {
color: red;
}
</style>
3. 编写组件逻辑
对于单文件组件,需要编写组件逻辑代码。例如,若编写的组件是表单组件,则需要编写以下逻辑:
- 接收外部传入的值,并将其绑定到组件中;
- 将组件在数据变化时触发事件,将变化的值传递给外部;
- 根据组件类型,设置默认值及校验规则等。
4. 编写组件模板及样式
对于单文件组件,需要编写组件模板和样式。例如,若编写的组件是表单组件,则需要编写以下模板及样式:
<template>
<div>
<label>{{ label }}</label>
<input v-model="currentValue" />
</div>
</template>
<style scoped>
label {
font-size: 16px;
color: #333;
}
input {
border: 1px solid #ccc;
padding: 5px;
}
</style>
5. 注册组件
在编写完组件代码后,需要将组件注册到Vue中。可以使用Vue.component方法来注册组件。例如:
Vue.component('my-component', {
// 组件选项
})
6. 使用组件
在注册完组件后,可以在Vue实例或其他组件中使用组件。例如:
<template>
<div>
<my-component v-model="formData.username" label="用户名"></my-component>
<my-component v-model="formData.password" label="密码"></my-component>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue'
export default {
components: {
'my-component': MyComponent
},
data() {
return {
formData: {
username: '',
password: ''
}
}
}
}
</script>
以上是"从零开始封装自己的自定义Vue组件"的完整攻略,下面给出两个示例说明。
示例一:实现一个带“加载中”状态的按钮组件
需求:“加载中”按钮在被点击后,文字变为“加载中”,禁用按钮,并显示loading图标。加载完成后,按钮恢复原来的状态。
实现过程:
- 定义按钮状态的数据(loading、disabled)及需要传递给父组件的事件(click);
- 根据按钮状态,修改按钮的文字、禁用状态和loading图标;
- 将按钮点击事件传递给父组件;
- 在父组件中监听按钮点击事件,根据需要执行异步操作,并根据异步操作的结果修改按钮状态。
完整代码:
<template>
<button :disabled="disabled || loading" @click="handleClick">
<span v-if="loading">加载中</span>
<span v-else>{{ text }}</span>
<i v-if="loading" class="icon-loading"></i>
</button>
</template>
<script>
export default {
props: {
text: {
type: String,
default: "提交"
}
},
data() {
return {
loading: false,
disabled: false
}
},
methods: {
handleClick() {
if (this.loading) {
return
}
this.loading = true
this.disabled = true
this.$emit('click', () => {
this.loading = false
this.disabled = false
})
}
}
}
</script>
<style scoped>
button {
border: none;
padding: 5px 10px;
background-color: #f90;
color: #fff;
cursor: pointer;
}
button[disabled] {
cursor: not-allowed;
opacity: 0.8;
}
.icon-loading {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid #fff;
vertical-align: middle;
margin-left: 5px;
animation: spin 1s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
</style>
示例二:实现一个可穿梭的文件列表组件
需求:文件列表可穿梭,支持文件的批量复制、移动和删除。
实现过程:
- 创建文件列表组件,包括文件列表、穿梭框和操作按钮;
- 利用vuex将文件列表状态存储于全局变量中;
- 通过事件处理机制,将状态变更传递到父组件中进行处理。
完整代码:
<template>
<div class="file-transfer">
<div class="file-transfer-list left">
<h3>文件列表</h3>
<ul>
<li v-for="file in filesLeft" :key="file.id">
<input type="checkbox" v-model="file.checked">
<span>{{ file.name }}</span>
</li>
</ul>
</div>
<div class="file-transfer-operate">
<button @click="transferLeftToRight">></button>
<button @click="transferRightToLeft"><</button>
</div>
<div class="file-transfer-list right">
<h3>穿梭框</h3>
<ul>
<li v-for="file in filesRight" :key="file.id">
<input type="checkbox" v-model="file.checked">
<span>{{ file.name }}</span>
</li>
</ul>
<div>
<button @click="copyFile">复制</button>
<button @click="moveFile">移动</button>
<button @click="deleteFile">删除</button>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState([
'files'
]),
filesLeft() {
return this.files.filter(item => item.status === 'left')
},
filesRight() {
return this.files.filter(item => item.status === 'right')
}
},
methods: {
transferLeftToRight() {
const checkedFiles = this.filesLeft.filter(item => item.checked)
checkedFiles.forEach(file => {
file.status = 'right'
})
},
transferRightToLeft() {
const checkedFiles = this.filesRight.filter(item => item.checked)
checkedFiles.forEach(file => {
file.status = 'left'
})
},
copyFile() {
const checkedFiles = this.filesRight.filter(item => item.checked)
// 复制文件逻辑
this.$emit('copyFile', checkedFiles)
},
moveFile() {
const checkedFiles = this.filesRight.filter(item => item.checked)
// 移动文件逻辑
this.$emit('moveFile', checkedFiles)
},
deleteFile() {
const checkedFiles = this.filesRight.filter(item => item.checked)
// 删除文件逻辑
this.$emit('deleteFile', checkedFiles)
}
}
}
</script>
<style scoped>
.file-transfer {
display: flex;
}
.file-transfer-list {
width: 300px;
padding: 20px;
}
.file-transfer-list h3 {
font-size: 18px;
margin-bottom: 10px;
}
.file-transfer-list ul {
margin: 0;
padding: 0;
list-style: none;
}
.file-transfer-list li {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.file-transfer-list li input {
margin-right: 5px;
}
.file-transfer-operate {
display: flex;
flex-direction: column;
padding: 20px;
}
.file-transfer-operate button {
margin-bottom: 10px;
}
</style>
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:从零开始封装自己的自定义Vue组件 - Python技术站