学而时习之 不亦说乎

Vue2 组件基础

发表于:2021-09-02分类:Vue.js

创建并使用一个简单的组件

<div id="components-demo">
  <button-counter></button-counter>
</div>

<script type="text/javascript">
    //创建组件
    Vue.component('button-counter', {
        data: function() {
            return {
                //声明变量
                count: 0
            }
        },
        //模板并使用组件
        template: '<button v-on:click="count++">点击了{{ count }}次.</button>'
    })
    //创建vue实例
    new Vue({ el: '#components-demo' })
</script>

组件复用

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

组件可以复用,并且都以独立实例存在。

注意:组件里的data应该是一个函数,正是有了这个方法,各个组件之间才能独立存储数据。

组件的组织

组件分为全局注册和局部注册。

通过 Prop 向子组件传递数据

Prop 可以给组件添加自定义属性,并传递属性值

<div id="components-demo">
    <blog-post title="值" content="内容"></blog-post>
</div>

<script type="text/javascript">
    Vue.component('blog-post', {
        props: ['title','content'],
        template: '<div><h3>{{ title }}</h3><p>{{ content }}</p></div>'
    })
            
    new Vue({
        el: '#components-demo'
    })
</script>

<!-- 得到 -->
<div id="components-demo"><div><h3>值</h3><p>内容</p></div></div>


<div id="blog-post-demo">
    <!-- 实用例子:
    v-for循环posts迭代
    v-bind:key绑定key值
    v-bind:title绑定title值 -->
    <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title"></blog-post>
</div>
<script type="text/javascript">
    Vue.component('blog-post', {
        //自定义属性title
        props: ['title'],
        template: '<h3>{{ title }}</h3>'
    })

    new Vue({
        el: '#blog-post-demo',
        data: {
            //posts有多个title值
            posts: [{
                    id: 1,
                    title: 'A'
                },
                {
                    id: 2,
                    title: 'B'
                },
                {
                    id: 3,
                    title: 'C'
                }
            ]
        }
    })
</script>

单个根元素

组件模板中只能有一个根元素

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

<!-- 不可以 -->
<h3>{{ title }}</h3>
<div v-html="content"></div>

当有多个属性时显然挨个写不太优雅

<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
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>

<script>
Vue.component('blog-post', {
  props: ['post'],
    //在模板中读取post的不同属性
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})
new Vue({
    el: '#blog-post-demo',
    data: {
        posts: [{
            id:0,
            title:"A",
            content:"aa"
        }, {
            id:1,
            title:"B",
            content:"bb"
        }]
    }
})
</script>

监听子组件的事件

上例中,可以传递数据给子组件了,子组件也可以使用$emit传递事件给父组件

new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
      //添加一个postFontSize,来支持(声明以便使用)
  }
})
<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }"><!-- 绑定样式读取postFontSize给字号 -->
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
      v-on:enlarge-text="postFontSize += 0.1"
    ></blog-post>
      <!-- 创建一个enlarge-text事件可以给postFontSize+0.1,对应修改了父组件的style -->
  </div>
</div>
Vue.component('blog-post', {
  props: ['post'],
    //模板中添加一个按钮来放大字体用,使用$emit,触发父组件的enlarge-text事件
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button v-on:click="$emit('enlarge-text')">
        放大
      </button>
      <div v-html="post.content"></div>
    </div>
  `
})

$emit的第二个参数可以携带值,父组件可以使用$event访问这个值

<button v-on:click="$emit('enlarge-text', 0.1)">
  Enlarge text
</button>
<!-- $event -->
<blog-post
  v-on:enlarge-text="postFontSize += $event"
></blog-post>

如果使用的是个方法,可以作为一个参数传递

//v-on:enlarge-text="onEnlargeText"
methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}

自定义事件也可以创建支持v-model的自定义组件

<input v-model="searchText">

<!-- 我们知道上下两个例子等价 -->

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

也就是当在组件中使用时应该是

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

为了让它正常工作,我们需要prop一个value和input事件触发时,抛出值

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

现在v-model才可以使用,别忘了在vue实例的date里声明searchText。

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

插槽

组件标签之间的内容可以传递给组件渲染用,slot就是这个插槽vue的自定义元素

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})
//<slot></slot>之间就渲染组件标签之间的内容

动态组件

通过特殊的component标签和is属性实现组件动态切换

<!-- 组件会在 `name` 改变时改变 -->
<component v-bind:is="name"></component>

name可以是组件名称或组件的选项对象,通过改变name来切换不同的组件

解析DOM模板时的注意事项

html是由严格解析限制的例如ul中只能有li,table中应该是tr。当我们在这些元素中使用自定义组件时会被提升到外部造成错误,使用is可以变通的方法

<table>
  <blog-post-row></blog-post-row>
</table>
<!-- 上例中直接使用自定义组件会渲染错误,下面使用table内允许的元素tr,通过加is来指定自定义组件 -->
<table>
  <tr is="blog-post-row"></tr>
</table>

注意单组件中不存在这个问题

评论已关闭