vue 点击传值_从零单排vue第八课--基础组件(二)

本文详细介绍了在Vue中如何通过事件和属性实现子组件与父组件之间的通信。通过一个放大博文字号的功能示例,讲解了如何定义属性、创建组件、使用$emit触发事件以及在父组件中接收并处理这些事件,以此实现组件间的交互。重点在于理解$emit的使用以及如何处理组件通信。
摘要由CSDN通过智能技术生成

394b90cdd915e6409ff2273c23057c53.png

前情回顾

上一节课,我们使用了组件化思维重构blog-post组件,这节课,我们通过一个小功能,继续加强blog-post组件,并通过这个过程,讲一讲子组件是如何与父组件传值的。

单个根元素

当构建博客组件<blog-post>时,肯定不止一个标题属性。至少,要包含正文:

1<h3>{{ title }}</h3>
2<div v-html="content"></div>

但这样写,Vue会报错,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。你可以将模板的内容包裹在一个父元素内,来修复这个问题,例如:

1<div class="blog-post">
2  <h3>{{ title }}</h3>
3  <div v-html="content"></div>
4</div>

这就是所谓的单个根元素。在Vue中每个组件必须只有一个根元素。

子组件与父组件如何传值?(难点)

在我们开发 <blog-post> 组件时,它的一些功能可能要求我们和父级组件进行沟通。例如我们可能会引入一个辅助功能来放大博文的字号,同时让页面的其它部分保持默认的字号。如何来实现这个功能呢?我们大概来想象下,首先肯定是有个按钮,点击放大字号,博文字号就会放大一点,点击一次放大一次。

