修改props

1.传统方式

Vue父传子可以使用props,子组件接收到后就可以使用了。但是如果子组件中需要对props进行修改,是不能在子组件中直接进行修改的,需要使用 $emit() 触发父组件的方法并传值过去,在父组件中接收传过来的数据然后进行修改。

父组件:

<template>
  <div class="app">
    <!-- 
      方式1:
      监控子组件是否触发changeTitle方法,触发则执行handleTitle事件
      从而实现子组件点击按钮修改父组件数据
    -->
    <info :title="title" @changeTitle="handleTitle"></info>
  </div>
</template>

<script>
import Info from './components/Info.vue'

export default {
  name: 'App',
  data() {
    return {
      title: '初始标题',
    }
  },
  methods: {
    handleTitle(title) {
      this.title = title
    },
  },
  components: {
    Info,
  },
}
</script>

子组件:

<template>
  <div class="info">
    <div class="title">{{ title }}</div>
    <div class="content"></div>
    <div class="buttons">
      <button @click="changeTitle">修改标题</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Info',
  props: {
    title: {
      type: String,
    },
  },
  methods: {
     // 触发自定义事件changeTitle并传值
     changeTitle(){
       this.$emit('changeTitle', '新的标题')
     },
  },
}
</script>

效果:

Images_1629706196610.GIF

2.代码简化

可以看到,父组件中是创建了 handleTitle 方法来进行修改的。而这是可以简化的。

父组件:

将父组件中的第8行代码(子组件标签)简单修改一下,然后删除父组件 methods 中的 changeTitle() 方法。

没错,这样写就不需要在父组件的 methods 中另写一个方法用于修改了,父组件直接一行代码搞定。

<!-- 
    方式2:
    vue内置了 @update:变量名="变量名=$event" 的方法,可以实现与方式1同样的功能
    该方法并不需要在父组件的methods中定义修改方法
    $event接收通过$emit传过来的参数,并通过赋值语句实现父组件title的修改
-->
<info :title="title" @update:title="title = $event"></info>

子组件:

子组件中 $emit() 的触发方法名修改为 update:title

changeTitle() {
    // 这里的触发方法必须是 update:变量名 的方式,这是固定写法
    this.$emit('update:title', '新的标题')
},

效果和之前一样。

3.使用.sync修饰符

你以为这就简化完成了?不不不,它还可以更简化。

子组件需要修改父组件 props 时,使用 .sync 修饰符可以不必单独在父组件中写修改方法。只需要在子组件中 $emit 触发方法 update:变量名 并传值,即可完成修改。

2. 的基础上,使用 .sync 修饰符进行改写,代码更短

父组件:

<!-- 
    方式3:
    此方式是方式2的简写形式
    使用 .sync 修饰符即可实现 @update:变量名="变量名=$event" 一样效果
-->
<info :title.sync="title"></info>

子组件:

2.2 一样,保存不变。

最后效果与前两个方式的效果是一样的,均能成功修改。

4.若props是对象呢

若传的 props 是一个对象,里面有多个值。

4.1参考方式1

那么按照 1. 的做法,直接将对象 doc 传过去,子组件接收,使用 doc.属性名 直接使用。

这当然没问题。但是这样,你就需要在父组件中为每一个属性写一个修改方法,很显然,代码量有点大。

4.2参考方式2

根据 2. 的做法,将需要用到的 doc 中的属性分别传过去,子组件分别接收。

父组件:

<template>
  <div class="app">
<!-- 
    方式4:
    与方式2一样,但是若此时传的是一个对象,若用方式2的写法,代码将会很长...
    毕竟每一个需要修改的变量都需要这样写一遍,变量越多,代码也就越长。
-->
    <info
      :title="doc.title"
      :content="doc.content"
      @update:title="doc.title = $event"
      @update:content="doc.content = $event"
    ></info>
  </div>
</template>

<script>
import Info from './components/Info.vue'

export default {
  name: 'App',
  data() {
    return {
      doc: {
        title: '初始doc标题',
        content: '初始doc内容',
      },
    }
  },
  components: {
    Info,
  },
}
</script>

子组件:

<template>
  <div class="info">
    <div class="title">{{ title }}</div>
    <div class="content">{{ content }}</div>
    <div class="buttons">
      <button @click="changeTitle">修改标题</button>
      <button @click="changeContent">修改内容</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Info',
  // 直接接收doc对象不可行
  // 那就分别接收doc对象中每一个需要用到的属性
  props: {
    title: {
      type: String,
    },
    content: {
      type: String,
    },
  },
  methods: {
     // 子组件触发默认方法 update:变量名 并传值
     changeTitle(){
       this.$emit('update:title', '新的标题')
     },
     changeContent() {
      this.$emit('update:content', '新的内容')
    },
  },
}
</script>

效果:

Images_1629709963463.GIF

很好,效果满足预期。但是,如果要传的数据很多怎么办?总不能每一项都写一遍吧。父组件就传了两个数据,代码就长这样了:

<info
    :title="doc.title"
    :content="doc.content"
    @update:title="doc.title = $event"
    @update:content="doc.content = $event"
></info>

多了还得了?

4.3参考方式3

那试试 3. 的方法,也是传 doc 对象过去,然后使用 .sync 修饰符如何?

但事实上, <info :doc.sync="doc"></info> 的写法是错误的,子组件通过 props 接收不到这个 doc 对象,会报错哒。

难道无解了?并没有,思路对了,写法错了...

绑定对象内多个属性,应使用如下语法: v-bind.sync = object

父组件:

<info v-bind.sync="doc"></info>

子组件:

4.2 一样,保持不变。

就这样,又是一行简短的代码,效果与 4.2 并无不同。

注意:

虽然父组件这里看着传的是 doc 对象,但是在子组件中可看到props接收的是 doc 对象中的指定属性,而不是直接接收 doc 对象本身。

因为该方式可以看做是将 doc 对象解构(es6语法)然后分别将解构得到的每一项属性传给了子组件。

所以子组件props接收到的不是一个 doc 对象,而是一个个通过 doc 解构后得到的属性。

分类: Vue 标签: 暂无标签

评论

全部评论 1

  1. dsapr
    dsapr
    FireFox Windows 10
    最近在学前端吗

目录