vue3和vue2组件之间传参的不同
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。
<script setup>
中的代码会在每次组件实例被创建的时候执行。
任何在 <script setup>
声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用,使用双花括号:{{自定义的声明}}。
在 <script setup>
中要使用动态组件的时候,应该使用动态的 :is
来绑定,结合三元运算符。
效果展示:
一、defineProps父传子
理论知识:
父组件通过 :传参名=“传递的数据” 向子组件传递参数
子组件通过 defineProps<{接收的数据}>() 来接收数据
在 script setup 中,引入的组件会自动注册,所以可以直接使用,无需再通过components进行注册。
代码:
父组件:
<div class="projects">实际已维修项目</div>
<AllTable
:widthIndex="100"
:AllTableData="data.AllTableData"
:tableColumns="data.tableColumns"
:heights="data.heights"
:option="true"
@changeTbaleData="changeTbaleData"
></AllTable>
<div class="isNewOpenD">
<el-button
type="primary"
plain
class="mt-4"
style="width: 100%"
@click="handleNew"
>
维修项目新增
</el-button>
</div>
<script setup>
const changeTbaleData = (value) => {
data.AllTableData = value;
};
const data = reactive({
AllTableData:[],
})
</script>
二、defineEmits子传父
在Vue 3中,可以使用 defineEmits
函数来声明子组件可以触发的事件。该函数需要在子组件中使用,并且需要在 setup
函数中调用 。
理论知识:
父组件通过@绑定子组件注册好的事件名,在父组件中进行处理子组件传过来的value
子组件通过两点:
1.defineEmits()函数用来声明子组件可以触发的事件
语法:const 事件名 = difineEmits(['事件'])
2.在"事件"方法内部,使用注册好的事件,向父组件传参
语法:声明的事件名('事件',传递的数据)
理论知识代码:
子组件:
//封装组件:AllTable.vue
<template>
<el-table :data="props.AllTableData" :height="props.heights" style="width: 100%"
:cell-style="{ textAlign: 'center' }" :header-cell-style="{ 'text-align': 'center' }">
<el-table-column type="index" label="序号" :width="props.widthIndex" />
<template v-for="(item, index) in props.tableColumns" :key="index">
<el-table-column :prop="item.value" :label="item.name" width="" :show-overflow-tooltip="true">
</el-table-column>
</template>
<el-table-column label="操作" v-if="props.option">
<template #default="scope">
<el-popconfirm title="你确定要删除吗?" confirm-button-text="确认" cancel-button-text="取消"
@confirm="confirmOption(scope.$index)" @cancel="cancelOption(scope.$index)">
<template #reference>
<el-button link type="danger" size="small">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
const emits = defineEmits(["changeTbaleData"])
const props = defineProps({
widthIndex: {
type: Number,
default: 180
},
AllTableData: Object,
tableColumns: Object,
heights: String,
option: {
type:Boolean,
default:false
},
})
const confirmOption = (index) => {
props.AllTableData.splice(index, 1)
emits("changeTbaleData", props.AllTableData)
}
const cancelOption = (index) => {
ElMessage({
message: '取消。',
type: 'warning',
})
}
</script>
写到这儿,在vue开发中,常用的组件之间就上面两种方式。
但,当情景不止是父<——>子 之间通讯,可以考虑inject注入、defineExpose()暴露、pinia(或vuex)
三、defineExpose 获取子组件的实例和内部属性
子组件将方法、变量暴露给父组件使用,父组件才可通过 ref API拿到子组件暴露的数据。
效果展示:
参考链接:defineexpose的使用
在vue2中,通常会在子组件便签上加,
ref
来获取子组件的实例和属性方法,在 Vue3的script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,
defineExpose
可以实现1.父组件通过ref中访问子组件的方法、变量
2.子组件中,子组件的方法、变量需要通过defineExpose暴露出去
代码:
<SelectMaterial ref="selectMaterial"></SelectMaterial>
const selectMaterial = ref();
const selectSystemMaterial = () => {
//打开弹框
selectMaterial.value.Open(true);
};
弹框组件:
//弹框组件封装
<!-- >>> 打开新增弹框 -->
<MaintainTableDialog
v-model:modelValue="data.isOpenDialog" //备注:弹框打开与否
:footer="data.dialogTitle != '查看' ? data.isShowFooter : true"
:title="'维修项目新增'"
:dialogWidth="'50%'"
>
<template #default>
<el-table
ref="multipleTableRef"
:data="data.mytableData" //备注:弹框表格的数据
style="width: 100%"
height="50vh"
:row-key="bindRowKeys"
>
<el-table-column
type="selection"
width="55"
:reserve-selection="true"
></el-table-column>
<el-table-column type="index" label="序号" width="70" />
<el-table-column prop="classifyName" label="设备分类" width="170" />
<el-table-column
prop="no"
label="项目编号"
width="160"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="name"
label="项目名称"
width="190"
:show-overflow-tooltip="true"
/>
<el-table-column prop="period" label="周期" width="100" />
<el-table-column prop="manager" label="负责人" width="100" />
</el-table>
</template>
<template #footer>
<div class="footer-btn">
<el-button
type="primary"
@click="clickNewDialog"
:loading="data.isLoading"
class="submit"
>确认</el-button
>
<el-button @click="data.isOpenDialog = false">取消</el-button>
</div>
</template>
</MaintainTableDialog>
<script setup>
const Open = (isAdd) => {
data.isOpenDialog = isAdd || true;
data.mytableData = store.selectMaterilReqList;
};
//父组件中访问子组件的方法或者变量需要通过defineExpose暴露出去
defineExpose({
Open,
});
</script>
写到这儿,以上就是difineExpose()的用法 。
以下是调取接口——数据回显——选中数据传参,可选看:
代码(打开弹框调取接口——数据回显):
const handleNew = () => {
data.isOpenDialog = !data.isOpenDialog;
const params = {
shipGuid: data.ruleForm.shipGuid,
category: 2,
};
console.log(data.AllTableData)
api.getMaintainItems.post(params).then((res) => {
// data.mytableData = res.data.data;
//!!!提示:首先打开弹框把“多选”勾选的数据置空,因为在之后会赋值选中的效果
multipleTableRef.value.clearSelection();
let itemGuidLists=[]
data.AllTableData.forEach(item=>{
itemGuidLists.push(item.itemGuid)
})
console.log(itemGuidLists)
data.mytableData=res.data.data.filter(item=>{
if(itemGuidLists.indexOf(item.itemGuid)<0){
//备注:只显示未勾选的。只显示未选择的
return true
}else{
return false
}
})
// data.AllTableData.forEach((row) => {
// data.mytableData.map((item) => {
// if (row.itemGuid == item.itemGuid) {
//备注:显示所有,并勾选计划内选中的。显示:有选中和未选择的
//!!!提示:设为true,多选框选中
// multipleTableRef.value.toggleRowSelection(item, true);
// }
// });
// });
});
};
代码(选中传数据):
<el-button
type="primary"
@click="clickNewDialog"
:loading="data.isLoading"
class="submit"
>确认
</el-button>
<script setup>
const clickNewDialog = (val) => {
data.isOpenDialog = false;//关闭子弹框
const selectData = multipleTableRef.value.getSelectionRows();//获取多选,选中的项,
//比较子弹框传的 和 父列表展示的 ,不存在添加到父列表
const guidData = data.AllTableData.map((item) => {
return item.itemGuid;
});
selectData.map((item) => {
// [].indexof() == -1 不存在
if (guidData.indexOf(item.itemGuid) < 0) {
data.AllTableData.push(item);
}
});
};
</script>
四、inject注入式父子组件传参
父组件可以向子组件(无论层级)注入依赖,每个子组件都可以获得这个依赖,无论层级。
参考链接:依赖注入
代码:
//父组件
<script setup>
import { ref, provide } from 'vue'
import { fooSymbol } from './injectionSymbols'
provide('foo', 'bar')// 提供静态值
const count = ref(0)// 提供响应式的值
provide('count', count)
provide(fooSymbol, count)// 提供时将 Symbol 作为 key
provide('parentData', data.msg);// 提供默认值
const data = reactive({
msg: "我是父组件的数据",
})
</script>
//子组件
<script setup>
import { inject } from 'vue'
import { fooSymbol } from './injectionSymbols'
// 注入不含默认值的静态值
const api = inject('parentData')
// 注入响应式的值
const count = inject('count')
// 通过 Symbol 类型的 key 注入
const foo2 = inject(fooSymbol)
// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')
// 注入一个值,若为空则使用提供的函数类型的默认值
const fn = inject('function', () => {})
</script>
<template>
<template>
<p>我是子组件</p>
<p>parent组件数据:{{api.parentData}}</p>
<p>parent组件数据:{{parentData}}</p>
</template>
写到这儿,以上就是注入依赖的用法。
实际代码,可选看:
在项目里,用到最多的是:
1、封装的axios,用于发起网络请求
2、封装的echart,用于调用原生的ecahrt
在整个项目的main.js文件,
provide():提供一个值,可以被后代组件注入。
//main.js
import * as api from './api/index'
import * as echarts from 'echarts'
import App from './App.vue'
const app = createApp(App);
//演示:
app.provide('$api', api);
app.provide('$echarts', echarts);
app.mount('#app')
inject():注入一个由祖先组件或整个应用 (通过 app.provide()
) 提供的值。
//任何一个vue页面组件
<script setup>
const api = inject("$api");
//...调取接口
api.某某
const _echarts = inject("$echarts");
myCharts = _echarts.init(
myChart.value,
{
width:"100%",
height:"100%",
},
{
renderer: "svg",
}
);
window.addEventListener("resize", function () {
myCharts && myCharts.resize();
});
</script>