【代码背景】
前端工作中经常需要用到相似的功能模块,例如对话框,大量重复单个页面的做法虽然简单粗暴但既不符合代码复用的指导原则也不利于后期维护,更重要的是浪费时间且不优雅,因此可以把大量的重复功能模块封装成一个组件,要使用的时候直接引用就可以了。
【代码目标】
实现两个页面:HomePage.vue和NavBar.vue共用一个修改用户密码的对话框ModifyPswd.vue,并实现父->子以及子->父的双向传值。
【代码实现】
ModifyPswd.vue
<template>
<!-- 对话框-用户修改密码 -->
<el-dialog title="修改密码" :visible.sync="modifyPswdDialogVisible" width="35%" @close="closeDialog">
<el-form ref="modifyPswdFormRef" :model="modifyPswdForm" :rules="modifyPswdFormRules" label-width="120px">
<el-form-item label="工号">
<el-input v-model="modifyPswdForm.user_id" disabled style="width: 280px" />
</el-form-item>
<el-form-item label="姓名" prop="user_name">
<el-input v-model="modifyPswdForm.user_name" disabled style="width: 280px" />
</el-form-item>
<el-form-item label="旧密码" prop="old_pswd">
<el-input v-model="modifyPswdForm.old_pswd" style="width: 280px" type="password" clearable />
</el-form-item>
<el-form-item label="新密码" prop="new_pswd">
<el-input v-model="modifyPswdForm.new_pswd" style="width: 280px" type="password" clearable />
</el-form-item>
<el-form-item label="再输入密码" prop="new_pswd_ag">
<el-input v-model="modifyPswdForm.new_pswd_ag" style="width: 280px" type="password" clearable />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="cancelmodifyPassword">取 消</el-button>
<el-button type="primary" @click="modifyPassword">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'Index',
props: {
isOpen: {
type: Boolean,
default () {
return false
}
}
},
data () {
return {
modifyPswdDialogVisible: false, // 用户修改密码框是否可见
modifyPswdForm: {
user_id: '',
user_name: '',
old_pswd: '',
new_pswd: '',
new_pswd_ag: ''
},
modifyPswdFormRules: { // 复杂密码验证规则
old_pswd: [
{
required: true,
validator: this.validatePass,
change: 'blur'
}
],
new_pswd: [
{
required: true,
validator: this.validatePass,
change: 'blur'
}
],
new_pswd_ag: [
{
required: true,
validator: this.validatePass2,
change: 'blur'
}
]
}
}
},
watch: {
isOpen (val) {
this.modifyPswdDialogVisible = val// 监听变更并同步到modifyPswdDialogVisible上
}
},
created () {
console.log('子组件开始加载')
console.log(this.isOpen)
this.modifyPswdForm.user_id = window.sessionStorage.getItem('user_id') // 填充工号
this.modifyPswdForm.user_name = window.sessionStorage.getItem('user_name') // 填充姓名
this.modifyPswdDialogVisible = this.isOpen
},
methods: {
// 验证复杂密码
validatePass (rule, value, callback) {
// const regEn = /(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[\W_]).{8,12}/
const regEn = /^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{8,10}$/
if (value === '') {
callback(new Error('请输入密码'))
// password 是表单上绑定的字段
} else if (!regEn.test(value)) {
callback(new Error('必须包含数字、大写字母、小写字母、特殊符号,长度在8-10位!'))
} else {
callback()
}
},
// 验证两次密码一致
validatePass2 (rule, value, callback) {
if (value === '') {
callback(new Error('请再次输入密码'))
// password 是表单上绑定的字段
} else if (value !== this.modifyPswdForm.new_pswd) {
callback(new Error('两次输入密码不一致!'))
} else {
callback()
}
},
// 确认修改
modifyPassword () {
this.$refs.modifyPswdFormRef.validate(async (valid) => {
if (valid) {
alert('调用接口修改密码!')
// 修改密码成功后
// 关闭修改对话框
this.modifyPswdDialogVisible = false
// 执行退出
await this.$store.dispatch('user/logout')
this.$router.push(`/login`)
}
})
},
// 取消修改
cancelmodifyPassword () {
this.$refs.modifyPswdFormRef.resetFields()
this.modifyPswdDialogVisible = false
},
// 通知父组件已关闭对话框
closeDialog () {
this.$emit('close')
}
}
}
</script>
HomePage.vue
<template>
<div class="dashboard-container">
<el-button type="primary" @click="modifyPswd">打开对话框</el-button>
<!--修改用户密码对话框-->
<modify-pswd :is-open="modifyPswdDialogVisible" @close="closeDialog" />
</div>
</template>
<script>
import ModifyPswd from '@/components/ModifyPswd/index'
export default {
name: 'HomePage',
components: {
'modify-pswd': ModifyPswd
},
data () {
return {
// - 对话框 -
modifyPswdDialogVisible: false // 修改密码框是否可见
}
},
created () {},
methods: {
// 修改用户密码
modifyPswd () {
this.modifyPswdDialogVisible = true // 打开修改密码框
},
// 关闭对话框
closeDialog () {
this.modifyPswdDialogVisible = false // 关闭修改密码框
}
}
}
</script>
NavBar.vue
<template>
<div class="navbar">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
<div class="right-menu">
<el-dropdown class="avatar-container">
<span>你好!{{ user_name }}<i class="el-icon-caret-bottom" /></span>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<el-dropdown-item @click.native="modifyPsd">
<span style="display:block;">修改密码</span>
</el-dropdown-item>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<!--修改用户密码对话框-->
<modify-pswd :is-open="modifyPswdDialogVisible" @close="closeDialog" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import ModifyPswd from '@/components/ModifyPswd/index'
export default {
components: {
Breadcrumb,
Hamburger,
'modify-pswd': ModifyPswd
},
data () {
return {
user_name: '',
modifyPswdDialogVisible: false // 修改密码框是否可见
}
},
computed: {
...mapGetters([
'sidebar'
])
},
created () {
this.user_name = window.sessionStorage.getItem('user_name')
},
methods: {
toggleSideBar () {
this.$store.dispatch('app/toggleSideBar')
},
// 退出
async logout () {
await this.$store.dispatch('user/logout')
this.$router.push(`/login`)
},
// 修改密码
modifyPsd () {
this.modifyPswdDialogVisible = true // 打开修改密码框
},
// 关闭对话框
closeDialog () {
this.modifyPswdDialogVisible = false // 关闭修改密码框
}
}
}
</script>
【重点说明】
1、父页面引入子组件
import ModifyPswd from '@/components/ModifyPswd/index'
2、注册子组件
components: {
'modify-pswd': ModifyPswd
},
3、使用子组件
<modify-pswd :is-open="modifyPswdDialogVisible" @close="closeDialog" />
4、父组件给子组件传值:父组件通过:is-open="modifyPswdDialogVisible" 绑定对话框是否打开,注意这里的modifyPswdDialogVisible是父组件的data值,子组件通过props来接收isOpen
props: {
isOpen: {
type: Boolean,
default () {
return false
}
}
},
然后通过赋值控制对话框的打开关闭事件
this.modifyPswdDialogVisible = this.isOpen
注意这里的modifyPswdDialogVisible是子组件的data值。
这里还有一点值得注意的是,最开始直接在子组件绑定isOpen来控制打开关闭事件
官方表示不建议这么做,因为isOpen是从父组件传过来的值,是个单向传值不允许被重写,因此这里还是改成重新赋值的方式比较好哦。
5、子组件给父组件传值:到目前为止,父组件成功通知子组件打开了对话框,但是关闭对话框以后,发现再点击按钮是打不开对话框的,因为子组件自己关闭以后并没有通知父组件他已经关闭了,父组件的modifyPswdDialogVisible从false变成true之后就一直维持这个状态了,所以在子组件的对话框关闭的时候需要通知父组件。
在子组件中的dialog添加一个@close事件
// 子组件的close事件,通知父组件调用子组件的close方法
closeDialog () {
this.$emit('close')
}
使用this.$emit通知调用父组件的“close”方法
// 父组件的close事件
closeDialog () {
this.modifyPswdDialogVisible = false // 关闭修改密码框
}
【小结】
【参考】
基于Vue如何封装组件 https://www.bilibili.com/video/BV1fZ4y1K7WC
感谢B站大佬免费分享~大部分知识都源于B站