v-
前缀,但调用的时候需要。Vue.directive('指令名称', { /*钩子函数*/ });
directives
选项定义。directives: {
// focus为指令名
focus: {
// 指令的定义,inserted为钩子函数
inserted: function (el) {
el.focus();
}
}
}
<input v-focus>
<div id="app" v-demo="message">div>
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。与JS行为相关的操作,最好在inserted去执行。update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。指令钩子函数会被传入以下参数: (加粗为重要(或常用)属性)
除了
el
之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset
来进行。
<div id="app" v-demo="message">div>
Vue.directive('demo', {
bind: function (el, binding, vnode) {
var s = JSON.stringify;
el.innerHTML =
'name: ' + s(binding.name) + '
' +
'value: ' + s(binding.value) + '
' +
'expression: ' + s(binding.expression) + '
' +
'argument: ' + s(binding.arg) + '
' +
'modifiers: ' + s(binding.modifiers) + '
' +
'vnode keys: ' + Object.keys(vnode).join(', ');
};
})
new Vue({
el: '#app',
data: {
message: 'hello!'
}
});
在很多时候,可能想在 bind
和 update
时触发相同行为,而不关心其它的钩子。可以采用以下方式:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value;
});
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
<div v-demo="{ color: 'white', text: 'hello!' }">div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color); // => "white"
console.log(binding.value.text); // => "hello!"
})
使用
包裹需要动画的元素。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效 。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave
被删除),在过渡/动画完成之后移除。对于这些在过渡中切换的类名来说,**如果使用一个没有定义name
属性的
,则 v-
是这些类名的默认前缀。**如果你使用了
,那么 v-enter
会替换为 fade-enter
。
<style>
/* 定义进入和离开时候的过渡状态 */
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s ease;
position: absolute;
}
/* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(100px);
}
style>
<div id="app">
<input type="button" value="动起来" @click="myAnimate">
<transition name="fade">
<div v-show="isshow">动画哦div>
transition>
div>
<script>
var vm = new Vue({
el: '#app',
data: {
isshow: false
},
methods: {
myAnimate() {
this.isshow = !this.isshow;
}
}
});
script>
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。out-in
:当前元素先进行过渡,完成之后新元素过渡进入。例:
<transition name="fade" mode="out-in">
transition>
enter-class
enter-active-class
enter-to-class
(2.1.8+)leave-class
leave-active-class
leave-to-class
(2.1.8+)<link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
<div id="example-3">
<button @click="show = !show" value="Toggle render">button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hellop>
transition>
div>
<script>
new Vue({
el: '#example-3',
data: {
show: true
}
});
script>
用
组件上的 duration
属性定制一个显性的过渡持续时间 。
定制进入和移出的持续时间 。
可以在属性中声明 JavaScript 钩子 。
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
transition>
// ...
methods: {
// 进入中
// --------
beforeEnter: function (el) { ... },
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) { /* ... */ done(); },
afterEnter: function (el) { ... },
enterCancelled: function (el) { ... },
// 离开时
// --------
beforeLeave: function (el) { ... },
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) { /* ... */ done(); },
afterLeave: function (el) { ... },
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) { ... }
}
ps:
v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。可以通过 appear
特性设置节点在初始渲染的过渡 。
<transition appear>
transition>
使用
组件。
,它会以一个真实元素呈现:**默认为一个
。可以通过 tag
特性更换为其他元素。key
属性值。<transition-group tag="ul">
<li v-for="(item, i) in list" :key="item.id" @click="del(i)">
{{item.id}}---{{item.name}}
li>
transition-group>
新增的 v-move 特性,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name
属性来自定义前缀。
组件化和模块化的不同:
Vue.extend{ template: 'HTMLCode' }
返回一个组件模板对象。Vue.component('组件名称', 创建出来的组件模板对象)
var login = Vue.extend({
template: '登录
'
});
Vue.component('login', login);
Vue.component('组件名称', { template: 'HTMLCode' })
Vue.component('register', {
template: '注册
'
});
标签,但要放到被控制的组件外使用)中:(常用)<script id="tmpl" type="x-template">
<div><a href="#">登录</a> | <a href="#">注册</a></div>
script>
同时,需要使用 Vue.component 来定义组件:
Vue.component('组件名称', 模板id选择器)
Vue.component('account', {
template: '#tmpl'
});
注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!
**如果使用 Vue.component
定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改写为小写的字母,两个单词见使用-
链接。**如果不使用驼峰,则直接那名称来使用即可。
<account>account>
components
选项定义。...
components: {
account: {
template: ''
}
}
data
需要被定义为一个方法,且必须return对象。data
属性中的值,需要使用this
来访问.Vue.component('account', {
template: '#tmpl',
data: function() {
return {
msg: '大家好!'
}
},
methods:{
login: function() {
alert('点击了登录按钮');
}
}
});
components
属性定义局部子组件<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
components: { // 定义子组件
account: { // account 组件
template: '这是Account组件{{name}}
'
}, // 在这里使用定义的子组件
components: { // 定义子组件的子组件
login: { // login 组件
template: "这是登录组件
"
}
}
}
});
script>
<div id="app">
<account>account>
div>
flag
标识符结合v-if
和v-else
切换组件<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<my-com1 v-if="flag">my-com1>
<my-com2 v-else="flag">my-com2>
div>
<script>
Vue.component('myCom1', {
template: '奔波霸
'
})
Vue.component('myCom2', {
template: '霸波奔
'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true
},
methods: {}
});
</script>
<style>
.v-enter, .v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active, .v-leave-active {
transition: all 0.5s ease;
}
style>
<div id="app">
<a href="#" @click.prevent="comName='login'">登录a>
<a href="#" @click.prevent="comName='register'">注册a>
<transition mode="out-in">
<component :is="comName">component>
transition>
div>
<script>
Vue.component('login', {
template: '登录组件
'
});
Vue.component('register', {
template: '注册组件
'
});
var vm = new Vue({
el: '#app',
data: {
// 存放is指定的名称
comName: 'login'
},
methods: {}
})
script>
当在这些组件之间切换的时候,都会重新渲染一遍;但有种时候切换回来时想保持第一次切换前的画面,则需要在第一次他们被创建的时候缓存下来,可以用一个
元素将其动态组件包裹起来。
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
注意这个
要求被切换到的组件都有自己的名字,不论是通过组件的
name
选项还是局部/全局注册。
一般用于大型项目,使用的组件足够多的时候,就需要考虑性能问题,则这时候就好使用异步组件。
Vue.js允许将组建定义为一个工厂函数,动态地解析组件。Vue.js 只是组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: 'I am async!'
})
}, 1000)
})
注意:当使用webpack和.vue单文件的时候使用异步组件,要注意webpack的配置问题。
2.3.0+新增
这里的异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
v-slot
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即
v-slot
指令)。它取代了slot
和slot-scope
。
Vue 2.x 提供了用来访问被slot分发的内容的方法$slot
,用法:this.$slot.具名slot
,不常用。
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
navigation-link>
后备内容,只会在没有提供内容的时候被渲染:
例如:
<submit-button>submit-button>
<script>
var btn = Vue.component('submit-button', {
template:
``
});
var app = new Vue({
el: '#app',
});
script>
将被渲染为:
<button type="submit">
Submit
button>
当想把具体模板插入具体位置,就需用到具名插槽v-slot
。
任何没有被包裹在带有 v-slot
的 中的内容都会被视为默认插槽的内容,即没有name的slot。
v-slot:插槽名字name
补充:不添加v-slot,其实内部默认添加v-slot:default
例如:
<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 v-slot:footer>
<p>Here's some contact infop>
template>
base-layout>
<script>
var btn = Vue.component('base-layout', {
template:
`
`
});
var app = new Vue({
el: '#app',
});
script>
将被渲染为:
<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>
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的
组件:
<span>
<slot>{{ user.lastName }}slot>
span>
<current-user>
{{ user.firstName }}
current-user>
只有
组件可以访问到
user
,而user.firstName
是在父组件中渲染的,模板中slot访问的user却是子组件的数据(user)。
<div id="app">
<current-user>
<template v-slot="userSlot">
{{ userSlot.user.firstName }}
template>
current-user>
div>
<script type="text/javascript">
Vue.component('current-user', {
template:
`
{{ user.lastName }}
`,
data: function () {
return {
user: {
firstName: '好看',
lastName: '好好好'
}
}
},
});
var vm = new Vue({
el: '#app',
data: {}
});
script>
解析:
在slot元素上有一个类似 props 传递数据给组件的写法 v-bind:属性名 = 'xxx'
将数据传到了插槽。在父组件中使用 v-slot = props
,这里的props是一个临时变量,保存传递过来的属性,用它来访问来自子组件传递过来的数据。
注意:
v-slot = props
等价于v-slot:default = props
(默认插槽)。2.6版本之前可以使用slot-scope = props
,现已废弃但还可以使用。
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
。
和其它指令一样,该缩写只在其有参数的时候才可用。即#="{user}",会报警告
由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横分隔命名。
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
<div id="app">
<com1 v-bind:parentmsg="msg">com1>
div>
props
获取数据。var vm = new Vue({
el: '#app',
components: {
com1: {
template: '这是子组件 --- {{parentmsg}}
',
props: ['parentmsg']
}
}
});
props: ['warningText']
Vue.component('my-component', {
props: {
// 必须是数字类型
propA: Number,
// 必须是字符串或数字类型
propB: [String, Number],
// 布尔值,如果没有定义,默认值就是true
propC: {
type: Boolean,
default: true
},
// 数字,而且是必传
propD: {
type: Number,
required: true
},
// 如果是数组或对象,默认值必须是一个函数来返回
propE: {
type: Array,
default: function () {
return [];
}
},
// 自定义一个验证函数
propF: {
validator: function (value) {
return value > 10;
}
}
}
});
Vue.2.x 与 Vue 1.x 比较大的一个改变就是,Vue 2.x 通过 props 传递数据时单向的,也就是父组件数据变化时会传递给子组件,但反过来是不行的。而Vue 1.x 里提供了 .sync 的修饰符来支持双向绑定。
一个非 prop 特性是指传向一个组件,但是该组件并没有相应 prop 定义的特性。
因为显式定义的 prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。
例如,想象一下你通过一个 Bootstrap 插件使用了一个第三方的
组件,这个插件需要在其 上用到一个
data-date-picker
特性。我们可以将这个特性添加到你的组件实例上:
<bootstrap-date-input data-date-picker="activated">bootstrap-date-input>
然后这个 data-date-picker="activated"
特性就会自动添加到
的根元素上。
对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。比如传入 type="text"
就会替换掉 type="date"
并把它破坏!class
和 style
特性除外,即两边的值会被合并起来。
例:
的模板<input type="date" class="form-control">
<bootstrap-date-input
data-date-picker="activated"
class="date-picker-theme-dark"
>bootstrap-date-input>
如果不希望组件的根元素继承特性,可以在组件的选项中设置 inheritAttrs: false
。例如:
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
这尤其适合配合实例的 $attrs
属性使用,该属性包含了传递给一个组件的特性名和特性值,例如:
{
required: true,
placeholder: 'Enter your username'
}
有了 inheritAttrs: false
和 $attrs
,就可以手动决定这些特性会被赋予哪个元素。在撰写基础组件的时候是常会用到的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
`
})
show
是父组件中methods
中定义的方法名称,func
是子组件调用传递过来方法时候的方法名称。<div id="app">
<com2 @func="show">com2>
div>
this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用。
<div id="app">
<com2 @func="show">com2>
div>
<template id="tmp1">
<div>
<h1>这是子组件h1>
<input type="button" value="子组件的按钮,点击触发父组件传递过来的func方法" @click="myclick">
div>
template>
<script>
var com2 = {
template: '#tmp1',
data: function () {
return {
sonmsg: {name: '小头儿子', age: 6}
}
},
methods: {
myclick: function () {
// $emit第一个参数是父组件传过来的方法,后面的参数可选,为父组件传过来的方法传参
this.$emit('func', this.sonmsg);
}
}
}
var vm = new Vue({
el: '#app',
data: {
datamsgFormSon: null
},
methods: {
show: function (data) {
this.datamsgFormSon = data;
}
},
components: {
'com2': com2
}
});
script>
$emit
方法外,还提供了 $dispatch()
和 $boroadcast()
这两个方法。$dispatch()
用来向上级派发事件,只要是它的父级(一级或多级以上),都可以在Vue实例的events
选项内接收。$broadcast()
是由上级向下级广播事件的,用法与 $dispatch()
一致,只是方向相反。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
<div id="app">
{{message}}
<my-component>my-component>
div>
<script type="text/javascript" src="lib/vue.js">script>
<script type="text/javascript">
Vue.component('my-component', {
template: '',
methods: {
handleDispatch: function() {
this.$dispatch('on-message', '来自内部组件的数据');
}
}
});
var app = new Vue({
el: '#app',
data: {
message: ''
},
events: {
'on-message': function(msg) {
this.message = msg;
}
}
})
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
<div id="app">
{{message}}
<component-a>component-a>
div>
<script type="text/javascript" src="lib/vue.js">script>
<script type="text/javascript">
var bus = new Vue();
Vue.component('component-a', {
template: '',
methods: {
handleEvent: function() {
bus.$emit('on-message', '来自组件component-a的内容');
}
}
});
var app = new Vue({
el: '#app',
data: {
message: ''
},
mounted: function() {
var _this = this;
// 在实例初始化时,监听来自bus实例的事件
bus.$on('on-message', function(msg) {
_this.message = msg;
});
}
});
script>
body>
html>
在所有子组件中,其根实例可以通过 $root
属性进行访问。
推荐大项目还是使用Vuex来管理。
$parent
属性可以用来从一个子组件访问父组件的实例。
通过 ref
特性为子组件赋予一个 ID 引用(类似html的id)。
<h3 id="myh3" ref="myh3">我怎么这么好看,这么好看怎么办?h3>
this.$refs.ref名字 // 获取到该组件的DOM对象
$refs
只会在组件渲染完成之后生效,并且它们不是响应式的。应该避免在模板或计算属性中访问$refs
。
使用 $parent
属性无法很好的扩展到更深层级的嵌套组件上(比如突然加多一层父组件则又需要加多一层$parent
)。
依赖注入使用到两个选项:provide
和 inject
。
provide
选项允许指定想要提供给后代组件的数据/方法。
provide: function () {
return {
getMap: this.getMap
}
}
在任何后代组件里,可以使用 inject
选项来接收指定的想要添加在这个实例上的属性:
inject: ['getMap']
缺点:依赖注入将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
例:
// 定义一个混入对象
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!"
Vue.extend() 局部注册
当组件和混入对象含有同名选项时,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。(methods
、components
和 directives
都是如此)
例:
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
同名钩子函数将合并为一个数组,因此都将被调用。而且混入对象的钩子将在组件自身钩子之前调用。
var mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"