十、组件(1)

本章概要

  • 全局注册与本地注册
  • 使用prop向子组件传递数据

组件时 Vue.js 最核心的功能,在前端应用程序中可以采用模块化的开发,实现可重用,可扩展。
组件时带有名字的可复用的实例,因此在在根组件实例中的各个选项,在组件中也一样可以使用。
通过组件系统,可以使用独立可复用的小组件构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。

10.1 全局注册与本地注册

与自定义指令类似,组件也有两种注册方式:全局注册与本地注册。全局注册组件使用应用程序实例的 component() 方法注册,该方法接受两个参数,第一个参数是组件的名字,第二个参数是一个函数对象,或者是一个选项对象。
语法形式如下:

app.component({string} name,{Function | Object} definition (optional))

本地注册是在组件实例的选项对象中使用 components 选项注册。
下面代码为一个全局注册组件的例子:

const app = Vue.createApp({});
app.component('ButtonCounter', {
  data() {
    return {
      count: 0
    }
  },
    template: `
        <button v-on:click="count++">
            You clicked me {{ count }} times.
        </button>`
});

app.mount('#app');

组件的内容通过 template 选项定义,当使用组件时,组件所在的位置将被 template 选项的内容所替换。
把组件当成自定义元素,按照元素的方式使用,元素的名称就是注册时指定的组件的名称。如下:

<div id="app">
  <ButtonCounter></ButtonCounter>
</div>

不幸的是,上述代码并不能正常工作。
这是因为 HTML 并不区分元素和属性的大小写,所以浏览器会把所有大写字符解释为小写字符,即 buttonCounter ,而注册时使用的名称是 ButtonCounter,这就导致找不到组件而出现错误。
解决办法是在 DOM 模板中采用 kebab-case 命名引用组件。如下:

<div id="app">
  <button-counter></button-counter>
</div>

只要组件注册时采用的是 PascalCase (首字母大写)命名,就可以采用 kebab-case ,命名来引用。不过,在非 DOM 模板中(如字符串模板或单文件组件内),是可以使用组件的原始名称的,即 ButtonCounter 和 button-counter 都是可以的。当然,如果想要保持名字的统一性,也可以在注册组件时,直接使用 kebab-case 命名来为组件命名。例如:

app.component('button-counter',...)

此外,由于 HTML 并不支持自闭合的自定义元素,所以在 DOM 模板中不要把 ButtonCounter 组件当做自闭合元素来使用。
当然,在非DOM 模板中没有这个限制,相反,还鼓励将没有内容的组件作为自闭合元素来使用,这可以明确该组件没有内容,由于省略了结束标签,代码也更简洁。
本例的渲染结果如下:
在这里插入图片描述

本地注册组件的代码如下:

const MyComponent = {
    data() {
      return {
      count: 0
    }
  },
  template: 
    `<button v-on:click="count++">
      You clicked me {{ count }} times.
    </button>`			
}

const app = Vue.createApp({
  components: {
    ButtonCounter: MyComponent
  }
}).mount('#app');

对于 components 选项对象,它的每个属性的名称就是自定义元素的名称,其属性值就是这个组件的选项对象。
全局注册组件可以在该应用程序中的任何组件实例的模板中使用,而局部注册组件只能在父组件的模板中使用。

10.2 使用prop向子组件传递数据

组件是当作自定义元素来使用的,元素一般是有属性的,同样,组件也可以有属性。
在使用组件时,为组件元素设置属性,但组件内部如何接收呢?
首先需要在组件内部注册一些自定义的属性,称为 prop ,这些prop 是在组件的 props 选项中定义的;之后,在使用组件时,就可以将这些 prop 的名称作为元素的属性名来使用,通过属性向组件传递数据,这些数据将作为组件实例的属性被使用。

<div id="app">
	<post-item post-title="duang duang"></post-item>
</div>
<script>
  const app = Vue.createApp({});
  app.component('PostItem',{
  // 声明props
  props: ['postTitle'],
    // postTitle就像data中定义的数据属性一样,
    // 在该组件中可以像 "this.postTitle" 这样使用
    template: '<h3>{{ postTitle }}</h3>'
  })
  app.mount('#app');
</script>

渲染结果为

