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.回车事件里触发公共组件的方法并接收值。

通过这次学习,提升了个人的一丢丢前端知识,记录存档,方便以后还知道怎么解决这个问题,也方便给别的小伙伴一些参考,

若该文章帮助到了你,请帮忙点一下赞好吗,若有更好的方案,可以评论告诉我,让我也学习一下。

热门相关:恭喜你被逮捕了   精灵掌门人   重生之嫡女祸妃   魔神狂后   上将大叔,狼来了!