vue通过子组件修改父组件prop的多种实现方式

目录
  • 前言
  • 常用方式
    • 1. 通过父组件on监听子组件emit事件实现修改prop
    • 2. 通过父组件sync修饰符 + 子组件emit事件实现修改prop
  • 取巧方式
    • 3.通过data实现修改prop
    • 4.通过计算属性computed实现修改prop

前言

实际工作项目开发中,很经常会有父组件先传递值给子组件,再由子组件渲染展示的场景,下面我总结一下目前工作中遇到和用过的一些方式,也算是给大家一个实现方式和思路参考,如有理解不对or有其他方法欢迎在评论区留言指导~

常用方式

推荐,遵循prop单向传递的规则,基本数据类型和引用数据类型均可。

1. 通过父组件on监听子组件emit事件实现修改prop

原理:

  • 给子组件中的input标签绑定value属性赋值prop中的值,因为是用value而不是v-model,所以修改input值的时候并不会影响到prop。
  • 然后给input绑定input事件,每次输入内容都会触发input事件,在事件里通过this.$emit(‘父要监听的事件名', 修改后的值)去将值向上传递。
  • 父组件中通过on监听子组件刚刚触发的emit事件,将事件传递过来的值更新到父组件的data中。
  • 父组件data中的值更新后,因为有通过prop传递给子组件,所以子组件也会同步更新prop值并渲染视图层。

父组件代码如下:

<template>
  <div style="background-color: skyblue;">
    <h3>通过父组件on监听子组件emit事件实现修改prop</h3>
    <div>父obj:{{ obj }}</div>
    <div>父msg:{{ msg }}</div>
    <!-- 父组件调用子组件时通过on监听子组件触发的emit事件,以接收值并更新用 -->
    <emitChild
      :obj="obj"
      :msg="msg"
      @update-obj="updateObj"
      @update-msg="updateMsg"
    />
  </div>
</template>

<script>
import emitChild from './components/emitChild'

export default {
  name: 'emitUpdate',
  components: {
    emitChild
  },
  data () {
    return {
      obj: {
        name: 'zhangsan',
        age: 18
      },
      msg: 'hello'
    }
  },
  methods: {
    // 监听子组件触发的事件并更新data中的obj
    updateObj (key, newVal) {
      this.obj[key] = newVal
    },
    // 监听子组件触发的事件并更新data中的msg
    updateMsg (newVal) {
      this.msg = newVal
    }
  }
}
</script>

子组件代码如下:

<template>
  <div style="background-color: pink;">
    <div>
      <span>修改name:</span>
      <!-- 这里绑定值用value,因为input在这主要用作展示prop数据,实际修改不在子组件,子组件只是作为修改触发源 -->
      <!-- 绑定input事件,作为触发源的入口 -->
      <input type="text" :value="obj.name" @input="updateObj($event, 'name')">
    </div>
    <div>
      <span>修改age:</span>
      <input type="text" :value="obj.age" @input="updateObj($event, 'age')">
    </div>
    <div>
      <span>修改msg:</span>
      <input type="text" :value="msg" @input="updateMsg($event.target.value)">
    </div>
  </div>
</template>

<script>
export default {
  name: 'emitUpdateChild',
  props: {
    obj: {
      type: Object,
      default: () => {}
    },
    msg: {
      type: String,
      default: ''
    }
  },
  methods: {
    // 通知父组件更新obj
    updateObj ($event, key) {
      // 接收输入的值,和obj中对应需要更新值的属性,然后回传给父组件
      // 父组件就可以知道子组件需要更新obj中哪个属性的值
      this.$emit('update-obj', key, $event.target.value)
    },
    // 通知父组件更新msg
    updateMsg (newVal) {
      this.$emit('update-msg', newVal)
    }
  }
}
</script>

2. 通过父组件sync修饰符 + 子组件emit事件实现修改prop

原理:

  • 给子组件中的input标签绑定value属性赋值prop中的值,因为是用value而不是v-model,所以修改input值的时候并不会影响到prop。
  • 然后给input绑定input事件,每次输入内容都会触发input事件,在事件里通过this.$emit(‘父要监听的事件名', 修改后的值)去将值向上传递。
  • 父组件调用子组件传递prop时,在prop后加上.sync即可将事件传递过来的值更新到父组件的data中,因为sync其实就相当于执行了@监听子触发的事件名 = "父data中的属性名 = emit传递的值(即$event)"这一段代码。
  • 父组件data中的值更新后,因为有通过prop传递给子组件,所以子组件也会同步更新prop值并渲染视图层。

父组件代码如下:

<template>
  <div style="background-color: skyblue;">
    <h3>通过父组件sync修饰符 + 子组件emit事件实现修改prop</h3>
    <div>父obj:{{ obj }}</div>
    <div>父msg:{{ msg }}</div>
    <!-- 父组件调用子组件传递prop时,在prop后加上.sync即可 -->
    <syncChild :obj.sync="obj" :msg.sync="msg" />
    <!--
      sync其实就相当于执行了
        @监听子触发的事件名 = "父data中的属性名 = emit传递的值(即$event)"
      这一段代码
    -->
    <!-- 效果相当于下面的代码,所以父组件methods中不需要再定义修改data数据的方法 -->
    <!--
      <syncChild
        :obj="obj"
        :msg="msg"
        @update-obj="obj = $event"
        @update-msg="msg = $event"
      />
    -->
  </div>
</template>

<script>
import syncChild from './components/syncChild'

export default {
  name: 'syncUpdate',
  components: {
    syncChild
  },
  data () {
    return {
      obj: {
        name: 'zhangsan',
        age: 18
      },
      msg: 'hello'
    }
  }
}
</script>

子组件代码如下:

<template>
  <div style="background-color: pink;">
    <div>
      <span>修改name:</span>
      <!-- 这里绑定值用value,因为input在这主要用作展示prop数据,实际修改不在子组件,子组件只是作为修改触发源 -->
      <!-- 绑定input事件,作为触发源的入口 -->
      <input type="text" :value="childObj.name" @input="updateObj($event, 'name')">
    </div>
    <div>
      <span>修改age:</span>
      <input type="text" :value="childObj.age" @input="updateObj($event, 'age')">
    </div>
    <div>
      <span>修改msg:</span>
      <input type="text" :value="msg" @input="updateMsg($event.target.value)">
    </div>
  </div>
</template>

<script>
// 这里引入lodash工具库的cloneDeep深拷贝方法
// 官方文档地址 https://www.lodashjs.com/
import { cloneDeep } from 'lodash'

export default {
  name: 'emitUpdateChild',
  props: {
    obj: {
      type: Object,
      default: () => {}
    },
    msg: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      // 这里通过深拷贝将prop的obj复制了一份,主要为了:
      // 1.区分2个对象(引用类型)用的不是同一个内存地址
      // 2.保留对象中所有的值,方便子组件渲染/修改/回传用
      childObj: cloneDeep(this.obj)
    }
  },
  methods: {
    // 通知父组件更新obj
    updateObj ($event, key) {
      // 接收输入的值,和childObj中对应需要更新值的属性
      // 然后更新childOBj后,回传给父组件
      // 父组件直接把拿到的childObj更新到data的obj即可
      this.childObj[key] = $event.target.value
      this.$emit('update:obj', this.childObj)
    },
    // 通知父组件更新msg
    updateMsg (newVal) {
      this.$emit('update:msg', newVal)
    }
  }
}
</script>

取巧方式

主要针对引用数据类型,绕过了vue对于prop的检测机制,具体看项目规范是否允许这样写。

3.通过data实现修改prop

前提:只有引用数据类型可以实现

原理:

  • 将父组件传入的prop直接赋值给子组件的data,此时prop和data两边的变量指向的都是同一个内存地址,所以修改data等于修改prop。
  • vue2开始不允许直接修改prop,但此时我们表面修改的是data不是prop,因此vue不会抛出错误,相当于绕过了vue对于不允许修改prop的检测机制。

父组件代码如下:

<template>
  <div style="background-color: skyblue;">
    <h3>通过赋值到data实现修改prop</h3>
    <div>父obj:{{ obj }}</div>
    <div>父msg:{{ msg }}</div>
    <dataChild :obj="obj" :msg.sync="msg" />
  </div>
</template>

<script>
import dataChild from './components/dataChild'

export default {
  name: 'dataUpdate',
  components: {
    dataChild
  },
  data () {
    return {
      obj: {
        name: 'zhangsan',
        age: 18
      },
      msg: 'hello'
    }
  }
}
</script>

子组件代码如下:

<template>
  <div style="background-color: pink;">
    <div>
      <span>修改name:</span>
      <!-- 这里因为我们直接把prop赋值给data了,所以可以直接用v-model双向绑定修改数据 -->
      <input type="text" v-model="dataObj.name">
    </div>
    <div>
      <span>修改age:</span>
      <input type="text" v-model="dataObj.age">
    </div>
    <div>
      <span>修改msg:</span>
      <!-- 这里提供另一种修改基本数据类型的思路,可以通过watch侦听器实现 -->
      <input type="text" v-model="dataMsg">
    </div>
  </div>
</template>

<script>
export default {
  name: 'dataChild',
  props: {
    obj: {
      type: Object,
      default: () => {}
    },
    msg: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      // 引用数据类型直接赋值时是浅拷贝,只会复制内存地址,修改其中一个会影响另一个
      dataObj: this.obj,
      // 基本数据类型直接赋值时是复制值,修改其中一个不会影响另一个
      dataMsg: this.msg
    }
  },
  watch: {
    // 这里侦听data中复制过来的dataMsg,当修改时执行emit通知父组件进行更新
    dataMsg (newVal) {
      this.$emit('update:msg', newVal)
    }
  }
}
</script>

