Vue基础篇--8组件基础 component

Posted xujunkai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue基础篇--8组件基础 component相关的知识,希望对你有一定的参考价值。

Vue基础篇--8组件基础 component

1.简单示例

<div id='components1'>
    <button-conter></button-conter>
</div>
<script>
    // 定义一个名为button-conter组件
    Vue.component("button-conter",{
        data:function () {
            return {
                count:0
            }
        },
        template:`<button v-on:click="count++">You clicked me {{count}}</button>`
    });
    // 创建一个根实例
    new Vue({el:"#components1"})
</script>
  • 组件是可以复用的,这样你在id=‘components1‘的作用域中再定义<button-conter></button-conter>也是可以的,并且它们彼此之间互不影响。
  • 因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

2.组件的复用

  • 你可以将组件进行任意次数的复用:
<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

注意: 当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

data必须是一个函数

  • 当我们定义这个 `组件时,你可能会发现它的data` 并不是像这样直接提供一个对象:
data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

3.组件的组织

  • 通常一个应用会以一颗嵌套的组件树的形式来组织

    例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

    为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

    Vue.component('my-component-name', {
      // ... options ...
    })

    全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

    到目前为止,关于组件注册你需要了解的就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把组件注册读完。

4.通过 Prop 向子组件传递数据

  • Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:
Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

然而在一个典型应用中,你可能在data里有一个博文数组:

<div id='components1'>
  <button-conter></button-conter>
  <blog-post v-for="post in posts"
  v-bind:title="post.title"
  v-bind:key="post.id"
  ></blog-post>
</div>
// 定义一个名为button-conter组件
Vue.component("button-conter",{
  data:function () {
      return {
          count:0,
      }
  },
  template:`<button v-on:click="count++">You clicked me {{count}}</button>`
});
Vue.component("blog-post",{
  props:['title'],
  template:`<h3>{{title}}</h3>`
})



// 创建一个根实例
new Vue({el:"#components1",
data:{
  posts:[
      {id:1,title:"My journey with Vue"},
      {id:2,title:"Blogging with Vue"},
      {id:3,title:"Why Vue is so fun"},
  ]
}
})

如上所示,你会发现我们可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

5.单个根元素

  • 当构建一个 <blog-post> 组件时,你的模板最终会包含的东西远不止一个标题:

    最最起码,你会包含这篇博文的正文:

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

然而如果你在模板中尝试这样写,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:

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

上述的这个和一些接下来的示例使用了 javascript模板字符串来让多行的模板更易读。它们在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。

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

6.监听子组件事件

  • 在我们开发<blog-post>组件时,它内部一些功能要求我们和父级组件进行沟通。比如在父组件定义字体大小,子组件中 引入一个辅助功能来放大博文的字号 ,同时页面其他字号保持默认

  • 示例:

    <div id='components1'>
        <!--在父组件中,我们添加一个postFontSize数据属性来支持这个功能-->
        <!--通过 v-on:enlarge-text="postFontSize += 0.1" 监听器,父级组件就会接收该事件并更新 postFontSize 的值-->
      <div :style="{fontSize:postFontSize + 'em'}">
          <blog-post v-for="post in posts"
          v-bind:post="post"
          v-bind:key="post.id"
          v-on:enlarge-text="postFontSize += 0.1"
          ></blog-post>
      </div>
    </div>
    Vue.component("blog-post",{
      props:['post'],
        // 每篇博文正文之前添加一个按钮来放大字号
        // 同时子组件调用内建$emit方法传入事件名称
      template:`<div class="blog-post">
          <h3>{{post.title}}</h3>
          <button v-on:click="$emit('enlarge-text')">Enlarge text</button>
          <div v-html="post.content"></div>
      </div>`
    })
    
    
    
    // 创建一个根实例
    new Vue({el:"#components1",
    data:{
      posts:[
          {id:1,title:"My journey with Vue",content:"<span>文章1</span>"},
          {id:2,title:"Blogging with Vue",content:"<span>文章2</span>"},
          {id:3,title:"Why Vue is so fun",content:"<span>文章3</span>"},
      ],
        // 定义默认字体大小
      postFontSize:1
    }
    })
    

使用事件抛出一个值

  • 例如我们想让<blog-post>组件决定它的文本要放大多少,可以通过使用$emit第二个参数提供这个值

    <!--子组件定义传入放大的大小-->
    <button v-on:click="$emit('enlarge-text', 0.1)">
      Enlarge text
    </button>

    然后当父组件监听这个事件时候,我们通过$event 访问抛出这个值:

    <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
      }
    }

在组件上使用v-model

  • 自定义事件也可以用于创建支持 v-model 的自定义输入组件。

    <input v-model="searchText">
  • 等价于

    <input
      v-bind:value="searchText"
      v-on:input="searchText = $event.target.value"
    >
  • 当用在组件上时,v-model 则会这样:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 `` 必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

写成代码之后是这样的:

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

现在 v-model 就应该可以在这个组件上完美地工作起来了:

<custom-input v-model="searchText"></custom-input>

7.通过插槽分发内容

  • 和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:
<div id="demo">
        <alert-box>
            Something bad happened
        </alert-box>
</div>
Vue.component("alert-box",{
        template:`
            <div class="demo-alert-box">
                你好啊!
            </div>
        `
    });
    new Vue({el:"#demo"})
  • 我们定义<alert-box>内的内容并没有打印出来,此时你只需要加入<slot></slot>,这样内容都会打印出来。

    Vue.component("alert-box",{
            template:`
                <div class="demo-alert-box">
                    你好啊!
                    <slot></slot>
                </div>
            `
        });
        new Vue({el:"#demo"})

8.动态组件

<div id="app">
    <button @click="change">切换</button>
    <component :is="type"></component>
</div>
Vue.component("One",{
    template:"<span>One</span>"
});
Vue.component("Two",{
    template:"<span>Two</span>"
});
new Vue({
    el:"#app",
    data:{
        type:"One"
    },
    methods:{
        change:function () {
            this.type = (this.type === "One"?"Two":"One")
        }
    }
})

9.解析 DOM 模板时的注意事项

  • 有些 HTML 元素,诸如 ,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 、 和,只能出现在其它某些特定的元素内部。

    这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

<table>
  <blog-post-row></blog-post-row>
</table>

这个自定义组件 <blog-post-row>会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is attribute 给了我们一个变通的办法:

<table>
  <tr is="blog-post-row"></tr>
</table>

参照文献:Vue.js组件基础

以上是关于Vue基础篇--8组件基础 component的主要内容,如果未能解决你的问题,请参考以下文章

vue.js基础知识篇:组件详解

Vue 开发实战基础篇 # 8:如何触发组件的更新

vue基础篇---vue组件《2》

Vue 开发实战基础篇 # 2:组件基础及组件注册

VUE2中文文档:组件基础篇

Vue 开发实战基础篇 # 3:Vue组件的核心概念:事件