vue通过js代码实例化组件
最近在写项目的一些公共组件(一些选择器),很多个地方都需要用,所以在main.js全局声明了,但发现子页面调用还是有挺多的地方需写。
例如,要在template实例化组件,并用ref绑定,然后在js里的methods里写方法。
main.js 声明全局组件
第一种方案
一开始想到的是用ref绑定组件,业务组件实例化公共组件,并赋予ref,然后通过这个ref绑定,直接调用公共组件的方法(为了一定能触发方法),例如 this.$refs.xxx.open()
如果用户在公共组件中,选择好数据操作完成后,公共组件触发emit方法,通过回调方法的方式通知业务组件,例如 this.$emit("getTableSelect",this.selection)
以下是相关代码:
公共组件
<template> <div> <el-dialog v-loading="loading" :before-close="cancel" :close-on-click-modal="false" :element-loading-text="loadingText" :visible="dialogVisible" append-to-body title="科目选择器" width="80%" > <el-form ref="queryForm" :inline="true" :model="queryParams" label-width="68px" size="small" > <el-form-item label="名称" prop="name"> <el-input v-model="queryParams.name" clearable placeholder="请输入名称" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="queryParams.status" clearable placeholder="请选择状态" > <el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item> <el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery" >搜索 </el-button> </el-form-item> </el-form> <el-table v-loading="loading" :data="dataList" @row-dblclick="rowDblclick" @selection-change="handleSelectionChange" > <el-table-column align="center" type="selection" width="55" /> <el-table-column align="center" label="科目id" prop="subjectId" /> <el-table-column align="center" label="名称" prop="name" /> <el-table-column align="center" label="介绍" prop="intro" /> <el-table-column align="center" label="排序" prop="sort" /> <el-table-column align="center" label="状态" prop="status"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" /> </template> </el-table-column> <el-table-column align="center" label="备注" prop="remark" /> </el-table> <div slot="footer" style="text-align: center"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">关闭</el-button> </div> </el-dialog> </div> </template> <script> import { listSubject } from "@/api/as/subject"; export default { name: "TableSelect", dicts: ["sys_normal_disable"], data() { return { loading: false, loadingText: "数据正在处理中...", dialogVisible: false, // 查询参数 queryParams: { pageNum: 1, pageSize: 10, name: null, status: null, }, // 总条数 total: 0, //表格数据 dataList: [], //被选择的行 selection: [], }; }, methods: { /** * 打开窗口 */ open(name) { this.dialogVisible = true; this.queryParams.name = name; this.handleQuery(); }, /** 搜索按钮操作 */ handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, /** 查询科目列表 */ getList() { this.loading = true; listSubject(this.queryParams).then((response) => { this.dataList = response.rows; this.total = response.total; this.loading = false; }); }, //取消 cancel() { this.dialogVisible = false; this.reset(); }, //确定 submitForm(row) { if (row) { this.$emit("getTableSelect", row); } else if (this.selection.length > 0) { this.$emit("getTableSelect", this.selection); } else { this.$message.warning("请选择"); return; } this.dialogVisible = false; this.reset(); }, //重置信息 reset() { this.queryParams = { pageNum: 1, pageSize: 10, name: null, status: null, }; this.dataList = []; this.selection = []; }, //表格选择改变 handleSelectionChange(selection) { this.selection = selection; }, //双击 rowDblclick(row) { this.submitForm(row); }, }, }; </script> <style lang="scss" scoped></style>
业务组件(简化)
<template> <div class="app-container"> <el-input v-model="queryParams.name" clearable placeholder="回车搜索科目" @keyup.enter.native="handleTableSelect" /> <table-select ref="tableSelect" @getTableSelect="getTableSelect" /> </div> </template> <script> export default { name: "Chapter", data() { return { // 遮罩层 loading: true, // 查询参数 queryParams: { name: null, }, }; }, methods: { //去打开窗口选择 handleTableSelect() { this.$refs.tableSelect.open(this.queryParams.name); }, //获取选择的行 getTableSelect(row) { this.queryParams.name = row.name; }, }, }; </script>
效果图
第一种方案,虽然可以使用,但业务组件若要使用,则需要正确在4个地方写上对应的代码
即,1.实例化公共组件,并写上ref和getTableSelect的事件监听,2.输入框写上回车事件,3.回车事件里触发公共组件的方法,4.写getTableSelect事件监听的方法。
若别的小伙伴一不小心,忘记了其中一个地方(大多数都是实例化组件,或者没写getTableSelect事件监听的方法,我们程序员,ctrl+c,ctrl+v,漏拷贝一点点方法,很正常吧),则会需排查原因,相对繁琐。
后面我想了一下,再通过百度,写了第二种方案。
第二种方案
为了减少出现拷贝少问题,我把第3步和第4步合并了(3.回车事件里触发公共组件的方法,4.写getTableSelect事件监听的方法),用Promise。
以下是相关的改动
公共组件改动情况
业务组件改动情况
这样做了之后,好处就是,触发的事件和接收结果的方法在一起了,减少出错的概率,代码也更加方便理解了。第二种方案,也是我一直用的方案,
但第二种方案,依旧要写3个地方,即1.实例化公共组件,并写上ref,2.输入框写上回车事件,3.回车事件里触发公共组件的方法并接收值,
这种情况下,还是有小伙伴会出现拷贝少的问题(没有实例化公共组件),我也曾想过,怎么去除这个实例化的代码,用js代码实例化组件,但受限于个人水平不够,我也没想到什么好的方法解决这个问题,所以这个问题就一直搁置了。
直到今天(2023-9-16),我心血来潮,想看看有没有办法解决没有实例化公共组件问题,经过长时间的百度,和查看vue官方文档,终于找到了一种可以在js代码上实例化组件的方案,也就是现在的第三种方案。
第三种方案
公共组件没有任何改变
新增一个公共的js方法
import Vue from "vue";
/**
* 获取表格选择
* @param name 搜索值
*/
export function getTableSelect(name) {
//获取公共里的实例
const MyComponent = Vue.component("TableSelect");
let myComponent = new MyComponent();
//挂载实例
let jbxxModal = myComponent.$mount();
return new Promise((resolve, reject) => {
jbxxModal
.open(name)
.then((row) => {
resolve(row);
})
.catch((res) => {
reject(res);
})
.finally(() => {
//销毁实例
myComponent.$destroy();
});
});
}
main.js,新增以下代码
业务组件改动情况
通过第三种方案,别的小伙伴只需要调用公共的js方法即可,不需要实例化了组件了,大大的减少了出错的概率了,
现在只需要1.输入框写上回车事件,2.回车事件里触发公共组件的方法并接收值。
通过这次学习,提升了个人的一丢丢前端知识,记录存档,方便以后还知道怎么解决这个问题,也方便给别的小伙伴一些参考,
若该文章帮助到了你,请帮忙点一下赞好吗,若有更好的方案,可以评论告诉我,让我也学习一下。