第一步 定义postFontSize属性来支持放大博文字号功能

 1var app = new Vue({
 2          el: '#app',
 3          data: {
 4            posts: [
 5              { id: 1, title: 'My journey with Vue', content: "...content..." },
 6              { id: 2, title: 'Blogging with Vue', content: "...content..." },
 7              { id: 3, title: 'Why Vue is so fun', content: "...content..." }
 8            ],
 9            postFontSize: 1
10          },

posts数组是博文数据,包括了标题和内容。postFontSize可以在HTML模板中,控制所有博文字号:

1<div id="app">
2      <div :style="{ fontSize: postFontSize + 'em'}">
3        <blog-post v-for="post of posts" :key="post.id" 
4                    :post="post"
5                    @enlarge-text="handleEnlarge"
6                    >
7        </blog-post>
8      </div>
9</div>

代码解析:

  • 这里使用了v-bind绑定标签style属性,使得style属性变为动态的。
  • fontSize的值等于postFontSize+em,只要按钮点击,我们控制postFontSize的值就相当于控制了整个博文字号了。
  • blog-post里的指令后面再来分析。

第二步 创建blog-post组件

 1Vue.component('blog-post',{
 2          props: ['post'],
 3          methods: {
 4            handleBtnClick() {
 5              this.$emit('enlarge-text')
 6            }
 7          },
 8          template:`
 9                  <div class="blog-post">
10                    <h3>{{post.title}}</h3>
11                    <div v-html="post.content"></div>
12                    <button @click="handleBtnClick">
13                      放大博文标题
14                    </button>
15                  </div>
16            `
17
18        })

代码解析:

  • 先看template,我们使用了单引号来阔起html代码,这样html代码可以多行编写
  • 模板里的内容就是包含了标题和博文内容,以及一个button按钮
  • 这个button按钮触发了一个点击事件handleBtnClick
  • handleBtnClick事件里,通过调用内建的$emit方法并传入事件名称来触发一个enlarge-text事件。为什么要这样做?为什么不直接在handleBtnClick方法里写一段代码直接控制字体放大的功能呢?那是因为,子组件里没有办法直接控制父组件的数据,那怎么办呢?

子组件blog-post通过$emit触发事件的方式将值传给父组件

$emit方法是Vue内建的方法,除了传递事件名还可以将值作为第二个参数传递到父组件,现在问题来了?父组件如何接收?回头看看之前的HTML模板里的代码。

1<div id="app">
2      <div :style="{ fontSize: postFontSize + 'em'}">
3        <blog-post v-for="post of posts" :key="post.id" 
4                    :post="post"
5                    @enlarge-text="handleEnlarge"
6                    >
7        </blog-post>
8      </div>
9</div>

在blog-post中,自定义了一个enlarg-text事件,该事件的方法handleEnlarge就写在父组件中

 1        var app = new Vue({
 2          el: '#app',
 3          data: {
 4            posts: [
 5              { id: 1, title: 'My journey with Vue', content: "...content..." },
 6              { id: 2, title: 'Blogging with Vue', content: "...content..." },
 7              { id: 3, title: 'Why Vue is so fun', content: "...content..." }
 8            ],
 9            postFontSize: 1
10          },
11          methods: {
12            handleEnlarge() {
13              this.postFontSize += 0.1
14            }
15          }
16        })

hanleEnlarge的作用就是一个,改变postFontSize的值,每次点击button一次,该值就加0.1,从而控制了博文字号。

第三步 完整代码如下:

 1<!DOCTYPE html>
 2<html lang="en">
 3    <head>
 4        <meta charset="utf-8">
 5        <title>blog-post组件</title>
 6    <script src="vue.js"></script>
 7    </head>
 8    <body>
 9    <div id="app">
10      <div :style="{ fontSize: postFontSize + 'em'}">
11        <blog-post v-for="post of posts" :key="post.id" 
12                    :post="post"
13                    @enlarge-text="handleEnlarge"
14                    >
15        </blog-post>
16      </div>
17    </div>
18      <script>
19        Vue.component('blog-post',{
20          props: ['post'],
21          methods: {
22            handleBtnClick() {
23              this.$emit('enlarge-text')
24            }
25          },q
26          template:`
27                  <div class="blog-post">
28                    <h3>{{post.title}}</h3>
29                    <div v-html="post.content"></div>
30                    <button @click="handleBtnClick">
31                      放大博文标题
32                    </button>
33                  </div>
34            `
35
36        })
37        var app = new Vue({
38          el: '#app',
39          data: {
40            posts: [
41              { id: 1, title: 'My journey with Vue', content: "...content..." },
42              { id: 2, title: 'Blogging with Vue', content: "...content..." },
43              { id: 3, title: 'Why Vue is so fun', content: "...content..." }
44            ],
45            postFontSize: 1
46          },
47          methods: {
48            handleEnlarge() {
49              this.postFontSize += 0.1
50            }
51          }
52        })
53      </script>
54    </body>
55</html>

代码解析:

现在还剩下一个问题,props选项里接收了一个post是什么意思?我们可以看到,博文是有很多属性组成的,比如标题、作者、时间、内容等等,这里为了方便说明问题,只取了两个属性:标题和内容,就是posts数组里的title和content。

那么多属性,如果一个一个在html标签里通过v-bind绑定是不是很繁杂?如下代码:

1<blog-post
2  v-for="post in posts"
3  v-bind:key="post.id"
4  v-bind:title="post.title"
5  v-bind:content="post.content"
6  v-bind:publishedAt="post.publishedAt"
7  v-bind:comments="post.comments"
8></blog-post>

所以是时候重构一下这个组件了,让它变成接受一个单独的 post prop:

1<blog-post
2  v-for="post in posts"
3  v-bind:key="post.id"
4  v-bind:post="post"
5></blog-post>

所以,这就是为什么props选项里只接收了一个post的原因了。

总结

  • 这节课内容比较多,也比较难懂,需要同学们好好将代码敲出来实现功能,然后慢慢琢磨。
  • 我们从这节课的代码里可以看到Vue的优点,只要定义好数据,想想你需要实现的功能,定义好方法,做好组件之间的传值,功能自然就实现了,而不需要去操作DOM,去思考这些无谓的问题。实际上,一个页面,就是组件组成,而功能无非就是数据加上组件之间通信,再加上恰当的方法,就可以实现了。
  • 严格来说,不是叫子组件与父组件传值,应该叫子组件如何与父组件通信。子组件无法直接与父组件通信,必须通过Vue内建的$emit方法将事件传递出去,在HTML标签里,触发该事件来与父组件通信,也就是获取父组件的数据。

欢迎关注公众号从零单排vue,获取最新学习笔记及前端学习资源

16340415fe59ed70439ab6606c6612fc.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值