<h3>duang duang</h3>

说明:
(1)在 props 选项中定义的 prop ,可以作为该组件实例的数据属性使用。
(2)props 选项中声明的每一个 prop ,在使用组件时,作为元素的自定义属性使用,属性值会被传递给组件内部的 prop 。
(3)HTML 中的属性名是不区分大小写的,浏览器在加载 HTML 页面时,会统一转换为小写字符,采用驼峰命名的 prop 名要使用其等价的 kebab-case(短横线分割命名)名称。如果在字符串模板或单文件组件内使用,则没有这个限制。
上述说明的第(3)点,通过下面的父子组件的例子,可以对字符串模板有更好的认识。

<div id="app">
  <post-list></post-list>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const app = Vue.createApp({});
  const PostItem = {
    // 声明props
    props: ['postTitle'],
    // postTitle就像data中定义的数据属性一样,
    // 在该组件中可以像 "this.postTitle" 这样使用
    template: '<h3>{{ postTitle }}</h3>'
  };
  app.component('PostList', {
    components: {
      PostItem
    },
    // 在字符串模板中可以直接使用PascalCase命名的组件名
    // 和camelCase命名的prop名
    template: '<div><PostItem postTitle="duang duang"></PostItem></div>'
  });

  app.mount('#app');
</script>

在本地注册组件时,使用了 ES6 的属性初始值的简写语法。
在字符串模板中,除了各种命名可以直接使用外,组件还可以作为自闭和元素使用。
下面稍微修改一下 PostList 组件,给它定义一个数据属性 title,然后用title 的值给子组件 PostItem 的 postTitle 属性赋值。如下:

<script>
  app.component('PostList', {
    data() {
      return {
        title: 'duang duang'
      }
    },
    components: {
      PostItem
    },
    // 在字符串模板中可以直接使用PascalCase命名的组件名
    // 和camelCase命名的prop名
    template: '<div><PostItem postTitle="title"></PostItem></div>'
  });

  app.mount('#app');
</script>

渲染结果为:

<h3>title</h3>

因为在解析的时候,title并没有作为表达式来解析,而仅仅是作为一个静态的字符串值传递给 postTitle 属性。
与普通的 HTML 元素的属性传值一样,要想接收动态值,需要使用 v-bind 指令,否则,接收的值都是静态的字符串值。
修改 PostList 组件的模板字符串,如下:

template: '<div><PostItem :postTitle="title"></PostItem></div>'

渲染结果如下:

<h3>duang duang</h3>

如果组件需要接收多个传值,那么可以定义多个 prop 。如下:

<div id="app">
  <post-list></post-list>
</div>
  <script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({});
    const PostItem = {
            props: ['author','title','content'],
            template: `
              <div>
                  <h3>{{ title }}</h3>
                  <p> 作者:{{ author }}</p>
                  <p>{{ content }}</p>
              </div>`
        };

      app.component('PostList', {
        data() {
          return {
        author: '余华',
        title: '活着',
        content: '管斤'
          }
        },
        components: {
              PostItem
          },
          // template: '<div><PostItem :post="post"/></div>'
    template:`<div> <PostItem :author="author" :title="title" :content="content"> </PostItem> </div>`
      
      });
  
  app.mount('#app');
</script>

从例子中可以看到,如果子组件定义的 prop 较多,调用时就需要写较多的属性,然后一一赋值,这很麻烦。
为此,可以使用不带参数的 v-bind 命令传入一个对象,只需要将该对象的属性和组件的 prop 一一对应即可。如下:

<div id="app">
  <post-list></post-list>
</div>
  <script src="https://unpkg.com/vue@next"></script>
<script>
    const app = Vue.createApp({});
    const PostItem = {
            props: ['post'],
            template: `
              <div>
                  <h3>{{ post.title }}</h3>
                  <p> 作者:{{ post.author }}</p>
                  <p>{{ post.content }}</p>
              </div>`
        };

      app.component('PostList', {
        data() {
          return {
        post:{
          author: '余华',
          title: '活着',
          content: '管斤'
        }
          }
        },
        components: {
              PostItem
          },
          template: '<div><PostItem :post="post"/></div>'		
      });
  
  app.mount('#app');
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小熊猫呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值