vue易忘知识点

前言:

vue项目开发,大多数时间是在写业务代码,能用到的知识点无非是v-指令,mounted加载,props和emit传参,对这几个知识点熟悉的话,就可以完成大部分的开发工作,然而vue项目也并不是想象中这么简单,某些复杂功能会涉及到一些不常用的知识点,下面我将总结出来,以备不时之需。

1. 指令动态参数

模版中的绑定属性和绑定事件的方法如下:

<a v-bind:href="url">...</a>

<a v-on:click="doSomething">...</a>

对应的url和doSomething会在js中的data及methods中找到,这是常规的绑定方法。2.6.0版本后,vue新增了动态参数的绑定方法:

<a v-bind:[attributeName]="url"> ... </a>

<a v-on:[eventName]="doSomething"> ... </a>

以上可以看到参数名变成动态了,可以在js中的data中找到对应的参数名的值。

2. 计算属性、方法、侦听器

有些在计算属性中得到的结果,在方法里也能得到,那么计算属性和方法的区别在哪?官方文档中提到,“不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。”这里提到了一个名词,“响应式依赖”,读过文档中vue的响应式原理之后,不难理解,当data中定义的变量发生变化时,页面渲染会跟着变化,这个过程可以被理解为响应式,那么计算属性就是当data中的变量发生变化时,计算属性才会重新计算,否则保持原来的结果不变。而方法是只要执行一次,结果就会发生改变。

计算属性与侦听器在实现原理及功能上是及其接近的,官网给出的解释很简单,例子如下:

watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }



computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }

同样是计算fullName,计算属性的写法相对简单一些,而watch更适合执行一些开销较大的计算或者异步方法。

计算属性默认带的只有getter,但是当你需要时,可以手动设置一个setter,官方🌰如下:

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

3. 组件样式

这里主要讲class样式,当在一个自定义组件上使用 class property 时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。

Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})

<my-component class="baz boo"></my-component>

<p class="foo bar baz boo">Hi</p>

对于带数据绑定 class 也同样适用:

<my-component v-bind:class="{ active: isActive }"></my-component>

当 isActive 为 truthy时,HTML 将被渲染成为:

<p class="foo bar active">Hi</p>

4.v-if和v-else

用法很简单,但有一个注意点,用key管理可复用的元素,官方🌰如下:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

上面代码,无论怎么切换loginType,如果输入框中有值,则值一直存在,不会因为切换就会消失,这时key的作用就来了:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

再次切换的时候,输入框中的值就消失了。

5. 列表渲染

列表渲染一般通过v-for来实现,比较简单,其中有几个关键点需要注意:

首先是要在循环的元素或组件上加key,key值一般是循环数据中的唯一值,目前元素的循环没有强制加key,但组件循环必须加key;

v-for中可以带有两个参数,第一个参数是数据项,第二个参数是当前项的索引,items是一个数组数据:

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>

你也可以用 of 替代 in 作为分隔符

<div v-for="item of items"></div>

v-for还可以遍历一个对象:

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>

new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

可以增加参数:name相当于对象数据中的key,value是值

<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

三个参数的情况:index为索引

<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

6. 事件修饰符、按键修饰符、系统修饰键

只举几个官方🌰,详情阅读文档:

<!-- 阻止单击事件继续传播 -->
<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>

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<input v-on:keyup.enter="submit">

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符

<input v-on:keyup.page-down="onPageDown">

系统修饰键在这里包含ctrl alt shift meta这几个。即当进行某个操作时,需要同时按住键盘中的这几个键,才能触发。

<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

7. 表单输入绑定v-model

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件

v-model的修饰符:

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

8. props向子组件传递数据

传固定值:

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

<blog-post title="My journey with Vue"></blog-post>

传绑定值: 

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind

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>

props的大小写:在js里用camelCase(驼峰式),在html中用kebab-case

props的类型:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

props是单项数据流

props验证:

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

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

非prop的Attribute

一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute。

