最近在寫項目的一些公共組件(一些選擇器),很多個地方都需要用,所以在main.js全局聲明瞭,但發現子頁面調用還是有挺多的地方需寫。 例如,要在template實例化組件,並用ref綁定,然後在js里的methods里寫方法。 main.js 聲明全局組件 第一種方案 一開始想到的是用ref綁定組件 ...
最近在寫項目的一些公共組件(一些選擇器),很多個地方都需要用,所以在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.回車事件里觸發公共組件的方法並接收值。
通過這次學習,提升了個人的一丟丟前端知識,記錄存檔,方便以後還知道怎麼解決這個問題,也方便給別的小伙伴一些參考,
若該文章幫助到了你,請幫忙點一下贊好嗎,若有更好的方案,可以評論告訴我,讓我也學習一下。