.lazy
在默认情况下,使用 v-model
使 input标签
中的内容与data
中的属性双向绑定,值实时同步。但添加 lazy
修饰符后,从而让数据在 失去焦点 或者 回车 时才会更新同步
<input v-model.lazy="msg">
lazy
修饰符可以避免value
内容没有打完就执行后续的change
方法
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
<input v-model.number="age" type="number">
这通常很有用,因为即使在 type="number"
时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat()
解析,则会返回原始的值
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
<input v-model.trim="msg">
为了方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。vue提供了方法修饰符
.stop
阻止单击事件继续以冒泡顺序(从里层到外层)传播
举例:
<a v-on:click="do1">
<a v-on:click="do2">
<a v-on:click="do3">a>
a>
a>
<a v-on:click="do1">
<a v-on:click.stop="do2">
<a v-on:click="do3">a>
a>
a>
.prevent
阻止默认事件,只是执行自己命名的函数,使用的并不多
举例:
<form v-on:submit.prevent="alert('hello')">form>
.capture
即是给元素添加一个监听器,当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发。
没加capture
修饰符的就遵从冒泡顺序。
<a v-on:click.capture="do1">
<a v-on:click="do2">
<a v-on:click.capture="do3">
<a v-on:click="do4">a>
a>
a>
a>
.self
点击当前元素自身时才触发事件
可以理解为跳过冒泡事件和捕获事件
<a v-on:click="do1">
<a v-on:click.self="do2">
<a v-on:click="do3">a>
a>
a>
.once
多次点击事件将只会触发事件一次
修饰符注意点
:1、 修饰符可以串联
<a v-on:click.stop.prevent="doThat">a>
2、使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
Vue除了提供了默认内置的指令外,还允许开发人员根据实际情况自定义指令,它的作用价值在于当开发人员在某些场景下需要对普通DOM元素进行操作的时候。
注册全局指令
Vue.directive( id, [definition] )
第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀)
第二个参数可以是对象数据,也可以是一个函数。
<div id="app">
<input type="text" v-focus>
div>
<script>
Vue.directive("focus", {
inserted: function(el){// 当被绑定的元素插入到 DOM 中时……
el.focus();// 聚焦元素
}
})
new Vue({
el: "#app"
})
script>
注册局部指令
在实例化对象中也接受一个 directives 的选项:
new Vue({
el: "#app",
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
})
上述例子实现了在页面加载完成之后自动聚焦到输入框的功能。其中 inserted 是自定义指令的钩子函数
钩子函数
待更新
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。由于组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子
等。仅有的例外是像 el 这样根实例特有的选项。
全局组件
全局组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中,甚至各个全局组件 在各自内部 也都可以相互使用
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data() {
return {
count: 0
}
},
template: '<button v-on:click="count++">
You clicked me {{ count }} times.
</button>'
})
你给予组件的名字可能依赖于你打算拿它来做什么,强烈推荐遵循 W3C 规范
中的自定义组件名 (字母全小写且必须包含一个连字符),这会帮助你避免和当前以及未来的 HTML 元素相冲突。
当我们定义这个
组件时,你可能会发现它的 data 并不是像这样直接提供一个对象data: { count: 0 }
取而代之的是:一个组件的 data 选项必须是一个函数,从而每个实例可以维护一份独立的拷贝:
data:() {
return {
count: 0
}
}
组件可以进行任意次数的复用
<div id="components-demo">
<button-counter>button-counter>
<button-counter>button-counter>
<button-counter>button-counter>
div>
每个组件都会各自独立维护它的data。每用一次组件,就会有一个它的新实例被创建
它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
局部组件
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。你可以通过某个 Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件
在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
在根实例的 components
选项中定义你想要使用的组件:
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
对于 components
对象中的每个 property 来说,其 property名
就是 自定义组件 的名字,其 property值
就是这个组件的对象。
注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentA
在 ComponentB
中可用,则你需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
}
组件交互Prop
(父 -> 子)prop
是你可以在组件上注册的一些 自定义 属性。当一个值传递给一个 prop属性
的时候,它就变成了那个组件实例的一个 property。一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop
在根实例中有一个数组,存着一些数据,同时需要在blog-post组件
中使用这些数据,那么就可以在blog-post组件
中添加一个props属性
Vue.component('blog-post', {
props: ['title'],
template: '{{ title }}
'
})
new Vue({
el: '#blog-post-demo',
data: {
blogs: [
{ 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="blog in blogs" :key="blog.id" :title="blog.title">blog-post>
HTML 中的 attribute 名是大小写不敏感的,prop 名最好是kebab-case (短横线分隔命名) 方式命名。ps : 如果使用字符串模板就可以使用驼峰命名,为了不搞混还是统一使用 kebab-case 方式命名
上述的举例中以字符串数组
形式列出了 prop ,但通常我们希望每个 prop 都有指定的值类型。这时,你可以以对象
形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
基础的类型检查:
Vue.component('my-component', {
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function
}
}
进阶的类型检查:
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 就会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
像这样给 prop 传入一个静态的值:
<blog-post title="My journey with Vue">blog-post>
同时 prop 可以通过 v-bind 进行动态赋值:
<blog-post v-bind:title="blog.title">blog-post>
<blog-post v-bind:title="blog.title + 'by' + blog.author.name">blog-post>
在上述两个示例中,传入的值都是字符串类型的,但实际上 任何 类型的值都可以传给一个 prop
<blog-post v-bind:likes="42">blog-post>
<blog-post v-bind:likes="post.likes">blog-post>
<blog-post is-published>blog-post>
<blog-post v-bind:is-published="false">blog-post>
<blog-post v-bind:is-published="post.isPublished">blog-post>
<blog-post v-bind:comment-ids="[234, 266, 273]">blog-post>
<blog-post v-bind:comment-ids="post.commentIds">blog-post>
对于一个给定的对象 post
:
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post
v-bind:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
>blog-post>
<blog-post v-bind:="post">blog-post>
<blog-post v-bind:id="post.id" v-bind:title="post.title" >blog-post>
所有的 prop 都使得其父子 prop 之间形成了一个单向下行
绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这意味着你不应该在一个子组件内部改变 prop
。如果你这样做了,Vue 会在浏览器的控制台中发出警告。因为这导致你的应用的数据流向难以理解
以下是两种常见的试图变更一个 prop 的情形:
1、这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
2、这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意 在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中变更这个对象或数组本身将会 影响 到父组件的状态。
自定义事件/监听子组件事件
(子 -> 父)有时子组件的一些功能可能要求和父级组件进行沟通,例如:触发事件让父组件做出响应,或者还想传数据给父组件。Vue 实例提供了一个自定义事件的系统来解决这个问题。父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件:
我们想用一个放大字号按钮
改变全局的文章的字号大小
定义一个根实例:
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [/* ... */],
postFontSize: 1 //这个 property 来控制全局字号大小
}
})
定义一个子组件:
提供三个功能:1、标题显示。 2、放大字体按钮。3、显示内容
Vue.component('blog-post', {
props: ['post'],
template: `
{{ post.title }}
`
})
子组件可以通过调用内建的 $emit 方法并传入事件名称(enlarge-text)
来触发一个父组件事件
html部分:
父组件监听对应子组件传入的事件名称,子组件提示触发时,父组件反馈响应(字号属性+0.1)
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<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>
div>
div>
这还不够!!!有的时候我们需要传值给父组件,我们把上面的例子进行扩展。
我们想让子组件的button来决定到底放大多少字号而不是按一下放大一点,那么这个时候就需要传一个明确的值给父组件
这其实比想象中简单,只需为$emit添加第二个参数即可
<button v-on:click="$emit('enlarge-text',0.5)">
Enlarge text
button>
然后当在父级组件监听这个事件的时候,我们可以通过 $event
访问到被抛出的这个值:
<blog-post
...
v-on:enlarge-text="postFontSize += $event">
blog-post>
很多时候父组件的响应是一个function(onEnlargeText)
<blog-post
...
v-on:enlarge-text="onEnlargeText">
blog-post>
那么这个值将会作为第一个参数enlargeAmount(参数名自定义)
传入这个方法:
methods: {
onEnlargeText(enlargeAmount) {
this.postFontSize += enlargeAmount;
}
}
在更复杂的情况下,我们应该考虑使用专门的状态管理模式Vuex
插槽slot
有时为一个插槽设置具体的默认内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 组件中,我们可能希望这个 内绝大多数情况下都渲染默认文本,我们可以将默认文本放在 标签内:
template:`
<button type="submit">
<slot>我是一个默认内容slot>
button>
`
在父组件中不添加任何内容地使用这个组件:
<submit-button>submit-button>
默认内容会被渲染:
<button type="submit">
我是一个默认内容
button>
但若为组件提供了内容:
<submit-button>用户自定义内容submit-button>
则这个提供的内容将会被渲染从而取代默认内容:
<button type="submit">
用户自定义内容
button>
很多时候我们需要多个插槽,对于一个带有如下模板的
组件:
template:`
<div class="container">
<header>
<slot>slot>
header>
<main>
<slot>slot>
main>
<footer>
<slot>slot>
footer>
div>
`
对于这样的情况, 元素有一个特殊的 attribute—name
,不带 name 的
会带有隐含的名字“default”
template:`
<div class="container">
<header>
<slot name="header">slot>
header>
<main>
<slot>slot>
main>
<footer>
<slot name="footer">slot>
footer>
div>
`
在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令,并以 v-slot (#为其缩写)的参数的形式提供其名称:
注意:slot指令已被弃用,尽量在teplate标签上使用v-slot指令
<base-layout>
<template v-slot:header>
<h1>Here might be a page titleh1>
template>
<p>A paragraph for the main content.p>
<p>And another one.p>
<template #footer>
<p>Here's some contact infop>
template>
base-layout>
现在 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的
中的内容都会被视为默认插槽的内容
渲染结果
<div class="container">
<header>
<h1>Here might be a page titleh1>
header>
<main>
<p>A paragraph for the main content.p>
<p>And another one.p>
main>
<footer>
<p>Here's some contact infop>
footer>
div>
动态组件(不常用)