假如一个<bootstrap-date-input>组件,其内部模板是<input type="date" class="form-control">,那么当在组件上加入一些属性后,<input type="date" class="form-control">中会自动加入该属性,并且会替换掉原来的属性。但class和style除外,这两种会与其进行合并,举例如下:

<bootstrap-date-input data-date-picker="activated" class="date-picker-theme-dark"></bootstrap-date-input>

<input type="date" data-date-picker="activated" class="form-control date-picker-theme-dark">

当然,上述这种模式也是可以被禁止的,

Vue.component('my-component', {
  inheritAttrs: false,
  // ...
})

被禁止后,组件上的attrebute就不会传到组件内的根元素了。

9. 监听子组件事件

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

监听组件blog-post中的事件enlarge-text,当触发这个事件时,在父组件中执行postFontSize+=0.1;

而这个事件是在组件blog-post内部进行触发的,假设该组件内部存在这样一个按钮:

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

通过按钮的click事件,用$emit方法触发事件,从而传到父组件。

假如触发事件时需要带参数,那么写法如下:

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

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

10. 在组件上使用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>

组件内部的实现:

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>

如果input中type=checkbox,那么emit的事件就不是input,应该是change,参数变为$event.target.checked

11. .native的用法

假如想要监听某个组件根元素的原生事件,你可能会直接在组件中加入事件:

<base-input v-on:focus="onFocus"></base-input>

组件内部:
<input/>

再比如:

<myComponent @click="myClick"></myComponent>

组件内部:
<button>点我</button>

按我们以往对组件的理解,组件的自定义事件,是可以被组件内部的元素通过$emit来触发的,从而使父组件能够监听到组件内部的一些事件。但若想在组件上通过focus、click等原生事件来监听组件内部根元素的原生事件,就需要在组件的事件上加上.native才能生效,所以在上述代码中,需要在原生事件后加上.native,例如:

<base-input v-on:focus.native="onFocus"></base-input>


<myComponent @click.native="myClick"></myComponent>

.native虽然解决了组件根元素原生事件的监听问题,但有下面这种情况它是不能发挥作用的:

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

如上面代码,组件的根元素是label,不是input,而label是没有focus事件的,所以.native这个解决方案就不适合此种情况了,该如何解决呢?

12. $attrs和$listeners

通过理解$attrs和$listeners的用法,来看如何解决监听原生事件的问题。

首先来看$attrs,它代表的是组件传给其子元素的除props和class、style之外的所有属性,举个🌰:

<myComponent label="姓名" placeholder="请输入姓名" class="myInput" otherAttr="其它属性"></myComponent>

上面是一个组件,并传入了label、placeholder、class、otherAttr等属性,下面来看组件内部的实现:

<template>
    <label>
        {{label}}-
          {{$attrs.placeholder}}-
          <input v-bind="$attrs"/>
    </label>
</template>
...

<script>
    export default {
        props:['label'],
        mounted():{
            console.log(this.$attrs);
    }
    
    }
</script>

打印的$attrs的值为{placeholder:"请输入姓名",otherAttr:"其它属性"},而lable和class没有被打印出来。此外需要注意的一点是,属性要用到哪个内部元素上,那么这个内部元素就要加上v-bind="$attrs"。

注意,默认情况下,非props属性会被直接继承到组件的根元素上,如果想阻止这种默认行为,需要在代码中加入inheritAttrs: false。

下面再来看一下$listeners

与$attrs类似,$listeners是将组件上的事件映射到组件内部的元素上,用法如下:

<base-input v-on:focus="onFocus"></base-input>


组件内部:

<label>
     {{ label }}
      <input
        v-on="$listeners"
      >
</label>

这样input上就带有focus事件了,当input获得焦点时,就会调用onFocus方法,这样就省去了通过$emit触发事件的步骤,这种方式在组件的嵌套中非常有用。(带有.native的事件不会被包含到$listeners中)

