查看: 155|回复: 0

Vue父子组件传值与provide/inject页面刷新

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
原文围绕 Vue 2 场景下的父子组件传值和列表刷新展开。示例使用 Element UI 的 Dialog、.sync 和 provide/inject。这里的“页面刷新”不是 window.location.reload,而是在新增、删除或编辑成功后重新调用列表接口,让当前列表获取最新数据。下面按传值、事件回传、ref 调用、provide/inject 刷新、跨多层组件刷新带参数来整理。

一、父组件向子组件传值:props 接收
父组件通过动态绑定把 data 传给子组件,子组件用 props 接收。示例:
  1. <!-- Parent.vue -->
  2. <template>
  3.   <Son :msg='msg'></Son>
  4. </template>
  5. <script>
  6. export default {
  7.   name: 'Parent',
  8.   data () {
  9.     return {
  10.       msg: '父组件传过来的信息'
  11.     }
  12.   }
  13. }
  14. </script>
复制代码
  1. <!-- Son.vue -->
  2. <template>
  3.   <div>父组件传递过来的数据:{{ msg }}</div>
  4. </template>
  5. <script>
  6. export default {
  7.   name: 'Son',
  8.   props: ['msg']
  9. }
  10. </script>
复制代码

注意:子组件不要直接修改 prop。原文遇到 Element Dialog 场景时出现警告:Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. 原因就是子组件修改了父组件传入的值。推荐用 computed 的 get/set 中转:get 返回 prop,set 通过 $emit 通知父组件更新。
  1. computed: {
  2.   SonValue: {
  3.     get () {
  4.       return this.msg
  5.     },
  6.     set (newValue) {
  7.       this.$emit('update:msg', newValue)
  8.     }
  9.   }
  10. }
复制代码

二、子组件向父组件传值:$emit 与 .sync
子组件通过 this.$emit('自定义事件', 值) 把数据回传父组件。父组件需要监听事件并处理。若父组件使用 .sync 修饰符,则子组件触发 update:msg 即可,父组件不必再单独写处理函数。
  1. <!-- Parent.vue -->
  2. <template>
  3.   <Son :msg.sync='msg'></Son>
  4. </template>
  5. <script>
  6. export default {
  7.   name: 'Parent',
  8.   data () {
  9.     return {
  10.       msg: '父组件传过来的信息'
  11.     }
  12.   }
  13. }
  14. </script>
复制代码
  1. <!-- Son.vue -->
  2. <template>
  3.   <div>
  4.     父组件传递过来的数据:{{ msg }}
  5.     <button @click='passValue'>传递值给父组件</button>
  6.   </div>
  7. </template>
  8. <script>
  9. export default {
  10.   name: 'Son',
  11.   props: ['msg'],
  12.   methods: {
  13.     passValue () {
  14.       this.$emit('update:msg', '这是回传给父组件的值')
  15.     }
  16.   }
  17. }
  18. </script>
复制代码

这里的 .sync 本质是 :msg 加 @update:msg 的语法糖。原文示例使用 Vue 2 写法,迁移到其他版本时要按目标版本的组件通信方式调整。

三、用 ref 调用子组件方法
ref 用在普通 DOM 上时,this.$refs.myDiv 拿到 DOM 元素;用在子组件上时,拿到子组件实例,可以调用子组件方法。原文给出的基本形式是:
  1. <div ref='myDiv'>这是一个 div 盒子</div>
复制代码
  1. this.$refs.myDiv
复制代码

子组件场景:
  1. this.$refs.组件ref名.组件方法名()
复制代码

这个能力适合父组件需要主动触发子组件动作的场景,例如让弹窗关闭、让列表组件重新取数。使用时要注意 ref 名称与模板中一致,并且目标方法要在子组件 methods 中定义。

