插槽 slot 在 2.6.0
版本中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
这两个目前已被废弃但未被移除且仍在文档中 的特性。新语法的由来可查阅这份 RFC 。
插槽内容 Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案 ,将 <slot>
元素作为承载分发内容的出口。
它允许你像这样合成组件:
1 <navigation-link url ="/profile" > Your Profile </navigation-link >
然后你在 <navigation-link>
的模板中可能会写为:
1 2 3 <a v-bind:href ="url" class ="nav-link" > <slot > </slot > </a >
当组件渲染的时候,<slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
1 2 3 4 5 <navigation-link url ="/profile" > <span class ="fa fa-user" > </span > Your Profile </navigation-link >
甚至其它的组件:
1 2 3 4 5 <navigation-link url ="/profile" > <font-awesome-icon name ="user" > </font-awesome-icon > Your Profile </navigation-link >
如果 <navigation-link>
没有 包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
了解更多
编译作用域 当你想在一个插槽中使用数据时,例如:
1 <navigation-link url ="/profile" > Logged in as {{ user.name }} </navigation-link >
该插槽跟模板的其它地方一样可以访问相同的实例属性 (也就是相同的“作用域”),而不能 访问 <navigation-link>
的作用域。例如 url
是访问不到的:
1 2 3 4 5 6 7 8 <navigation-link url ="/profile" > Clicking here will send you to: {{ url }} </navigation-link >
作为一条规则,请记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
了解更多
后备内容 有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中:
1 2 3 <button type ="submit" > <slot > </slot > </button >
我们可能希望这个 <button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot>
标签内:
1 2 3 <button type ="submit" > <slot > Submit</slot > </button >
现在当我在一个父级组件中使用 <submit-button>
并且不提供任何插槽内容时:
1 <submit-button > </submit-button >
后备内容“Submit”将会被渲染:
1 <button type ="submit" > Submit</button >
但是如果我们提供内容:
1 <submit-button > Save </submit-button >
则这个提供的内容将会被渲染从而取代后备内容:
1 <button type ="submit" > Save</button >
了解更多
具名插槽 自 2.6.0
起有所更新。已废弃的使用 slot
特性的语法在这里 。
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
1 2 3 4 5 6 7 8 9 10 11 <div class ="container" > <header > </header > <main > </main > <footer > </footer > </div >
对于这样的情况,<slot>
元素有一个特殊的特性:name
。这个特性可以用来定义额外的插槽:
1 2 3 4 5 6 7 8 9 10 11 <div class ="container" > <header > <slot name ="header" > </slot > </header > <main > <slot > </slot > </main > <footer > <slot name ="footer" > </slot > </footer > </div >
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
1 2 3 4 5 6 7 8 9 10 11 12 <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 >
现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。
然而,如果你希望更明确一些,仍然可以在一个 <template>
中包裹默认插槽的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <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 >
任何一种写法都会渲染出:
1 2 3 4 5 6 7 8 9 10 11 12 <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 >
注意 v-slot
只能添加在一个 <template>
上 (只有一种例外情况 ),这一点和已经废弃的 slot
特性 不同。
具名插槽
作用域插槽
自 2.6.0 起有所更新。已废弃的使用 slot-scope
特性的语法在这里 。
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的 <current-user>
组件:
1 2 3 <span > <slot > {{ user.lastName }}</slot > </span >
我们想让它的后备内容显示用户的名,以取代正常情况下用户的姓,如下:
1 <current-user > {{ user.firstName }} </current-user >
然而上述代码不会正常工作,因为只有 <current-user>
组件可以访问到 user
而我们提供的内容是在父级渲染的。
为了让 user
在父级的插槽内容中可用,我们可以将 user
作为 <slot>
元素的一个特性绑定上去:
1 2 3 <span > <slot v-bind:user ="user" > {{ user.lastName }} </slot > </span >
绑定在 <sloat>
元素上的特性被称为插槽 prop 。现在在父级作用域中,我们可以给 v-slot
带一个值来定义我们提供的插槽 prop 的名字:
1 2 3 4 5 <current-user > <template v-slot:default ="slotProps" > {{ slotProps.user.firstName }} </template > </current-user >
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps
,但你也可以使用任意你喜欢的名字。
了解更多
独占默认插槽的缩写语法 在上述情况下,当被提供的内容只有 默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
1 2 3 <current-user v-slot:default ="slotProps" > {{ slotProps.user.firstName }} </current-user >
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
1 <current-user v-slot ="slotProps" > {{ slotProps.user.firstName }} </current-user >
注意默认插槽的缩写语法不能 和具名插槽混用,因为它会导致作用域不明确:
1 2 3 4 5 6 7 <current-user v-slot ="slotProps" > {{ slotProps.user.firstName }} <template v-slot:other ="otherSlotProps" > slotProps is NOT available here </template > </current-user >
只要出现多个插槽,请始终为所有的 插槽使用完整的基于 <template>
的语法:
1 2 3 4 5 6 7 <current-user > <template v-slot:default ="slotProps" > {{ slotProps.user.firstName }} </template > <template v-slot:other ="otherSlotProps" > ... </template > </current-user >
了解更多
解构插槽 Prop 作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里:
1 2 3 4 function (slotProps ) { }
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件 或现代浏览器 ),你也可以使用 ES2015 解构 来传入具体的插槽 prop,如下:
1 <current-user v-slot ="{ user }" > {{ user.firstName }} </current-user >
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user
重命名为 person
:
1 <current-user v-slot ="{ user: person }" > {{ person.firstName }} </current-user >
你甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形:
1 2 3 <current-user v-slot ="{ user = { firstName: 'Guest' } }" > {{ user.firstName }} </current-user >
了解更多
动态插槽名
2.6.0 新增
动态指令参数 也可以用在 v-slot
上,来定义动态的插槽名:
1 2 3 <base-layout > <template v-slot: [dynamicSlotName ]> ... </template > </base-layout >
了解更多
具名插槽的缩写 跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
1 2 3 4 5 6 7 8 9 10 11 12 <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 >
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
1 2 <current-user #="{ user }" > {{ user.firstName }} </current-user >
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
1 <current-user #default ="{ user }" > {{ user.firstName }} </current-user >
了解更多
其它示例 插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。 这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
例如,我们要实现一个 <todo-list>
组件,它是一个列表且包含布局和过滤逻辑:
1 2 3 <ul > <li v-for ="todo in filteredTodos" v-bind:key ="todo.id" > {{ todo.text }}</li > </ul >
我们可以将每个 todo 作为父级组件的插槽,以此通过父级组件对其进行控制,然后将 todo
作为一个插槽 prop 进行绑定:
1 2 3 4 5 6 7 8 9 10 11 12 <ul > <li v-for ="todo in filteredTodos" v-bind:key ="todo.id" > <slot name ="todo" v-bind:todo ="todo" > {{ todo.text }} </slot > </li > </ul >
现在当我们使用 <todo-list>
组件的时候,我们可以选择为 todo 定义一个不一样的 <template>
作为替代方案,并且可以从子组件获取数据:
1 2 3 4 5 6 <todo-list v-bind:todos ="todos" > <template v-slot:todo ="{ todo }" > <span v-if ="todo.isComplete" > ✓</span > {{ todo.text }} </template > </todo-list >
这只是作用域插槽用武之地的冰山一角。想了解更多现实生活中的作用域插槽的用法,我们推荐浏览诸如 Vue Virtual Scroller 、Vue Promised 和 Portal Vue 等库。
了解更多
过度动画 transition
css 动画库 animate.css
JavaScript 动画库 Velocity.js
概念 1. 实现条件 :在需要有过渡效果的标签外面添加<transition>
。也就是说 vue 中被<transition>
包裹的元素才能实现过渡效果。
2. 注意 类名的说明: 对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
。opacity 的说明: 在过渡动画的显示和隐藏会用 opacity 属性来表征。opacity: 1 不透明(显示) | opacity: 0 透明(不显示)
3. 原理 vue 在加了<transition>
标签的元素提供了三个进入过渡的类,和三个离开过渡的类。
进入过渡
离开过渡
.v-enter
:定义进入过渡的开始状态
.v-leave
:定义离开过渡的开始状态
.v-enter-to
:定义进入过渡的结束状态
.v-leave-to
:定义离开过渡的结束状态
.v-enter-active
:定义进入过渡生效时的状态
.v-leave-active
:定义离开过渡生效时的状态
4. 这 6 个类的生效时间 对于进入 动画来说:
.v-enter
. v-enter-to
. v-enter-active
定义过渡的开始状态
定义过渡的结束状态
定义过渡生效时的状态
插入之前生效
插入之后下一帧生效(同时 v-enter 被移除)
动画整个过程生效
下一帧被移除
动画完成之后移除
动画整个过程生效
对于离开 动画来说:
.v-leave
.v-leave-to
.v-leave-active
定义过渡的开始状态
定义过渡的结束状态
定义过渡生效时的状态
离开过渡被触发时立刻生效
触发之后下一帧生效 (与此同时 v-leave 被删除)
整个离开过渡的阶段中应用
下一帧被移除
动画完成之后移除
整个离开过渡的阶段中应用
5. 原理图
代码示例 定义一个长方体 div,在点击按钮显示时,让高度从 0 增长到 200px。切换隐藏时,让高度从 200px 减少到 0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 //一个命名为fade的<transition > 标签包裹着类名为h的<div > <transition name ="fade" > <div class ="h" v-if ="show" > hello world</div > </transition > <style > .h { width :100px ; height : 200px ; background-color : aqua; } ------------------------------------------------------------------------------------- .fade-enter-active , .fade-leave-active { transition : height 5s ; } ------------------------------------------------------------------------------------- .fade-enter { height : 0 ; } .fade-enter-to { height : 200px ; } ------------------------------------------------------------------------------------- .fade-leave { height : 200px ; } .fade-leave-to { height : 0 ; } </style >
了解更多
多个元素过渡 我们之后讨论多个组件的过渡 ,对于原生标签可以使用 v-if
/v-else
。最常见的多标签过渡是一个列表和描述这个列表为空消息的元素:
1 2 3 4 5 6 <transition > <table v-if ="items.length > 0" > </table > <p v-else > Sorry, no items found.</p > </transition >
可以这样使用,但是有一点需要注意:
当有相同标签名 的元素切换时,需要通过 key
特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition>
组件中的多个元素设置 key 是一个更好的实践。
示例:
1 2 3 4 <transition > <button v-if ="isEditing" key ="save" > Save</button > <button v-else key ="edit" > Edit</button > </transition >
在一些场景中,也可以通过给同一个元素的 key
特性设置不同的状态来代替 v-if
和 v-else
,上面的例子可以重写为:
1 2 3 <transition > <button v-bind:key ="isEditing" > {{ isEditing ? 'Save' : 'Edit' }}</button > </transition >
使用多个 v-if
的多个元素的过渡可以重写为绑定了动态属性的单个元素过渡。例如:
1 2 3 4 5 <transition > <button v-if ="docState === 'saved'" key ="saved" > Edit</button > <button v-if ="docState === 'edited'" key ="edited" > Save</button > <button v-if ="docState === 'editing'" key ="editing" > Cancel</button > </transition >
可以重写为:
1 2 3 4 5 6 <transition > <button v-bind:key ="docState" > {{ buttonMessage }}</button > </transition > // ... computed: { buttonMessage: function () { switch (this.docState) { case 'saved': return 'Edit' case 'edited': return 'Save' case 'editing': return 'Cancel' } } }
了解更多
过渡模式 在两个元素的过渡中,两个元素都被重绘了,一个离开过渡的时候另一个开始进入过渡。这是 <transition>
的默认行为 - 进入和离开同时发生。
解决方法:
组件过渡 多个组件的过渡简单很多 - 我们不需要使用 key
特性。相反,我们只需要使用动态组件 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > 组件过渡</title > <style > .component-fade-enter-active , .component-fade-leave-active { transition : opacity 0.3s ease; } .component-fade-enter , .component-fade-leave-to { opacity : 0 ; } </style > <script src ="/lib/vue/dist/vue.js" > </script > </head > <body > <div id ="transition-components-demo" > <button @click ="view = 'v-a'" > a</button > <button @click ="view = 'v-b'" > b</button > <transition name ="component-fade" mode ="out-in" > <component v-bind:is ="view" > </component > </transition > </div > <script > new Vue ({ el : "#transition-components-demo" , data : { view : "v-a" , }, components : { "v-a" : { template : "<div>Component A</div>" , }, "v-b" : { template : "<div>Component B</div>" , }, }, }); </script > </body > </html >
了解更多
列表过度 列表的进入/离开过渡 现在让我们由一个简单的例子深入,进入和离开的过渡使用之前一样的 CSS 类名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > 列表过度</title > <style > .list-item { display : inline-block; margin-right : 10px ; } .list-enter-active , .list-leave-active { transition : all 1s ; } .list-enter , .list-leave-to { opacity : 0 ; transform : translateY (30px ); } </style > <script src ="/lib/vue/dist/vue.js" > </script > </head > <body > <div id ="list-demo" class ="demo" > <button v-on:click ="add" > Add</button > <button v-on:click ="remove" > Remove</button > <transition-group name ="list" tag ="p" > <span v-for ="item in items" v-bind:key ="item" class ="list-item" > {{ item }} </span > </transition-group > </div > <script > new Vue ({ el : "#list-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 ); }, }, }); </script > </body > </html >
这个例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,我们下面会解决这个问题。
了解更多
列表的排序过渡 <transition-group>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move
特性 ,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name
属性来自定义前缀,也可以通过 move-class
属性手动设置。
示例 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > 列表过度</title > <style > .list-item { display : inline-block; margin-right : 10px ; } .list-enter-active , .list-leave-active { transition : all 1s ; } .list-enter , .list-leave-to { opacity : 0 ; transform : translateY (30px ); } .list-leave-active { position : absolute; } .list-move { transition : all 1s ; } </style > <script src ="/lib/vue/dist/vue.js" > </script > </head > <body > <div id ="list-demo" class ="demo" > <button v-on:click ="add" > Add</button > <button v-on:click ="remove" > Remove</button > <transition-group name ="list" tag ="p" > <span v-for ="item in items" v-bind:key ="item" class ="list-item" > {{ item }} </span > </transition-group > </div > <script > new Vue ({ el : '#list-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 ) }, } }) </script > </body > </html >
示例 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <title > 列表过度-交错过渡</title > <link rel ="stylesheet" href ="/lib/animate/animate.css" /> <style > .abs { position : absolute; } .myAni { transition : all 1s ; } li { display : table; } </style > <script src ="/lib/vue/dist/vue.js" > </script > </head > <body > <div id ="box" > <input type ="text" v-model ="text" /> <transition-group tag ="ul" enter-active-class ="animated fadeInLeft" leave-active-class ="animated fadeOutLeft abs" move-class ="myAni" > <li v-for ="data in querylist" :key ="data" > {{ data }}</li > </transition-group > <p > {{ querylist }}</p > </div > <script > new Vue ({ el : "#box" , data : { text : "" , list : [ "asdfffffsafsdf" , "asdjfklasjdflk" , "fjkfjklsjfkdaf" , "fdjslfjlkasfsf" , "ffjlajsdfoisff" , ], }, computed : { querylist : function ( ) { return this .list .filter ( (item ) => item.toLowerCase ().indexOf (this .text .toLowerCase ()) !== -1 ); }, }, }); </script > </body > </html >
需要注意的是使用 FLIP 过渡的元素不能设置为 display: inline
。作为替代方案,可以设置为 display: inline-block
或者放置于 flex 中
了解更多
把过渡放到组件里 管理太多的状态过渡会很快的增加 Vue 实例或者组件的复杂性,幸好很多的动画可以提取到专用的子组件。 我们来将之前的示例改写一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 <script src ="https://cdn.jsdelivr.net/npm/tween.js@16.3.4" > </script > <div id ="example-8" > <input v-model.number ="firstNumber" type ="number" step ="20" /> + <input v-model.number ="secondNumber" type ="number" step ="20" /> = {{ result }} <p > <animated-integer v-bind:value ="firstNumber" > </animated-integer > + <animated-integer v-bind:value ="secondNumber" > </animated-integer > = <animated-integer v-bind:value ="result" > </animated-integer > </p > </div > <script > Vue .component ("animated-integer" , { template : "<span>{{ tweeningValue }}</span>" , props : { value : { type : Number , required : true , }, }, data : function ( ) { return { tweeningValue : 0 , }; }, watch : { value : function (newValue, oldValue ) { this .tween (oldValue, newValue); }, }, mounted : function ( ) { this .tween (0 , this .value ); }, methods : { tween : function (startValue, endValue ) { var vm = this ; function animate ( ) { if (TWEEN .update ()) { requestAnimationFrame (animate); } } new TWEEN .Tween ({ tweeningValue : startValue }) .to ({ tweeningValue : endValue }, 500 ) .onUpdate (function ( ) { vm.tweeningValue = this .tweeningValue .toFixed (0 ); }) .start (); animate (); }, }, }); new Vue ({ el : "#example-8" , data : { firstNumber : 20 , secondNumber : 40 , }, computed : { result : function ( ) { return this .firstNumber + this .secondNumber ; }, }, }); </script >
我们能在组件中结合使用这一节讲到各种过渡策略和 Vue 内建的过渡系统 。总之,对于完成各种过渡动效几乎没有阻碍。
了解更多
自定义指令 指令定义 全局指令:
1 2 3 4 5 6 7 8 Vue .directive ("focus" , { inserted : function (el ) { el.focus (); }, });
局部指令:
1 2 3 4 5 6 7 8 9 directives : { focus : { inserted : function (el ) { el.focus () } } }
钩子函数 一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前 。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind
:只调用一次,指令与元素解绑时调用。
钩子函数参数 指令钩子函数会被传入以下参数:
el
:指令所绑定的元素,可以用来直接操作 DOM 。
binding
:一个对象,包含以下属性:
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 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
oldVnode
:上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用。
示例:
html:
1 <div id ="hook-arguments-example" v-demo:foo.a.b ="message" > </div >
js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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,text,elm,ns,context,fnContext,fnOptions,fnScopeld,key, componentOptions,componentinstance,parent,raw,isStatic,isRootlnsert,isComment, isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder
动态指令参数 示例:
html:
1 2 3 4 <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 >
js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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" , }; }, });
函数简写 在很多时候,你可能想在 bind
和 update
时触发相同行为,而不关心其它的钩子。比如这样写:
1 2 3 Vue .directive ("color-swatch" , function (el, binding ) { el.style .backgroundColor = binding.value ; });
对象字面量 如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
1 2 3 4 5 6 7 8 <div v-demo ="{ color: 'white', text: 'hello!' }" > </div > <script > Vue .directive ("demo" , function (el, binding ) { console .log (binding.value .color ); console .log (binding.value .text ); }); </script >
了解更多
过滤器 过滤器可以用在两个地方:
双花括号插值
v-bind
表达式 (从 2.1.0+ 版本后开始支持)
示例 全局过滤器:
1 2 3 4 5 6 7 8 9 Vue .filter ("capitalize" , function (value ) { if (!value) return "" ; value = value.toString (); return value.charAt (0 ).toUpperCase () + value.slice (1 ); }); new Vue ({ });
组件内定义过滤器:
1 2 3 4 5 6 7 8 filters : { capitalize : function (value ) { if (!value) return '' value = value.toString () return value.charAt (0 ).toUpperCase () + value.slice (1 ) } }
过滤器串联/传参 示例:
1 {{ message | filterA | filterB }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
1 {{ message | filterA('arg1', arg2) }}
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。
路由 router <router-link>
相关属性接下来我们可以了解下更多关于 <router-link>
的属性。
to 表示目标路由的链接。 当被点击后,内部会立刻把 to
的值传到 router.push()
,所以这个值可以是一个字符串或者是描述目标位置的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <router-link to ="home" > Home</router-link > <a href ="home" > Home</a > <router-link v-bind:to ="'home'" > Home</router-link > <router-link :to ="'home'" > Home</router-link > <router-link :to ="{ path: 'home' }" > Home</router-link > <router-link :to ="{ name: 'user', params: { userId: 123 }}" > User</router-link > <router-link :to ="{ path: 'register', query: { plan: 'private' }}" > Register</router-link>
replace 设置 replace
属性的话,当点击时,会调用 router.replace()
而不是router.push()
,导航后不会留下 history
记录。
1 <router-link :to ="{ path: '/abc'}" replace > </router-link >
append 设置 append
属性后,则在当前 (相对) 路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append
,则路径为 /b ,如果配了,则为 /a/b
1 <router-link :to ="{ path: 'relative/path'}" append > </router-link >
tag 有时候想要 <router-link>
渲染成某种标签,例如 <li>
。 于是我们使用 tag
,prop
类指定何种标签,同样它还是会监听点击,触发导航。
1 2 3 <router-link to ="/foo" tag ="li" > foo</router-link > <li > foo</li >
active-class 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。
1 2 3 4 5 6 7 8 9 10 11 12 13 <style > ._active { background-color : red; } </style > <p > <router-link v-bind:to ="{ path: '/route1'}" active-class ="_active" > Router Link 1</router-link > <router-link v-bind:to ="{ path: '/route2'}" tag ="span" > Router Link 2</router-link > </p >
注意这里 class 使用 **active_class=”_active”**。
exact-active-class 配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
1 2 3 4 5 6 7 8 <p > <router-link v-bind:to ="{ path: '/route1'}" exact-active-class ="_active" > Router Link 1</router-link > <router-link v-bind:to ="{ path: '/route2'}" tag ="span" > Router Link 2</router-link > </p >
event 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
1 2 3 <router-link v-bind:to ="{ path: '/route1'}" event ="mouseover" > Router Link 1</router-link>