此外,元素上的原生事件可以覆盖组件上传入的事件,举例如下:

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` 将所有的对象合并为一个新对象
      return Object.assign({},
        // 我们从父级添加所有的监听器
        this.$listeners,
        // 然后我们添加自定义监听器,
        // 或覆写一些监听器的行为
        {
          // 这里确保组件配合 `v-model` 的工作
          input: function (event) {
            vm.$emit('input', event.target.value)
          },
           focus: function(){
            console.log("我是元素上的focus")
            }
        }
      )
    }
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `
})

inputListeners包含了$listeners中的事件,同时增加了input和focus两个事件,因为组件外部也传入了focus事件,所以这个后定义的focus事件会覆盖组件上的focus事件。

13. .sync修饰符

属性的传递是单项的,由上而下的,而组件如果想要改变组件外的属性,以前需要通过$emit触发一个监听事件,然后在对应的方法中改变属性的值,但是在使用.sync修饰符后就不用了。

<text-document v-bind:title.sync=oldTitle></text-document>

上面代码中,title是传入到组件内部的一个props,组件内部可以直接使用这个title,那么如果想在组件内部改变这个title的值,可以通过以下代码来实现:

this.$emit('update:title', newTitle)

update:xxxxx是固定写法。

其实.sync其实是一个语法糖,其等价于

<text-document
  v-bind:title="oldTitle"
  v-on:update:title="oldTitle= $event"
></text-document>

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。

14. 插槽slot

插槽的一般用法不难理解,主要看以下具名插槽和作用域插槽

具名插槽,看代码:

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

上面是一个组件内部的元素,slot标签内加上了name属性。

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

上面是一个base-layout组件,组件中间的内容会被插入到组件内部的slot中。template中的v-slot的值,对应的就是组件内部的slot标签的name值。没有v-slot的内容,会被插入到没有name的slot内。(v-slot只能加在<template>上,只有一种情况除外,见后面内容);

上面代码渲染后的结果:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

具名插槽的名字,也可以是动态参数:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

具名插槽的简单写法:

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

作用域插槽稍微有点难理解,但也不是太难,直接看代码讲解:

<current-user>
  {{ user.firstName }}
</current-user>

假如有一组件,组件中间是user.firstName,user的值不是在组件外获取的,而是来自组件内部,如下代码:

<template>
    <span>
        <slot>{{user.lastName}}</slot>
    </span>
</template>

...

data(){
    return {
     user:{
            firstName:"zhang",
            lastName:"san"
        }
    }
}

这个时候代码是无法运行的,因为组件外部的user.firstName是取不到组件内部的user的。按照官方文档的说法,为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

<current-user>
  <template v-slot="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

上面的slotProps就是我们为组件内部的slot标签上的插槽prop起的名字,当然我们也可以起其它的你喜欢的名字。这样在组件外我们就可以调用组件内部的user数据了。

上面提到v-slot只能用在<template>标签中,但有一种情况例外,这种情况就是当组件内部只有一个默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:

<current-user v-slot:default="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

更简单的写法是
<current-user v-slot="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

但如果组件内部有其它具名插槽时,就不能这么写了。

上面用到的slotProps是我们为slot标签上的插槽prop起的名字,那么假如我们不起这个名字,还可以使用解构的方式来实现相同的功能:

<current-user v-slot="{ user }">
  {{ user.firstName }}
</current-user>

还可以对prop进行重命名:

<current-user v-slot="{ user: person }">
  {{ person.firstName }}
</current-user>

15. keep-alive

用于防止动态组件重复渲染,一般用在路由组件中比较多,这样路由在来回切换的时候,不用每一次都重新加载页面数据。需要注意的是,在使用keep-alive时,切换的动态组件需要有名字,也就是name。

16. 异步组件工厂函数

const AsyncComponent = () => ({
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})

以上结构是2.3.0新增的,还没有亲自使用过,暂且放这。

17. 混入

混入,一般用于各个组件可复用的部分,一个混入对象可以包含任意组件选项。

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

混入对象和使用混入对象的组件,如果出现冲突时,以组件中内容为准;

同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。

var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"

18. 自定义指令

全局指令:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

局部指令:

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

用法:

<input v-focus>

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