4.通过计算属性computed实现修改prop

前提:只有引用数据类型可以实现

原理:

  • 在子组件中直接通过计算属性computed监听父组件传入的prop,此时计算属性computed和prop两边的变量指向的都是同一个内存地址,所以修改计算属性computed等于修改prop。
  • vue2开始不允许直接修改prop,但此时我们表面修改的是计算属性computed不是prop,因此vue不会抛出错误,相当于绕过了vue对于不允许修改prop的检测机制。

父组件代码如下:

<template>
  <div style="background-color: skyblue;">
    <h3>通过计算属性监听实现修改prop</h3>
    <div>父obj:{{ obj }}</div>
    <div>父msg:{{ msg }}</div>
    <computedChild :obj="obj" :msg.sync="msg" />
  </div>
</template>

<script>
import computedChild from './components/computedChild'

export default {
  name: 'computedUpdate',
  components: {
    computedChild
  },
  data () {
    return {
      obj: {
        name: 'zhangsan',
        age: 18
      },
      msg: 'hello'
    }
  }
}
</script>

子组件代码如下:

<template>
  <div style="background-color: pink;">
    <div>
      <span>修改name:</span>
      <!-- 这里因为我们直接通过计算属性computed监听prop了,所以可以直接用v-model双向绑定修改数据 -->
      <input type="text" v-model="computedObj.name">
    </div>
    <div>
      <span>修改age:</span>
      <input type="text" v-model="computedObj.age">
    </div>
    <div>
      <span>修改msg:</span>
      <!-- 这里提供另一种修改基本数据类型的思路,可以通过计算属性computed的setter实现 -->
      <input type="text" v-model="computedMsg">
    </div>
  </div>
