mentor: 你的组件就是这么封装的 ?前言:小编最近在实习的过程中接触了不少业务,有时候一个前端对接三个后端,天天忙
前言:小编最近在实习的过程中接触了不少业务,有时候一个前端对接三个后端,天天忙的不可开交,看着测试反馈回来的bug,也只能在汗流浃背中拼命追赶,以为过了测试这关,然后上线就完了,但是事实并非如此,导师每周开展的code-review可以说才是我的噩梦,这也正响应了这次的标题哈哈哈,小编已经被狠狠批斗过了,不过毕竟这个是一个编码思想在不断提升的过程,所以部门同事各自所在小组每次的code-review现场都可以找到小编的身影,感觉这个收获还是颇丰的,话不多说,我们直接进入正题吧...
一. 组件封装
1.1 组件封装是什么?
在前端开发中,组件封装是一种将用户界面(UI)分解为独立、可重用的部分的方法。每个组件都是一个独立的模块,负责特定的功能或视图,并且可以在应用程序的不同部分重复使用。组件封装的核心思想是将复杂的UI分解为更小、更易于管理的部分,从而提高代码的可维护性、可读性和可重用性。
1.2 组件封装的主要特点
- 独立性:每个组件都是独立的,可以独立开发、测试和维护。组件之间通过明确定义的接口(如属性、事件)进行通信。
- 可重用性:组件可以在应用程序的不同部分重复使用,减少代码重复,提高开发效率。
- 可组合性:组件可以组合在一起形成更复杂的UI。例如,一个按钮组件和一个输入框组件可以组合成一个表单组件。
- 封装性:组件封装了其内部实现细节,外部只需要知道如何使用它,而不需要关心其内部如何工作。
在前端框架中,组件封装通常通过以下方式实现:
框架 --> React
在React中,组件通常通过函数组件或类组件来定义。
// 函数组件
function Button(props) {
return <button onClick={props.onClick}>{props.label}</button>;
}
// 类组件
class Button extends React.Component {
render() {
return <button onClick={this.props.onClick}>{this.props.label}</button>;
}
}
框架 ==> Vue
在Vue中,组件通过.vue
文件来定义,包含模板、脚本和样式。
<template>
<button @click="onClick">{{ label }}</button>
</template>
<script>
export default {
props: ['label', 'onClick']
}
</script>
<style scoped>
button {
background-color: blue;
color: white;
}
</style>
1.3 组件封装的好处
- 提高代码的可维护性:将UI分解为独立的组件,使得代码更易于理解和维护。
- 提高开发效率:组件可以重复使用,减少重复代码的编写。
- 促进团队协作:团队成员可以独立开发和维护不同的组件,提高开发效率。
- 提高代码的可测试性:独立的组件更容易进行单元测试。
二. 组件封装示例
2.1 在日常开发中,有没有发现很多代码其实是在重复写的呢?如果要 小编举例说明的话,我觉得最经典的不过是新增和编辑弹框了哈哈哈,前几天小编就是在同一个业务模块中把新增和编辑弹框分开写了,在小组code-review中被狠狠diss了,让我回去好好整改一下,把这两个组件合并起来,下面我将带着大家讲解一下我的实现思路,当然有更好的思路的小伙伴可以在评论区发布您的高见噢。我首先声明一下,我下面的这两个组件的话颗粒度还是比较大的,实话实说并不算是一个纯粹的组件,而是针对性地把这新增编辑这两个组件合并在一起,因为这两个组件处理的业务需求有点多,加上时间比较紧,小编封装的这个并不是通用型的组件,而是业务针对性比较强的组件封装,继续往下看吧,小编尽量梳理清楚...
2.2 父组件(触发新增编辑的组件)
由于这个需求是Vue2项目的一个迭代需求哈,小编从事件触发点开始讲解代码,涉及到部分其他代码,我们关注重点即可
<template>
<div class="box-wrap" style="height: 100%">
<asp-smart-table
ref="aspSmartTable"
v-model="model"
:table-json="tableJson"
:size-change="sizeChange"
:current-change="currentChange"
:before-http="beforeHttp"
:after-http="afterHttp"
:before-button="beforeButton"
:before-router="beforeRouter"
:before-table-row-render="beforeTableRowRender"
:before-table-render="beforeTableRender"
:before-click-table-cell="beforeClickTableCell"
:render-table="renderTable"
@on="onbind"
>
<!-- 企业名称查询下拉选择的自定义插槽实现数据联动 -->
<template #customArea>
<div>
<el-select v-model="enterpriseName" placeholder="请选择企业">
<el-option
v-for="item in enterpriseNames"
:key="item.enterpriseId"
:value="item.enterpriseName">
</el-option>
</el-select>
</div>
</template>
</asp-smart-table>
<!-- 封装编辑新增弹框 -->
<openDialog ref="dialogOptions" @success="updateTable"></openDialog>
</div>
</template>
<script>
import { aesDecryptFn, aesEncrypt } from '@/utils/encryptUtils'
import { getEnterpriseVisiblePageList } from '@/api/channel'
import openDialog from './components/openDialog'
export default {
name: 'enterpriseList',
components: {
openDialog
},
data() {
return {
dataAdd: false,
tableJson: null,
model: {},
renderTable: {},
rowPropData: {},
enterpriseNames: [],
enterpriseName: '',
euserId: ''
}
},
async mounted() {
this.tableJson = JSON.parse(JSON.stringify(require('./json-config/provider-mgr.json')))
this.model = this.tableJson.model
this.getEnterpriseList()
},
methods: {
// 刷新表格
updateTable() {
this.$refs.aspSmartTable.asp_refreshTableList('myTable', true)
},
// 获取所属企业列表
async getEnterpriseList() {
// type 为 0 表示需要获取类型为供应商企业
const data = {
type: 0,
pageSize: 999
}
const res = await getEnterpriseVisiblePageList(data)
res && res.forEach((item) => this.enterpriseNames.push({
enterpriseName: item.enterpriseName,
enterpriseId: item.enterpriseId
}))
console.log('所属企业列表数据', this.enterpriseNames)
},
// 关闭编辑弹窗
closeDialog() {
this.editDialogVisible = false
this.updateTable()
},
// 关闭新增弹框
closeAddDialog() {
this.addDialogVisible = false
this.updateTable()
},
resetTable() {
// 映射完之后需要把企业名称置空,作为判断条件来确定用户是否选择了企业名称,否则不重置后每次都会把之前的数据上传,导致请求参数出错
this.enterpriseName = ''
this.updateTable()
},
/**
* 智能表格监听所有组件的交互事件操作:监听、捕捉事件响应
* @param item 响应组件对象属性集(类型、组件Id,控件内元数属性),columnName每个组件单元的唯一码(组件单元Id)
* @param type 事件类型(click/blur/onblur等)
* @param index 当是表格组件时,返回组件的行号
* @param model 查询区域表单数据模型
* @param tableModel 表格组件数据模型
* @param row 表格行数据
* @param multipleSelection 表格多选数据(当出现列表复选时才有,包括跨页数据,整行数据)
* @param sortProps 排序属性
* @returns {Promise<void>}
*/
async onbind({ item, type, index, model, tableModel, row, subFormSelectData, sortProps }) {
// 操作栏触发
const actions = {
// 新增
addEnterprise: () => this.$refs.dialogOptions.openDialog(),
// 编辑
edit: () => this.$refs.dialogOptions.openDialog(row),
// 重置
reset: () => this.resetTable()
}
actions[item.columnName] && actions[item.columnName](row)
},
/**
* 智能表格页面所有请求前的前置操作
* 例如:修改请求参数、修改请求方式、修改请求URL、或者请求条件不满足不给发送请求
* @param tableItem 组件对象属性集
* @param params 请求参数body,数据格式如下(字段格式不一致的需要自行转换)如下:
* {
* page:1, // 分页属性(当前页号),数字类型 (不是分页接口,没有这个字段)
* rows: 10,// 分页属性(页大小),数字类型 (不是分页接口,没有这个字段)
* ....... // 业务属性字段
* }
* @param httpMethod.url 请求地址URL
* @param httpMethod.type 请求方式,目前主要六种:'post+json', 'post+form', 'get','put+json','delete+json','patch+json'
* @param row 当组件为表格并且是表格操作列触发的请求,此参数返回表格行数据,其它返回undefined
*/
beforeHttp({ tableItem, params, httpMethod, row }) {
console.log(tableItem, params, httpMethod, row)
params.pageNum = params.page
params.pageSize = params.rows
// 如果用户根据企业名称搜索,需要将自定义插槽实现的企业名称映射为企业Id上传
if (this.enterpriseName) {
this.enterpriseNames.forEach(item => {
if (item.enterpriseName === this.enterpriseName) {
params.enterpriseId = item.enterpriseId
}
})
}
// 参数加密处理,否则查询按钮无法联动表格数据
params.name = aesEncrypt(params.name || '')
params.mobile = aesEncrypt(params.mobile || '')
delete params.page
delete params.rows
Object.keys(params).forEach((key) => {
if (!params[key]) delete params[key]
})
this.$main_tools.util.formatPlatformParams(params)
},
/**
* 智能表格页面所有请求后置操作
* 例如:请求后的数据包体需要做二次处理
* @param tableItem 组件对象属性集
* @param responseBody 响应数据body, 数据包格式(字段格式不一致的需要自行转换)如下:
* {
* status: "200", // 业务状态码,字符串类型,成功返回"200",失败返回其它数据
* message: "", // 业务提示语,字符串类型,给业务的提示语属性
* page:1, // 分页属性(当前页号),数字类型 (不是分页接口,没有这个字段)
* total: 53, // 分页属性(总记录大小),数字类型 (不是分页接口,没有这个字段)
* data: {}或者[] // 业务数据区,对象或数组类型,用于各业务逻辑处理
* }
*/
afterHttp({ tableItem, responseBody }) {
// 接口响应数据处理映射
if (tableItem.columnName === 'myTable') {
const res = responseBody.data || []
responseBody.status = 200 // 一定要200才会显示
responseBody.page = res.pageNum || 0
responseBody.total = res.total || 0
let list = []
if (res.list) {
console.log('供应商列表数据', res.list)
list = res.list.map(item => {
item.name = aesDecryptFn(item.name || '')
item.mobile = aesDecryptFn(item.mobile || '')
item.email = aesDecryptFn(item.email || '')
item.enterpriseName = item.enterpriseName || ''
item.status = item.status === 0 ? '正常' : '关闭'
return item
})
}
responseBody.data = list
} else {
const res = responseBody.data || []
responseBody.status = 200 // 一定要200才会显示
responseBody.data = res || []
}
},
/**
* 智能表格页面上的按钮的前置操作:包括不限于查询区域,表格顶部、表格操作列
* 例如:对操作按钮进行处理的数据进行预处理,或者对按钮请求进行个性胡逻辑判断等
* 注意:本函数有next()回调函数需要执行,调取next()函数才能继续后面的业务逻辑,否则操作将中止
* @param item 组件对象属性集
* @param rowObj 当组件为表格操作列中的按钮,此参数返回表格行数据,其它返回undefined
* @param next 回调函数
*/
beforeButton({ item, rowObj, next }) {
next(item, rowObj) // !!!注意!!!此行不许删除,否则无法继续!!!(该注释也不能删除)
},
/**
* 智能表格页面路由跳转的前置操作
* 注意:本函数有next()回调函数需要执行,调取next()函数才能继续后面的业务逻辑,否则操作将中止
* @param item 响应组件对象属性集
* @param row 当响应组件为表格操作列中的按钮时,此参数返回表格行数据,其它返回undefined
* @param routerObj.routerType: 路由类型
* @param routerObj.routerParamType 路由参数类型
* @param routerObj.routerUrl 路由地址或名称
* @param routerObj.routerParamValue 路由参数
* @param next 回调函数
*/
beforeRouter({ item, row, routerObj, next }) {
next(routerObj) // !!!注意!!!此行不许删除,否则无法继续!!!(该注释也不能删除)
},
/**
* 表格内容渲染之前的前置动作,
* @param tableName 当前表格名称
* @param tableData 表格当页的数据
* @param columnItem 表格当前列的信息
* @param scope 表格行信息包含属性 $index row等
* @param callback 回调事件,用于改变指定列的显示内容
* @param callback 参数说明如下
* 参数一:指定修改的表格名称 tableName
* 参数二:指定修改的列名 columnName
* 参数三:指定索引集合,整列生效则传空数组[],指定某几行生效则传索引集合[1,3] indexList
* 参数四:显示内容{ content: 可以是文本也可以是html代码片段}
* 示例:callBack('aspSmartTable', 'name', [], { content: `【附加标题】<a @click="detial(${scope.row})">${scope.row.name}</a>` })
*/
beforeTableRender({ tableName, tableData, columnItem, scope }, callBack) {},
/**
* 智能表格监听所有行绘制的前置回调响应
* @param item 组件对象属性集(类型、组件columnName,组件内元数属性),columnName是每个组件的唯一标识码
* @param tableData 表格数据模型
* @param row: 表格组件当前绘制的行数据
* @param rowClassName: 子表单组件当前行绘制class name
* @param callback: 回调api
* @param callback回调api参数: rowClassName: 子表单组件当前行绘制class name
*/
beforeTableRowRender({ item, tableData, row, rowClassName }) {
return rowClassName // !!!注意!!!此行不许删除,否则无法继续!!!(该注释也不能删除)
},
/**
* 智能表格单元格点击的前置操作
* @param item 响应组件对象属性集
* @param row 此参数返回表格行数据
* @param tableData: 表格数据模型
*/
beforeClickTableCell({ item, row, tableData }) {},
/**
* 表格页码大小发生变化时触发的前置事件
* 注意:本函数有next()回调函数需要执行,调取next()函数才能继续后面的业务逻辑,否则操作将中止
* @param tableItem 表格对象属性集
* @param pageSize 表格页码大小
* @param next 回调函数
*/
sizeChange({ tableItem, pageSize, next }) {
next(true) // 允许继续运行传true, 否则传false // !!!注意!!!此行不许删除,否则无法继续!!!(该注释也不能删除)
},
/**
* 表格当前页发生变化时触发的前置事件,包括点翻页、上一页、下一页、刷新页、重置页
* 注意:本函数有next()回调函数需要执行,调取next()函数才能继续后面的业务逻辑,否则操作将中止
* @param tableItem 表格对象属性集
* @param currentPage 当前页码号
* @param next 回调函数
*/
currentChange({ tableItem, currentPage, next }) {
next(true) // 允许继续运行传true, 否则传false // !!!注意!!!此行不许删除,否则无法继续!!!(该注释也不能删除)
}
}
}
</script>
<style lang="scss" scoped>
</style>
咦?怎么这么长?哈哈哈,这个是小编业务中的完整代码,加上小编比较懒,就全贴出来啦,其实这个是小编公司内使用的低代码平台构建的样板代码,小编加下来截取一下我们今天想要看的部分就好啦,保证不影响大家的理解
其实下图这个部分就是新增和编辑的触发点,大家有没有发现了呢,这里有个重点就是我们要怎么区分新增和编辑按钮呢?撕~,小小的脑袋有大大的疑问是不是,不过小编想了想,编辑的时候不是要传递某某字段给后端拿数据回显表单吗,但是新增不需要呀
,有没有恍然大悟的感觉嘿嘿嘿,所以思路就有了呀,我们在封装的组件当中判断当前有没有传递过来编辑接口的参数来判断就好了,在 Vue2
当中我们可以使用 $refs
2.3 子组件(被封装后的新增编辑组件)
<template>
<!-- 新增编辑弹框 -->
<div class="content">
<el-dialog :title="dialogTitle" :visible.sync="dialogPop" :show-close="false" width="400px">
<el-form :model="form" :rules="rules" ref="form" label-width="80px" class="form-container" label-position="left">
<el-form-item v-loading="loading" label="所属企业" prop="enterpriseName">
<el-select v-model="form.enterpriseName" placeholder="请选择所属企业">
<el-option v-for="(item, index) in enterpriseNames" :key="index" :label="item.enterpriseName" :value="item.enterpriseName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="联系人" prop="name">
<el-input v-model="form.name" size="small"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="mobile">
<el-input :disabled="!isAdd" v-model="form.mobile" size="small"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" size="small"></el-input>
</el-form-item>
<el-form-item label="传真" prop="fax">
<el-input v-model="form.fax" size="small"></el-input>
</el-form-item>
<el-form-item label="邮编" prop="postalCode">
<el-input v-model="form.postalCode" size="small"></el-input>
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色">
<el-option v-for="item in roleList" :key="item.roleId" :label="item.roleName" :value="item.roleName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账号状态" prop="status">
<el-select v-model="form.status" placeholder="请选择账号状态">
<el-option label="正常" :value="0"></el-option>
<el-option label="关闭" :value="2"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<div class="left">
<el-button v-if="!isAdd" @click="handleDelete('form')" type="primary" size="small">删除</el-button>
</div>
<div class="right">
<el-button @click="handleReturn('form')" size="small">取消</el-button>
<el-button type="primary" @click="submitData('form')" size="small">确定</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { aesDecryptFn, aesEncrypt } from '@/utils/encryptUtils'
import { getEnterpriseVisiblePageList } from '@/api/channel'
import { getRoleList } from '@/api/common'
import { editProviderUser, providerUserDetail, addProviderUser } from '@/api/provider-mgr'
export default {
name: 'openDialog',
data() {
return {
dialogPop: false, // 弹框显示与隐藏
loading: false,
dialogTitle: '新增', // 弹窗标题
isAdd: false,
propKey: 0,
form: {
enterpriseName: '', // 所属企业
name: '', // 联系人
mobile: '', // 联系电话
email: '', // 邮箱
fax: '', // 传真
postalCode: '', // 邮编
role: '', // 角色
status: '', // 账号状态
roleIds: [] // 角色Id
},
rules: {
enterpriseName: [
{ required: true, message: '请选择所属企业', trigger: 'blur' }
],
mobile: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ validator: this.validatePhonePass, trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮件', trigger: 'blur' },
{ validator: this.validateEmailPass, trigger: 'blur' }
],
name: [
{ required: true, message: '请输入联系人', trigger: 'blur' },
{ max: 10, message: '联系人名称长度不能超过10个字符', trigger: 'blur' }
],
fax: [
{ required: false, message: '请输入传真', trigger: 'blur' },
{ max: 20, message: '传真号码长度不能超过20个字符', trigger: 'blur' }
],
postalCode: [
{ required: false, message: '请输入邮编', trigger: 'blur' },
{ max: 10, message: '邮编长度不能超过10个字符', trigger: 'blur' }
],
role: [
{ required: true, message: '请选择角色', trigger: 'blur' }
],
status: [
{ required: true, message: '请选择账号状态', trigger: 'blur' }
]
},
roleList: [], // 角色列表数据
enterpriseNames: [] // 企业列表数据
}
},
methods: {
// 根据条件判断是新增还是编辑
openDialog(editData = {}) {
console.log('编辑弹框传递的数据', editData)
this.dialogPop = true
if (editData.euserId) {
this.loading = true
this.getEditData(editData.euserId)
this.dialogTitle = '编辑'
this.isAdd = false
} else {
this.isAdd = true
this.dialogTitle = '新增'
}
this.getRolesList()
this.getEnterpriseList()
this.loading = false
},
// 获取供应商用户数据
async getEditData(euserId) {
console.log('企业用户ID', euserId)
const res = await providerUserDetail({ euserId })
console.log('供应商用户编辑前数据', res)
// 数据解密和映射
const resData = {}
resData.email = aesDecryptFn(res.email || '')
resData.mobile = aesDecryptFn(res.mobile || '')
resData.name = aesDecryptFn(res.name || '')
resData.status = res.status
resData.role = res.roles[0].roleName
this.form = { ...res, ...resData }
},
// 电话校验
validatePhonePass(rule, value, callback) {
if (value === '') {
callback()
} else if (/^(\d{3})\*{4}(\d{4})$/.test(value) || /^1[3-9]\d{9}$/.test(value)) {
callback()
} else {
callback(new Error('请输入11位有效电话号码'))
}
},
// 邮箱校验
validateEmailPass (rule, value, callback) {
// 使用正则表达式来匹配正确的邮件格式
const emailRegex = /^[a-zA-Z0-9._%+-]+(\*{4})?@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
if (!value) {
// 如果值为空,则提示用户输入邮件
return callback(new Error('请输入邮件'))
}
if (!emailRegex.test(value)) {
// 如果值不符合邮件格式,则提示用户输入正确的邮件格式
return callback(new Error('请输入正确的邮件格式'))
}
// 如果一切正常,则回调无错误
callback()
},
// 返回
handleReturn(form) {
this.dialogPop = false
this.$refs[form].resetFields()
},
// 获取所属企业列表
async getEnterpriseList() {
// type 为 0 表示需要获取类型为供应商企业
const data = {
type: 0,
pageSize: 999
}
const res = await getEnterpriseVisiblePageList(data)
res && res.forEach((item) => this.enterpriseNames.push({
enterpriseName: item.enterpriseName,
enterpriseId: item.enterpriseId
}))
},
// 获取角色列表数据
async getRolesList() {
const data = {
domain: 'supplier',
appId: 'APP1'
}
const res = await getRoleList(data)
this.roleList = res
},
// 删除
handleDelete(form) {
this.$refs[form].validate((valid) => {
if (valid) {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const data = this.adjustFormData()
// 更新接口status传1表示删除
data.status = 1
console.log('删除供应商用户提交数据', data)
editProviderUser(data).then(res => {
this.$message({
message: '删除成功',
center: true,
type: 'success'
})
this.$emit('success')
this.$refs[form].resetFields()
this.dialogPop = false
}).catch(err => {
this.$message({
message: err.head.respDesc,
center: true,
type: 'error'
})
this.$emit('success')
this.$refs[form].resetFields()
this.dialogPop = false
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
} else {
console.log('error submit!!')
return false
}
})
},
// 提交表单
submitData(form) {
this.$refs[form].validate(async (valid) => {
if (valid) {
const data = this.adjustFormData()
console.log('供应商用户提交数据', data)
// 若是新增,需要提交的 API为 addProviderUser,反之为 editProviderUser
const request = this.isAdd ? addProviderUser : editProviderUser
request(data)
.then(res => {
this.$message({
message: '提交成功',
center: true,
type: 'success'
})
this.$emit('success')
this.$refs[form].resetFields()
this.dialogPop = false
})
.catch(err => {
this.$message({
message: `提交失败,${err.head.respDesc}`,
center: true,
type: 'error'
})
this.$emit('success')
this.dialogPop = false
})
} else {
console.log('error submit!!')
return false
}
})
},
adjustFormData() {
// 进行数据加密映射和提交操作
const data = { ...this.form }
data.name = aesEncrypt(data.name)
data.email = aesEncrypt(data.email)
data.mobile = aesEncrypt(data.mobile)
// 选中的供应商企业后要映射上传对应的供应商ID
this.enterpriseNames.forEach(enterprise => {
if (this.form.enterpriseName === enterprise.enterpriseName) {
data.enterpriseId = enterprise.enterpriseId
}
})
// 重新选中的角色要映射上传对应的角色ID
this.roleList.forEach(role => {
if (this.form.role === role.roleName) {
data.roleIds = [String(role.roleId)]
}
})
delete data.enterpriseName // 删除不必要的上传字段
delete data.role
return data
}
}
}
</script>
<style lang="scss" scoped>
.form-container {
:deep(.el-form-item) {
margin-bottom: 20px;
}
}
.dialog-footer {
width: -webkit-fill-available;
display: flex;
justify-content: space-between;
}
.delete-confirm {
:deep(.el-dialog__title) {
font-size: 25px;
}
:deep(.el-dialog__body) {
display: flex;
justify-content: center;
align-items: center;
font-size: 25px;
}
:deep(.dialog-footer) {
width: auto;
}
}
</style>
还是很长是不是哈哈哈,其实是这个需求是这么设计的,小编也只能按部就班地写啦,其实重点就在下面这里,我们在父组件中调用了子组件的这个 openDialog
方法,传递了必要的参数进行区分,其余部分其实都是围绕这个条件展开来动态显示新增和编辑的内容
小编其实还在 openDialog
方法中增加了一个 isAdd
变量来区分是不是新增组件,后续的逻辑判断就更加清晰了有没有感觉到呢,单纯对于这个组件来说,内部的扩展性还是挺高的,当然它也成为不了通用性的组件哈,这里面的很多代码其实都是可以抽离出来的,不过先对于小编原先的代码(新增编辑分开写,各自用父组件的 props 来控制组件的显隐,子组件通过 watch 侦听传递过来的 props ,还要使用 emit 触发回调进行关闭等等...
有没有感觉很晕了呢,也难怪我被导师狠狠批斗哈哈哈哈哈哈哈),对比之下,之前的写法确实是很俗了
结语,小编的知识点就总结到这里啦,针对这个Vue2老项目有什么更好的思路或者对于组件封装有什么更好的通用性思路的话非常欢迎在评论区分享哦,小编写的不好的地方也可以狠狠diss吼吼吼...感谢观看!!!
转载自:https://juejin.cn/post/7413222778855424034