一个页面多次复用相同组件

288 阅读3分钟

一个页面多次复用相同组件


要求

  1. 表格中每行都需要一个,即一个页面多次复用
  2. 多个相同组件之间数据不能乱串
  3. 对多个组件进行操作,多选

问题

  1. 表格换页或者发请求,更换表格数据时,可能会造成混乱
  2. 组件内的值是否需要响应式,组件内是否都需要重置
  3. 对某一组件操作时,会影响到其他组件的内容

实现

1. 组件

  1. 组件列是封装的组件,简单的el-descriptions,蓝色+控制内部infoValue++

    • id是通过props的传值,只要都传都接收,不会混乱
    • infoValue noneValue 内部响应式变量,有混乱的可能
    <div class="container">
        <el-descriptions :column="4" size="small"  direction="vertical" border >
            <!--id是通过props的传值-->
            <el-descriptions-item label="id">{{
                props.id
                }}</el-descriptions-item>
             <!--infoValue noneValue 是经计算的值 -->
            <el-descriptions-item label="infoValue">{{
                infoValue
                }}</el-descriptions-item>
            <el-descriptions-item label="noneValue">{{
                noneValue
                }}</el-descriptions-item>
            <el-descriptions-item label="加">
                <el-button type="primary" @click="plus" icon="plus"></el-button>
            </el-descriptions-item>
        </el-descriptions>
    </div>
    
    const plus = () => {
    	infoValue.value = infoValue.value + 1;
    };
    defineExpose({plus, noneValue, infoValue})
    
  2. 组件初始值

    const props = defineProps(["row", "id"]);
    const infoValue = ref(0);
    const personValue = ref();
    const noneValue = ref(-1); 
    
    watch(
    	() => props.row,
    	(newvalue, oldvalue) => {
    		noneValue.value = 0 // 注意点二:要重置noneValue
    		infoValue.value = newvalue.initValue;
    		personValue.value = newvalue.list
    		if(personValue.value.length > 2){
    			noneValue.value += personValue.value.length
    		}
    	},
    	{ immediate: true, deep: true } // 注意点一:对象里面的属性值发生改变时,深度监听
    );
    

1. 表格

image-20240412143217052.png
  1. 姓名列、组件列、操作列(红色+通过组件暴露的函数控制组件内infoValue++)

    <el-table
              :data="personList"
              ref="tableRef"
              table-layout="auto"
              @selection-change="handleSelectionChange"
              v-if="personList" 
              >
        <el-table-column type="selection" width="55" />
        <el-table-column label="姓名" prop="name"> </el-table-column>
        <el-table-column label="组件">
            <template #default="scope">
                <!--注意点三:key 属性 唯一标识组件-->
                <!--注意点四:多个相同组件的ref-->
                <InfoLine
                          :row="scope.row"
                          :id="scope.row.id"
                          :key="scope.row.id" 
                          :ref="
                                (el) => {
                                slideRefs[scope.row.id] = el;
                                }
                                "
                          />
            </template>
        </el-table-column>
    
        <el-table-column label="操作">
            <template #default="scope">
                <el-button
                           type="danger"
                           icon="plus"
                           @click="plusRef(scope.row.id)"
                           ></el-button>
            </template>
        </el-table-column>
    </el-table>
    
    import InfoLine from "./info.vue";
    const slideRefs = ref([]);
    const plusRef = (id) => {
    	slideRefs.value[id].plus(); // 调用组件暴露出的方法
    };
    
  2. 表格分页:模拟分两页

    /* 表格数据 */
    const personList = ref([]);  // 若表格外还有切换的话,需要personList=[]
    const tableRef = ref();
    personList.value = [
    	{
    		id: 1,
    		name: "人员一",
    		initValue: 2,
    		list: [1],
    	},
    	{
    		id: 2,
    		name: "人员二",
    		initValue: 2,
    		list: [2, 2],
    	},
    ];
    const getTableData = (pageNum) => {
    	showdata.value = null; // 注意点五:重置数据
    
    	if (pageNum === 1) {
    		personList.value = [
    			{
    				id: 1,
    				name: "人员一",
    				initValue: 1,
    				list: [1],
    			},
    			{
    				id: 2,
    				name: "人员二",
    				initValue: 2,
    				list: [2, 2],
    			},
    		];
    	} else {
    		personList.value = [
    			{
    				id: 3,
    				name: "人员三",
    				initValue: 3,
    				list: [3, 3, 3],
    			},
    
    			{
    				id: 4,
    				name: "人员四",
    				initValue: 4,
    				list: [4, 4, 4, 4],
    			},
    		];
    	}
    };
    const pageConfig = reactive({
    	pageNum: 1,
    	pageSize: 2,
    	total: 4,
    });
    const handleCurrentChange = (pageNum) => {
    	pageConfig.pageNum = pageNum;
    	getTableData(pageNum);
    };
    
    
    <el-pagination
                   background
                   layout="prev, pager, next, total"
                   class="pagination"
                   @current-change="handleCurrentChange"
                   :page-size="pageConfig.pageSize"
                   :total="pageConfig.total"
                   :current-page="pageConfig.pageNum"
                   />
    
  3. 展示:按钮【多选数据】

    <div class="show">
        <el-button type="primary" @click="multipleShow">多选数据</el-button>
        {{ showdata }}
    </div>
    

3. 注意点

  1. 注意点一:watch深度监听对象属性值改变(初始由空变有值)

  2. 注意点二:要重置noneValue

    对如下这种情况,nonevalue没有重新设置,就会造成该值在组件间混乱。这种情况可通过key避免(注意点三)。

    if(personValue.value.length > 2){
        noneValue.value += personValue.value.length
    }
    
  3. 注意点三:设置key 属性,唯一标识组件。切记设置的key是唯一的,包括根据别的情况切换表格的情况,都要唯一。

  4. 注意点四:多个相同组件的ref,通过唯一标识索引。可通过slideRefs.value[id]获取组件,可获取组件暴露出的变量,调用方法。

    // :ref="(el) => { slideRefs[scope.row.id] = el;}"
    const slideRefs = ref([]);
    // slideRefs.value[id]
    

4. 表格批量操作

  1. 可通过slideRefs获取选中的组件,从而进行数据处理或请求等。
  2. 本例通过showdata展示批量选择的表格数据。
const showdata = ref();
const multipleSelection = ref([]);
const handleSelectionChange = (val) => {
	multipleSelection.value = val;
};

const multipleShow = () => {
	try {
		// 判断是否选中数据
		if (multipleSelection.value.length === 0) {
			ElMessage.info("未选中修改数据!");
			// 显示提示
			showdata.value = "未选中数据";
		} else {
			// 取出将选中人员的数据
			let mdata = [];
			multipleSelection.value.forEach((element) => {
				mdata.push({
					id: element.id,
					name: element.name,
					noneValue: slideRefs.value[element.id].noneValue,
					infoValue: slideRefs.value[element.id].infoValue,
				});
			});
			// 显示数据
			showdata.value = mdata;
		}
	} catch (error) {}
};

5. 其他

  1. 组件内部按钮操作组件的方法,更加方便安全。父组件调用暴露方法也可实现,但如果该方法中有数据请求、对父组件的更新等复杂交互逻辑时一定要注意各个函数、请求的顺序、请求是否异步等。

  2. 完善测试,测试各种情况,包括脏数据,尽可能接近生产数据的测试,以保证代码正确。