四、provide/inject 实现无参数刷新
原文推荐的无参数刷新思路是:父组件提供获取列表的方法,子组件注入该方法;子组件在 Dialog 确认并完成新增后调用注入方法,让父组件重新请求列表。原文示例中的 provide 按可运行写法整理为函数返回对象:
  1. <!-- Parent.vue -->
  2. <template>
  3.   <div class='parent'>
  4.     <el-table :data='data'></el-table>
  5.     <Son :dialogValue.sync='dialogValue'></Son>
  6.   </div>
  7. </template>
  8. <script>
  9. import Son from './Son.vue'
  10. export default {
  11.   name: 'Parent',
  12.   components: { Son },
  13.   data () {
  14.     return {
  15.       data: [],
  16.       dialogValue: false
  17.     }
  18.   },
  19.   provide () {
  20.     return {
  21.       getListApi: this.getListApi
  22.     }
  23.   },
  24.   methods: {
  25.     async getListApi () {
  26.       const tableList = await this.$axios({
  27.         url: 'http://xxx.xxxx.xxx',
  28.         method: 'GET'
  29.       })
  30.       this.data = tableList
  31.     }
  32.   }
  33. }
  34. </script>
复制代码
  1. <!-- Son.vue -->
  2. <template>
  3.   <div class='son'>
  4.     <el-dialog
  5.       title='提示'
  6.       :visible.sync='dialogVisibleShow'
  7.       width='30%'
  8.       :before-close='handleClose'>
  9.       <span>这是一段信息</span>
  10.       <span slot='footer' class='dialog-footer'>
  11.         <el-button @click='dialogVisibleShow = false'>取 消</el-button>
  12.         <el-button type='primary' @click='insertData'>确 定</el-button>
  13.       </span>
  14.     </el-dialog>
  15.   </div>
  16. </template>
  17. <script>
  18. export default {
  19.   name: 'Son',
  20.   props: ['dialogValue'],
  21.   inject: ['getListApi'],
  22.   computed: {
  23.     dialogVisibleShow: {
  24.       get () {
  25.         return this.dialogValue
  26.       },
  27.       set (newValue) {
  28.         this.$emit('update:dialogValue', newValue)
  29.       }
  30.     }
  31.   },
  32.   methods: {
  33.     insertData () {
  34.       this.getListApi()
  35.       this.$emit('update:dialogValue', false)
  36.     },
  37.     handleClose (done) {
  38.       done()
  39.     }
  40.   }
  41. }
  42. </script>
复制代码

关键点有两个:一是子组件通过 computed 的 set 触发 update:dialogValue,不直接改 prop;二是新增成功后调用 inject 注入的 getListApi,使父组件列表重新取数。

五、跨多层组件刷新带参数:A 通知 B 刷新
原文用 A、B、C 三层组件说明复杂用法:A 是 B 的父组件,B 是 C 的父组件。B 是 A 的 Dialog,B 中某一项点击编辑后弹出 C Dialog;C 点击确定并成功插入数据后,需要重新获取 B 的列表数据。

原文给出的流程是:
1. B 中点击编辑后,通过 this.$emit('自定义事件', item) 通知 A,A 把 item 存入 data 中的变量。
2. A 使用 provide 注入一个方法,该方法内部通过 this.$refs.<ref名>.方法名(data 中存储的 item) 调用 B 的取数方法。
3. B 定义获取列表数据的方法,方法名与第 2 步调用的名称一致。
4. C 中 inject A 注入的方法;C 点击确定后调用该方法,A 再调用 B 的取数方法,B 重新获取列表。

这个方案的核心不是“刷新整个浏览器页面”,而是用 provide/inject 把刷新动作从深层组件传回目标列表组件。参数需要提前保存在 A 的 data 中,否则 C 触发刷新时无法把 B 需要的 item 带给列表请求。对于组件层级较深、又不想引入 Vuex 的场景,这是原文给出的可落地做法;如果只是父子两层,直接 $emit 或 ref 会更直观。

六、排查与适用边界
常见问题包括:props 被直接修改导致警告;$emit 的事件名与父组件监听名不一致;.sync 场景没有触发 update:xxx;ref 名称写错或调用时机早于子组件渲染;inject 注入的方法没有在父级 provide 中提供;刷新方法只关闭弹窗却没有重新请求列表。排查时先确认数据流向,再确认事件名、ref 名、provide 键名和方法名是否一一对应,最后检查接口调用是否在新增成功之后执行。

总结:Vue 父子组件通信优先用 props 下行、$emit 上行,需要双向同步时可用 .sync 的 update:事件;页面刷新优先封装列表请求方法,通过 ref 或 provide/inject 触发重新取数。原文示例基于 Vue 2 与 Element UI 的常见写法,迁移版本时应同步调整组件通信语法。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-25 16:17 , Processed in 0.027035 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部