Vue学习笔记(三)

2019-06-04

博客的Markdown解释器显示有问题,请移步github

Vue组件

组件基础

创建可复用的组件,利用Vue.component方法

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me 8 times.</button>'
})

使用时可以像普通组件一样用,可以绑定在某个vue实例(vm)上,接受普通vm一样的属性值,比如data,methodscomputedwatch以及一些声明周期方法等

<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })
  • 对于Vue组件,data必须是一个getter函数,而不是直接定义一个对象。

组件prop

组件间的prop是父组件向子组件传递数据的方式,在Vue.component方式中定义prop

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3></h3>'
})

这样可以传递数据了

<blog-post title="Why Vue is so fun"></blog-post>

可以使用vue标签和组件prop配合,例如v-bind:title

单根元素限制

Vue在风格上强行限制了每一个组件的模板只能有一个根元素

子组件事件

Vue解决监听子组件事件的思维是:子组件监听原生事件=>子组件发出自定义事件=>由父组件处理自定义事件

  • 子组件监听原生事件,当然是用v-on:click之类的方法

  • 子组件发出自定义事件,使用内建的$emit方法

    <button v-on:click="$emit('action')">
      button
    </button>
    
    • $emit还可以跑出一个参数值,比如$emit('action',1)
  • 父组件同样通过v-on监听事件

    <child v-on:action="handle">
    </child>
    
    • 如果子组件$emit抛出了值,父组件的处理函数将会将其作为第一个参数传入方法。

v-model双向绑定

我们在表单中使用了v-model来绑定属性,同样父子组件也可以通过v-model进行双向数据绑定,公式如下:

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

等于

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

<slot>元素

<slot>是Web Components技术标准的一部分,翻译为插槽。

在Vue中,<slot>元素是用来在组件标签下插入元素,完成渲染,当然用prop也可以做到。

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})
<alert-box>
  Something bad happened.
</alert-box>

<component>元素

<component>元素是Vue内建的组件,用途是一个组件占位符,定义其is属性,可以替换成别的元素

<component v-bind:is="currentComponent"></component>

通过绑定Vue实例中的currentComponent数据,可以动态切换组件。currentComponent可以是任何已注册的组件名称。

在特殊标签内正确渲染

有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。可以使用is属性来正确渲染。

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

深入组件

命名风格

Vue推荐两种风格:

Vue.component('my-component-name', { /* ... */ })
Vue.component('MyComponentName', { /* ... */ })
//使用下面这种方式时,在html内引用时两种方式都可以用

组件注册

  1. 全局注册,这种方法注册的组件可以在任何地方使用。

    Vue.component('my-component-name', {/*组件选项对象*/})
    
  2. 局部注册,在一个Vue实例中注册对象,仅在这个实例中使用,其子组件和父组件都不能用

    new Vue({
        el: '#app',
        components:{
            my-component-name:{/*组件选项对象*/}
        }
    })
    

    甚至可以简写为:

    var myComponentA={/*组件选项对象*/}
    new Vue({
        el: '#app',
        components:{
            myComponentA
        }
    })
    

    相当于一个名称为'myComponentA'的组件,其选项对象为myComponentA

    如果要在一个组件内局部注册另外一个组件,可以用以下类似的形式

    var ComponentA = { /*组件选项对象*/ }
       
    var ComponentB = {
      components: {
        'component-a': ComponentA
      },
      // 组件其他选项属性
    }
    

组件与模块

使用Babel(ES2015)的模块系统来管理组件,Vue推荐创建一个components目录,将组件放置在各自的文件中,通过importexport来导入/导出局部的组件。

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
   
export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