接下来我们来看一下钩子函数的参数 (即 elbindingvnode 和 oldVnode)。

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

这是一个使用了这些 property 的自定义钩子样例:

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>

Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})


运行结果为:
name:'demo',
value:'hello',
expression:'message',
argument:'foo',
modifiers:{"a":true,"b":true},
vnode keys:tag,data,children...//省略若干,可见官网详情

动态指令参数:

直接看代码理解:

Vue.directive('pin', {
  bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    var s = (binding.arg == 'left' ? 'left' : 'top')
    el.style[s] = binding.value + 'px'
  }
})

new Vue({
  el: '#dynamicexample',
  data: function () {
    return {
      direction: 'left'
    }
  }
})
<div id="dynamicexample">
  <h3>Scroll down inside this section ↓</h3>
  <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
</div>

19. 参数h

import AnchoredHeading from './AnchoredHeading.vue'

new Vue({
  el: '#demo',
  render: function (h) {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
    )
  }
})

将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入 const h = this.$createElement,这样你就可以去掉 (h) 参数了。对于更早版本的插件,如果 h 在当前作用域中不可用,应用会抛错。

20. 插件

使用插件很简单Vue.use();难的是开发插件,这部分内容需要查看官方文档的插件写法,同时到网上找一些插件的例子。

21. 过滤器

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
你可以在一个组件的选项中定义本地的过滤器:

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

或者在创建 Vue 实例之前全局定义过滤器:

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})

过滤器可以串联:

{{ message | filterA | filterB }}

22. 过渡&动画

基本例子:

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

new Vue({
  el: '#demo',
  data: {
    show: true
  }
})


.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

例子说明:

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  3. v-enter-to2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  4. v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. v-leave-to2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter

自定义过渡类名:

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

配合第三方 CSS 动画库,如 Animate.css 结合使用十分有用。例子:

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<div id="example-3">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>
</div>

new Vue({
  el: '#example-3',
  data: {
    show: true
  }
})

显性的过渡时间

<transition :duration="1000">...</transition>//单位是毫秒


<transition :duration="{ enter: 500, leave: 800 }">...</transition>

过渡模式

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开。

  • out-in:当前元素先进行过渡,完成之后新元素过渡进入。

<transition name="fade" mode="out-in">
  <!-- ... the buttons ... -->
</transition>

列表过渡

<transition-group>组件使用要点:

  • 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>。你也可以通过 tag attribute 更换为其他元素。
  • 过渡模式不可用,因为我们不再相互切换特有的元素。
  • 内部元素总是需要提供唯一的 key attribute 值。
  • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

列表的排序过渡

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>

<div id="list-complete-demo" class="demo">
  <button v-on:click="shuffle">Shuffle</button>
  <button v-on:click="add">Add</button>
  <button v-on:click="remove">Remove</button>
  <transition-group name="list-complete" tag="p">
    <span
      v-for="item in items"
      v-bind:key="item"
      class="list-complete-item"
    >
      {{ item }}
    </span>
  </transition-group>
</div>

new Vue({
  el: '#list-complete-demo',
  data: {
    items: [1,2,3,4,5,6,7,8,9],
    nextNum: 10
  },
  methods: {
    randomIndex: function () {
      return Math.floor(Math.random() * this.items.length)
    },
    add: function () {
      this.items.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove: function () {
      this.items.splice(this.randomIndex(), 1)
    },
    shuffle: function () {//.shuffle函数,是lodash中的一个函数,创建一个被打乱值的集合。
      this.items = _.shuffle(this.items)
    }
  }
})

.list-complete-item {
  transition: all 1s;
  display: inline-block;
  margin-right: 10px;
}
.list-complete-enter, .list-complete-leave-to
/* .list-complete-leave-active for below version 2.1.8 */ {
  opacity: 0;
  transform: translateY(30px);
}
.list-complete-leave-active {
  position: absolute;
}

列表的其它过渡方式,详见官网,这里引用官网一句话:唯一的限制是你的想象力。

状态过渡:详见官网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值