【Vue】组件进阶——优雅的表达:单个根元素,使用 events 向父组件发送消息

参考网址:
https://vue.docschina.org/v2/guide/components.html

大部分来自官网,一些是根据别人提的问题做的总结,
希望我把问题说明白了。
合起来的代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="test">
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" ></blog-post>
</div>
</body>
<script>
Vue.component('blog-post',{
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<div v-html="post.content">
</div>
</div>
`,
data(){
return{}
}
})
var test = new Vue({
el:'#test',
data: {
posts:[
{id:1,title:'做梦',content:'是的'},
{id:2,title:'做梦',content:'是的'},
{id:3,title:'做梦',content:'是的'},
]
}
})
</script>
</html>

在这里插入图片描述

单个根元素

根元素也就是说在网页上作为根的元素,
一般<html></html>这样的就是根元素。

在创建 <blog-post> 组件时,最终的模板中,不仅要包含标题:

<h3>{{ title }}</h3>

至少,还需要包含文章的内容:

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

如果你试图在 template 模板中按照以上方式书写,Vue 将会显示一个错误,并解释为 every component must have a single root element(译注:每个组件都必须有一个根元素)。你可以通过为以上模板包裹一个父元素,来修复这个错误,例如:

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

随着组件的扩充,我们的文章内容不只会有标题和内容,还会加入发布日期、评论和其他。为每个相关信息,都去定义一个对应的 prop,这会变得非常繁琐:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
></blog-post>

或许是时候重构 <blog-post> 组件了,我们现在只接收一个 post prop:
显示的时候用一个v-for循环来引用每个post项的id(同时也是键值)和内容——每个post对象,post具有若干属性

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>

Vue的组件定义:

Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})

上面的示例,还有接下来的一些示例,都用到了 JavaScript 的 模板字面量(template literal),以便多行模板更加具备可读性。Internet Explorer (IE) 不支持此语法,因此如果你必须支持 IE,而又不想转译代码(例如,使用 Babel 或 TypeScript 进行转译),使用 新行转义(newline escapes) 替代模板字面量语法。

现在,不论何时为 post 对象添加一个新的属性,它都会自动地在 <blog-post> 内可用。
例如:

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: '我的 Vue 旅程' , content: '内容一' },
      { id: 2, title: '用 Vue 写博客' , content: '内容2' },
      { id: 3, title: 'Vue 如此有趣' , content: '内容三' }
    ]
  }
})

你还可以继续在content后面给post对象加别的属性。这样就不需要一对一地去v-bind你需要的属性了,而可以直接在template里面引用post.xxxxx,如下:

 template: `
        <div class="blog-post">
          <h3>{{ post.title }}</h3>
          <div v-html="post.content"></div>
        </div>
      `

使用 events 向父组件发送消息

在我们开发 <blog-post> 组件时,有些功能可能恰好与 props 相反,需要子组件反过来和父组件进行通信。例如,我们可能会决定添加能放大文章文本字号的辅助功能,而将页面的其余部分保留为默认大小:

在父组件中,我们可以通过在 data 中添加一个 postFontSize 属性来支持这种功能:

new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* .省略.. */],
    postFontSize: 1
  }
})

以便控制博客组件 template 模板中,所有博客文章的字号大小:

<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    ></blog-post>
  </div>
</div>

现在,我们在每篇文章的内容前面添加一个可以加大文本字号的按钮:

Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button>
        放大文本
      </button>
      <div v-html="post.content"></div>
    </div>
  `
})

现在的问题是,这里的 button 无法实现这个功能:

<button>
  放大文本
</button>

当我们点击 button 时,我们需要和父组件通信,告知它加大所有文章的文本字号。幸运的是,Vue 实例为我们提供了一个自定义事件(custom event)系统,来解决这个问题。想要向父组件发送事件,我们可以调用实例中内置的 $emit 方法,传递事件名称enlarge-text:

<button v-on:click="$emit('enlarge-text')">
  放大文本
</button>

然后在我们的博客文章组件上,我们可以通过 v-on 监听这个事件enlarge-text,就如同我们使用原生 DOM 事件一样:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

在 event 事件中发送一个值

有时,想在 event 事件中发送一个特定的值。例如,我们可能想要在 <blog-post> 组件自身内部,去控制放大文本字号的间隔。在这种情况下,我们可以使用 $emit 的第二个参数来提供字号间隔值:

<button v-on:click="$emit('enlarge-text', 0.1)">
  放大文本
</button>

然后,当我们在父实例中监听这个事件时,我们可以通过 $event 来访问这次发送事件的值,不仅传送增加字体大小的控制enlarge-text,还要传送字号间隔值0.1:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += $event"
></blog-post>

或者,如果事件处理函数是一个方法:

<blog-post
  ...
  v-on:enlarge-text="onEnlargeText"
></blog-post>

然后,这个值会被传入到方法中,作为第一个参数:

methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值