事件处理

v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码,示例:

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
<!-- 点击按钮会依次显示'Hello Vue.js!'和Button>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // `this` 在方法里指向当前 Vue 实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
})

// 也可以用 JavaScript 直接调用方法
example2.greet() // => 这种调用只会弹窗一次,显示'Hello Vue.js!'
  • 事件修饰符
    Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的,包括:
    • .stop
    • .prevent
    • .capture
    • .self
    • .once
    • .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

  • 其他修饰符(基本用不到,用到了再查文档就行)
    • 按键修饰符
    • 系统修饰符
    • .exact 修饰符
    • 鼠标按钮修饰符

组件基础篇

Vue示例中的data类型:Object | Function;限制:组件的定义只接受 function,这一点尤其需要注意,data详情用法见链接api
例子:

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

同一个组件可以任意次的复用,正是由于data返回的是函数,所以每个实例可以维护一份被返回对象的独立的拷贝,否则,返回的是对象的话,所有的实例将共享引用同一个数据对象,这样显然不合适

  • 组件注册
    • 全局注册
      全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
    • 局部注册
  • prop
    Prop 是你可以在组件上注册的一些自定义特性,用来传递数据给组件,组件默认可以有任意数量的 prop ,可以用一个 props 选项将其包含在该组件可接受的 prop 列表中,其中v-for迭代数据,v-bind绑定数据,key作为唯一标示区别等用法跟前面基本语法保持一致。
    例子:

    <div id="blog-post-demo" class="demo">
      <blog-post
          v-for="post in posts"
          v-bind:key="post.id"
          v-bind:title="post.title"
      ></blog-post>
    </div>
      Vue.component('blog-post', {
      props: ['title'],
      template: '<h3>{{ title }}</h3>'
      })
    
      new Vue({
      el: '#blog-post-demo',
      data: {
          posts: []
      },
      created: function () {
          // Alias the component instance as `vm`, so that we
          // can access it inside the promise function
          var vm = this
          // Fetch our array of posts from an API
          fetch('https://jsonplaceholder.typicode.com/posts')
          .then(function (response) {
              return response.json()
          })
          .then(function (data) {
              vm.posts = data
          })
      }
      })

    fetch请求的返回数据格式为:

    [
      {
          userId: 1,
          id: 1,
          title: "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
          body: "quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto"
      },
      {
          userId: 1,
          id: 2,
          title: "qui est esse",
          body: "est rerum tempore vitae sequi sint nihil reprehenderit dolor beatae ea dolores neque fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis qui aperiam non debitis possimus qui neque nisi nulla"
      },
    ]
  • 单个根元素
    组件template部分不能写多个单独的元素,必须用父元素包裹起来。
    错误例子:

      template: '<h3>{{ title }}</h3><h1>abc</h1>'

    正确例子:

      template: '<div class="test"><h3>{{ title }}</h3><h1>abc</h1></div>'

    属性传递多个值示例:

      <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>
      `
      })
    这样传递的post对象的所有属性都可以迭代出来,上面例子中template用到了``这个JavaScript 的模板字符串来写,对于多行书写更优雅一些。
  • 监听子组件事件(这部分初看很难理解,需要结合实际代码效果多看几遍)
    父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件

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

    同时子组件可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件

      <button v-on:click="$emit('enlarge-text')">
        Enlarge text
      </button>

    有了这个 v-on:enlarge-text="postFontSize += 0.1" 监听器,父级组件就会接收该事件并更新 postFontSize 的值。emit的用法参考api
    可以使用 $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(难以理解就后面再看)

        Vue.component('custom-input', {
      props: ['value'],
      template: `
        <input
          v-bind:value="value"
          v-on:input="$emit('input', $event.target.value)"
        >
      `
    })
      <custom-input v-model="searchText"></custom-input>
  • 过插槽分发内容

    和 HTML 元素一样,我们经常需要向一个组件传递内容

      Vue.component('alert-box', {
        template: `
          <div class="demo-alert-box">
            <strong>Error!</strong>
            <slot></slot>
          </div>
        `
      })
  • 动态组件
    示例代码1
    示例代码2

  • 解析 DOM 模板时的注意事项
    有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。错误示例:

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

    这个自定义组件 会被作为无效的内容提升到外部,并导致最终渲染结果出错
    正确示例:

    <table>
      <tr is="blog-post-row"></tr>
    </table>
    需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
    • 字符串 (例如:template: '...')
    • 单文件组件 (.vue)
    • <script type="text/x-template">
01-18 11:10