prop

  1. 命名风格

    按照标准,HTML中的特性名是大小写不敏感的,所以推荐使用短横线命名法。如果用了驼峰命名法,会自动解释成短横线命名法。

    Vue.component('blog-post', {
      props: ['postTitle'],
      template: '<h3></h3>'
    })
    
    <blog-post post-title="hello!"></blog-post>
    
  2. 动态prop

    我们知道v-bind标签是用来动态绑定DOM结点的属性,当然也可以绑定自定义的属性,实现动态prop。

    并且,v-bind标签也可以用来传入JS表达式,而直接写的自定义属性是字符串。

  3. 传入对象的所有属性

    post: {
      id: 1,
      title: 'My Journey with Vue'
    }
    
    <blog-post v-bind="post"></blog-post>
    

    等价于:

    <blog-post
      v-bind:id="post.id"
      v-bind:title="post.title"
    ></blog-post>
    
  4. 单向数据流

    父子组件间通过prop形成单向数据流,prop的值应该完全由父组件决定,而子组件不应该去修改prop。而是通过事件去通知父组件从而改变父组件的状态。

  5. prop验证

    Vue提供了多种prop验证来在HTML自定义属性的基础上加强prop的鲁棒性。

    Vue.component('my-component', {
      props: {
        // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
        propA: Number,
        // 多个可能的类型
        propB: [String, Number],
        // 必填的字符串
        propC: {
          type: String,
          required: true
        },
        // 带有默认值的数字
        propD: {
          type: Number,
          default: 100
        },
        // 带有默认值的对象
        propE: {
          type: Object,
          // 对象或数组默认值必须从一个工厂函数获取
          default: function () {
            return { message: 'hello' }
          }
        },
        // 自定义验证函数
        propF: {
          validator: function (value) {
            // 这个值必须匹配下列字符串中的一个
            return ['success', 'warning', 'danger'].indexOf(value) !== -1
          }
        }
      }
    })
    

    然而,Vue并不会强制执行验证,只是在开发版本的Vue会产生警告。

    类型验证还可以是任意的构造函数,并不是只有原生对象。

  6. 非prop的特性

    没有在prop中定义的属性,会直接作用于组件的根元素。如果组件已经有该名称的属性,将会覆盖。特别的,对于classstyle属性,不会覆盖而是合并。可以在组件选项中设置inheritAttrs: false来禁用掉根元素继承特性。

自定义事件

我们知道组件自定义事件的触发是使用组件的$emit方法

this.$emit('myEvent');

绑定原生事件:通过.native修饰符来监听组件根元素的原生事件

插槽

在组件基础中我们知道了<slot>可以作为承载inner元素内容的出口,在组件指定位置进行渲染,使用时需要注意如下特点:

  1. 编译作用域

    插槽中内容编译的时候,仍然是在当前作用于中编译的,能够访问当前vm中的数据属性。

    <my-component>
        My name is ;
    </my-component>
    
  2. 插槽默认值

    可以在<slot>标签内部写默认值,会在父组件不提供插槽内容时进行渲染。

  3. 具名插槽

    在别的框架组件和HTML一些标签中,我们经常见到一些具有多插槽的组件,类似

    <my-article>
        <header>
        	标题
        </header>
        <main>
        	内容
        </main>
        <footer>
        	脚注
        </footer>
    </my-article>
    

    对于vue,拥有具名插槽的支持,如下使用

    <div>
        <header>
        	<slot name="header"></slot>
        </header>
        <main>
        	<slot></slot>
        </main>
        <footer>
        	<slot name="footer"></slot>
        </footer>
    </div>
    

    未命名的标签会有隐含的名称default,在Vue组件使用时,我们可以用<template>元素配合v-slot标签来插入到指定命名插槽。

    <base-layout>
      <template v-slot:header>
        <h1>Here might be a page title</h1>
      </template>
       
      <template v-slot:default>
        <p>A paragraph for the main content.</p>
        <p>And another one.</p>
      </template>
       
      <template v-slot:footer>
        <p>Here's some contact info</p>
      </template>
    </base-layout>
    

异步组件

Vue支持以类似Promise的语法来定义异步组件

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

当resolve调用时,组件才会被注册

访问元素和组件

  • 在Vue实例中通过$root属性访问组件根元素

  • 在Vue实例中通过$parent属性访问组件的父元素

  • 通过ref来访问任意元素和组件

    <base-input ref="usernameInput"></base-input>
    
    this.$refs.usernameInput
    

依赖注入

Vue也提供了父子组件间进行依赖注入的方法

父组件定义provide选项,它需要定义一个getter函数

provide: function () {
  return {
    getMap: this.getMap
  }
}

子组件声明自己需要什么依赖

inject: ['getMap']

那么每个子组件就都拥有了getMap这个方法