</template>

<script>
export default {
  name: 'computedChild',
  props: {
    obj: {
      type: Object,
      default: () => {}
    },
    msg: {
      type: String,
      default: ''
    }
  },
  computed: {
    computedObj () {
      // 这里直接return引用数据类型obj,此时computedObj相当于obj
      // 所以是浅拷贝,只会复制内存地址,修改其中一个会影响另一个
      return this.obj
    },
    computedMsg: {
      get () {
        // 这里prop每次更新时,都会触发计算属性的getter方法获取最新的值
        // 直接return基本数据类型时是复制值,修改其中一个不会影响另一个
        return this.msg
      },
      set (newVal) {
        // 这里利用计算属性的setter方法,监听到值修改时触发emit通知父组件更新值
        this.$emit('update:msg', newVal)
      }
    }
  }
}
</script>

到此这篇关于vue通过子组件修改父组件prop的几种实现方式的文章就介绍到这了,更多相关vue子组件修改父组件prop内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue父组件如何获取子组件中的变量

    在vue项目日常开发中,难免要把功能性组件抽离出来,这样结构就会出现父子组价,兄弟组件等,但是这样就会涉及到不同组件需要互相使用其中的值得问题. 之前有说过通过ref来让父组件操作子组件,并且传值,那么我们今天来详细看看. 案例一:点击父组件的按钮,操作子组件显示 注:可以通过获取id/class来操作,这里我就不介绍这种方法了,至于jquery的话,在vue中还是慎用. 介绍:这里通过给子组件绑定ref属性,引号命名自定义,然后父组件通过 this.$refs.名字 就可以操作子组件的元素,以

  • Vue子组件与父组件详细解析

    目录 一.父组件和子组件 二.模板分离写法 1.template标签 2.text/x-template 三.父子组件通信-父传子 1.Prop 类型 四.父子组件通信子传父 1.vm.$emit( eventName, [-args] ) 五.父子组件通信-结合双向绑定案例 1.基本模板代码 2.增加双向绑定 3.反向绑定 六.组件访问父访问子 一.父组件和子组件 我们经常分不清什么是父组件,什么是子组件.现在来简单总结下:我们将某段代码封装成一个组件,而这个组件又在另一个组件中引入,而引入该

  • Vue父组件调用子组件函数实现

    Vue父组件调用子组件的函数 父组件通过事件调用子组件的函数.例如父组件通过 点击事件 让子组件发请求. 文章中的项目已经通过脚手架去创建. DEMO: Father.js <template> <div> <div> <son ref="son"></son> <input type="button" value="点击" @click="useSonFun"

  • 详解vue中在父组件点击按钮触发子组件的事件

    我把这个实例分为几个步骤解读: 1.父组件的button元素绑定click事件,该事件指向notify方法 2.给子组件注册一个ref="child" 3.父组件的notify的方法在处理时,使用了$refs.child把事件传递给子组件的parentMsg方法,同时携带着父组件中的参数msg  4.子组件接收到父组件的事件后,调用了parentMsg方法,把接收到的msg放到message数组中 父组件 <template> <div id="app&qu

  • Vue子组件调用父组件方法案例详解

    一.直接在子组件中通过this.$parent.event来调用父组件的方法 <!-- 父组件 --> <template> <div> <child></child> </div> </template> <script> import child from '~/components/dam/child'; export default { components: { child }, methods: {

  • Vue父组件监听子组件生命周期

    比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现: // Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); } 以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示: //

  • vue 子组件和父组件传值的示例

    一.子组件向父组件传值 1:子组件中(例:) 2:父组件中(例:) 二.父组件向子组件传值 1:父组件中(例:) 2:子组件中(例:) 以上就是vue 子组件和父组件传值的示例的详细内容,更多关于vue 子组件和父组件传值的资料请关注我们其它相关文章!

  • Vue中父组件向子组件传递数据的几种方法

    最近在学习vue的源码,总结了几种vue中父子组件传递数据的方法. 1.props & event 父组件向子组件传递props数据,子组件通过触发事件向父组件回传数据,代码如下: //子组件 <template> <div @click="changeName('YYY')">{{name}}</div> </template> <script> export default{ props:['name'],//or

  • vue通过子组件修改父组件prop的多种实现方式

    目录 前言 常用方式 1. 通过父组件on监听子组件emit事件实现修改prop 2. 通过父组件sync修饰符 + 子组件emit事件实现修改prop 取巧方式 3.通过data实现修改prop 4.通过计算属性computed实现修改prop 前言 实际工作项目开发中,很经常会有父组件先传递值给子组件,再由子组件渲染展示的场景,下面我总结一下目前工作中遇到和用过的一些方式,也算是给大家一个实现方式和思路参考,如有理解不对or有其他方法欢迎在评论区留言指导- 常用方式 推荐,遵循prop单向传

  • 解决vue 子组件修改父组件传来的props值报错问题

    vue不推荐直接在子组件中修改父组件传来的props的值,会报错 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "result&

  • vue中如何让子组件修改父组件数据

    一.关于vue中watch的认识 我们要监听一个属性的的变化就使用watch一般是父组件传递给子组件的时候 •1.常见的使用场景 ... watch:{ value(val) { console.log(val); this.visible = val; } } ... •2.如果要一开始就执行 ... watch: { firstName: { handler(newName, oldName) { this.fullName = newName + '-' + this.lastName;

  • vue3.0 子组件如何修改父组件传递过来的值

    目录 子组件修改父组件传递过来的值 使用toRefs进行解决 子组件向父组件传值emit的使用注意事项 子组件的写法 父组件使用 子组件修改父组件传递过来的值 vue 的子组件 不是 不能直接更改父组件的值,需要将props 的值 给到子组件,后再修改, 否则:Unexpected mutation of “props” prop. vue3提供了一个解决:toRefs:https://v3.cn.vuejs.org/api/refs-api.html#torefs toRefs 非常有用,这样

  • vue子组件改变父组件传递的prop值通过sync实现数据双向绑定(DEMO)

    最近开始在用elementUI做一个后台管理系统项目,遇到一个问题,需求是这样,在父组件有一个按钮,点击按钮会显示弹窗(子组件),在子组件中用的是elementUI 的el-diolog弹窗组件,在关闭弹窗时(elementUI自带事件)便会报错.话不多说直接上代码. DEMO 这是父组件的代码: <template> <div> <app-refund :dialogVisible="refundVisible"></app-refund&g

  • vue 组件间的通信之子组件向父组件传值的方式

    父组件向子组件传值:父组件通过属性向下传值的方式和子组件通信: 使用步骤: 定义组件:现有自定义组件com-a.com-b,com-a是com-b的父组件: 准备获取数据:com-b要获取父组件data中的name属性: 在<com-b :name="name"></com-b> 使用v-bind 绑定name属性,红色部分为属性名称,可以随意写. 在子组件定义部分里添加选项,值是个字符串数组 props:['name'],将上边红色的属性名称写在这里: 之后就

  • vue 子组件向父组件传值方法

    子组件注册触发事件,父组件注册 触发子组件事件后的方法写在method里面 父组件这么写 <component-a v-on:child-say="listenToMyBoy"></component-a> <p>Do you like me? {{childWords}}</p> methods: { listenToMyBoy: function (somedata){ this.childWords = somedata } } 子

  • vue 父组件给子组件传值子组件给父组件传值的实例代码

    父组件如何给子组件传值 使用props 举个例子: 子组件:fromTest.vue,父组件 app.vue fromTest.vue <template> <h2>{{title}}</h2> //title必须是父组件传递的 </template> <script> export default (){ props:["title"] //可以是数组,也可以是对象 //如何对title进行校验 //props:{ // t

  • Vue子组件向父组件通信与父组件调用子组件中的方法

    子组件向父组件通信 子组件的button按钮绑定点击事件,事件方法名为sendToParent(), 该方法在子组件的methods中声明,实现功能this.$emit('cus-event',this.msg); 在父组件引入子组件,并给cus-event事件绑定doAction($event)方法,该方法中this.msg = e;console.log(e), 而msg已经在data中声明,其值为"子级消息",故最终的输出结果为: 展示父级接收到的消息:子级消息 父组件调用子组件

  • Vue.js子组件向父组件通信的方法实例代码详解

    一.场景描述: 曾经有个电商项目,其中有个"老带新"模块,而且该模块新增的入口很多,但是新增后展示效果还不一样,当时就考虑将新增的组件单独拿出来,其实就是一个子组件向父组同步数据的过程. 当然,背景不重要了,关键是看实现的方式. 二.场景展示效果 (PS:展示效果请忽略美感) 三.如何实现 注意:Vuejs架构通过vue-cli 3.X搭建的项目,版本无所谓. 1.先看下目录体系,下图子组件放在components文件夹内,模拟子组件为itemAdd.vue,父组件视图放在views文

随机推荐