Vue快速上门(2)-模板语法
VUE家族系列:
01、模板语法
1.1、template模板
<template>
是H5新增的模板元素,是一个用于HTML模板内容的包装元素,主要使用其内部的内容。在普通的HTML页面中,模板会出现在Dom树中,但不会渲染,里面的请求、脚本也不会执行。<template>
是Vue里主要的模板定义方式,除此以外,常用的定义Vue.template
方式:
Vue.template | 描述 | 示例 |
---|---|---|
<template> 模板 |
H5的模板元素,其内容作为模板内容, | <template id="tp"> |
<script> 模板 |
在一个 <script> 元素中定义模板内容,里面的内容都会被当成字符串。通过#id 引用 |
<script type="text/x-template" id="xtp"> |
字符模板 | 用HTML字符串直接定义模板内容 | template: '<p>{{name}}</p>' |
内联模板inline-template |
用在子组件上,用这个子组件元素里面的内容作为模板,而不是渲染他的原本的内容。这个一般不常用吧,作用域有点乱。 | <mycom inline-template> </mycom> |
📢Vue的
template
选项值:
- HTML语法:模板内容就是普通的HTML语法,Vue新增了一些绑定数据的指令。
- 根元素:内部都必须有一个根元素,
<template>
本身是没啥用的,主要用的是其内部InnerHTML。- #值:如果值以 # 开始,则它将被用作
id
选择符,并使用匹配元素的 innerHTML 作为模板。- 优先于el:根据上文中Vue生命周期流程可知,
template
内容会优先于el.outerHTML
内容,编译为渲染render。
<div id="app5">
<h2>app</h2>
<template>
<p>vue中的template的innerHtml会被正常渲染</p>
</template>
<user-com inline-template>
<p>内联模板,替代原有内容</p>
</user-com>
</div>
<script>
let app5 = new Vue({
el: "#app5",
template: '#tp', //<template id="tp">模板
template: '#xtp', //<script type="text/x-template" id="xtp">模板
template: '<p>{{mes}}--字符模板</p>', //字符模板
template: '<p>{{mes}}</p><span>❌</span>', //❌报错:Component template should contain exactly one root element
data: { mes: "message:hello!" },
components: {
'user-com': {
data: function () { return { name: "same" } },
template: '<p>{{name}}-这是一个子组件</p>',
}
}
})
</script>
1.2、文本插值
Vue的模板语法基于HTML的语法,可以在模板中申明式的绑定实例数据、事件方法。在Vue中,模板被编译成虚拟Dom渲染函数,先在虚拟Dom上进行操作,这样可以最优化Dom及操作次数,然后再渲染到正式的Dom中。常用的一种数据绑定方式为—— 文本插值。
💠文本插值:{{ data }}
,“Mustache”语法 (双大括号) (/ˈmʌstæʃ; məˈstɑːʃ/ 胡子),内容不支持html标签、不支持绑定。
📢注意:安全第一,不要用输入的内容来插值,容易导致XSS攻击。
<div id="app3">
<div>div1:{{html}}
<p>{{mes}}</p>
<p>{{birthday}}</p>
</div>
<div v-text="html">div2:</div>
<div v-html="html">div3:</div>
</div>
<script>
let vm2 = new Vue({
el: "#app3",
data: {
html: "<span style='color:red'>red span{{mes}}</span>",
mes: "hello world!",
birthday: '2000-12-11',
},
});
</script>
1.3、JavaScript表达式
在{{文本插值}}
、v-bind
绑定、v-on
事件绑定等指令中都支持完全的JavaScript的表达式,只能是单个表达式语句,不支持复杂语句、循环控制。
<div id="app3">
<div>
<p v-bind:style="'color:'+fcolor">{{mes.split(' ').join('---')}}</p>
<p>age:{{(new Date().getFullYear() - new Date(birthday).getFullYear())}}</p>
<p>age:{{((new Date() - new Date(birthday))/3600/24/365/1000).toFixed(1)}}</p>
</div>
</div>
<script>
let vm2 = new Vue({
el: "#app3",
data: {
mes:"hello world!",
birthday:'2000-12-11',
fcolor:'red',
},
});
</script>
02、模板指令
2.1、指令大全🔖
指令格式:指令:参数.修饰符 = "值"
Vue指令/ 简写 | 描述 | 示例 |
---|---|---|
v-text= | 绑定 textContent,同{{文本插值}}(如JS延迟,有闪烁) | <span v-text="msg"></> = <span>{{msg}}</> |
v-html= | 绑定 innerHTML,内容支持html标签(不支持绑定),需注意安全性。其他插值指令的值都不支持HTML标签,会被转义。 | <div v-html="html"></div> |
v-bind: = / := | 绑定元素属性值,动态地绑定一个或多个 attribute,或组件prop |
<img v-bind:src="img" :class="imgStyle"> |
v-model= | 表单元素的值双向绑定,不支持表达式 | <input type="date" v-model="birthday"> |
v-show= | 值为真元素显示(切换 display ),适用于频繁切换显示。 |
<div v-show="value =='方案1'">方案1</div> |
v-if= | 值为真才渲染,否则不会创建或销毁已有元素,支持<template> |
<img v-if="growth > 0" alt="上升"> |
v-else-if= | v-if 的else if 块,可连续使用,跟在v-if/v-else-if 后面 |
<img v-else-if="growth < 0" alt="下降"> |
v-else | v-if 或者 v-else-if 添加的“else 块”,无参数 |
<img v-else alt="稳定"> |
v-for= | 列表渲染,item in/of expression ,支持index索引、对象 |
<li v-for="item in items">{{item}}</li> |
v-on: = / @= | 绑定事件,参数为事件名;值为方法名,或内联语句func($event) | <button v-on:click="submit" @click=""> |
:key= | 元素唯一key 值,配合v-for 使用,避免重复key更新异常,用于虚拟Dom中判断新旧元素。或用于强制元素替换而不复用 |
ul v-for="item in list" :key="item.id"> <i:key="text">{{text}}</i> //每次都是新元素 |
v-once | 只更新元素/组件一次,后续更新都视为静态内容,不再更新 | <p v-text="message" v-once></p> |
v-pre | 不编译,跳过这个元素和它的子元素的编译过程,显示原生的内容 | <div v-pre> </div> |
is | 动态的绑定一个组件,值为组件名称/Vue选项对象 | <component is="user-info"></component> |
v-slot:= / #:= | 具名插槽,有名分的插槽。指定插槽名称,或绑定插槽ViewModel | v-slot:header v-solt="{user}" |
ref | 给子组件、Dom元素注册引用名,通过$refs 访问这些被ref 标记的对象 |
组件上的ref 指向其组件Vue实例<div ref="left"> |
v-cloak | 解决{{文本插值} }会闪烁的问题,锁定[v-cloak] 样式直到编译完后清除 |
[v-cloak]{ display: none;} <div v-cloak> |
2.2、[动态参数]
2.6.0新增的[动态参数],指令的参数可以使用[动态参数]
=一个JavaScript表达式。
<a v-on:[functype]="setStyle" v-bind:[href]="1+1">方案1</a>
- 字符串值:这里的动态参数预期结果为一个字符串,异常则为
null
,null
也可用于显示的移除绑定。 - 不要出现引号,包括
空格
、引号、<
、>
、/
、=
,HTML的属性中是非法的。 - 参数名用小写,不要用大写,浏览器会强制转换HTML属性为小写。如
<a v-on:[funcType]="fswitch">方案1</a>
包含大写的属性参数会找不到对应值。
<div id="app">
<img v-bind:[getProp(file.type)]="file.url" alt="file" v-on:[eventType]="eventHandler">
<img v-bind:[getprop1]="file.url" alt="file" v-on:[eventType]="eventHandler">
</div>
<script>
let vm2 = new Vue({
el: "#app",
data: {
file: { type: 'img', url: '../../res/bg.gif' },
eventType: 'click', //大写会找不到,报错:Property or method "eventtype" is not defined
eventtype: 'click',
},
computed:{ getprop1(){ return 'src'; } },
methods: {
getprop(type) { //如果有大写(getProp)就会找不到报错:Property or method "getprop" is not defined
switch (type) {
case 'img': return 'src';
case 'url': return 'href';
}
},
eventHandler(e) { console.log(e.target.tagName); }
}
});
</script>
2.3、v-for 列表渲染
用循环创建多个元素/组件,循环对象可以是数组、对象、常量,也可以是计算属性、方法、表达式。循环表达式中可以用in
,也可用of
(没有区别)。如果循环创建多个元素没有根元素,则可用一个模板<template>
来包裹,这时就不需要key了。
- 列表循环:
v-for="item in items"
- 列表循环-带索引参数:
v-for="(item,index) in items"
- 对象循环-value:
v-for="value in user"
,Vue是按照Object.keys(obj)
的结果遍历。 - 对象循环-带参数:
v-for="(value,name,index) in user"
<div id="app">
<span v-for="n of 20">{{ n }} </span>
<ul>
<li><span v-for="item in items">{{item.name}} ; </span></li>
<li><span v-for="(item,index) in items">{{index+1}}:{{item.name}} ; </span></li>
<!--user对象-->
<li><span v-for="value in user">{{value}} ; </span></li>
<li><span v-for="(value,name,index) in user"><i>{{index+1}}</i>)<b>{{name}}</b>:{{value}} ; </span></li>
</ul>
<br>
</div>
<script>
let vm2 = new Vue({
el: "#app",
data: {
items: [{ name: 'sam' }, { name: 'zhangsan' }],
user: { name: 'sam', age: 20, birthday: '2000-12-11' }
},
});
</script>
🔸列表更新策略(:key):Vue默认列表项原地更新,不管数据的顺序。
如果希望更新时保持数据、元素的顺序,及更新的准确性、高性能,则需要给元素设置一个唯一身份标识 key:v-bind:key="kid" :key="kid"
,值应为字符、数字。
Vue默认是最大限度复用元素,虚拟Dom中用key
来判断新旧元素,重复key
可能会导致更新异常,也可用于强制元素替换而不复用。一般推荐尽量提供key
,他是Vue识别节点的通用机制(diff中的元素比较)。在<transition-group>
列表动画中,key
是必须的。
📢 注意:
v-for
循环中的参数的顺序须一致,别名不重要。v-for
优先级高于v-if
,共用时需注意,可用来过滤不符合条件的项。
2.4、class样式绑定
class
、style
为HTML原本的attribute
,通过v-bind
绑定,增强:
- 对象绑定,语法:
{classA:bindbool, classB:bindbool}
,绑定值bindbool
为真则样式classA有效,方便基于判断绑定多个class。除了直接对象表达式,还可以是绑定的对象、计算属性返回的对象、普通表达式。 - 数组绑定,绑定多个样式,数组中可以是绑定值、表达式、常量样式名。
- 支持和原生class共存,会合并
class
值。包括组件也是如此,和组件的根元素class
合并。
🌰一个tab切换示例:
<style>
#app5 ul { list-style-type: none; margin: 0; padding: 0; text-align: center; }
#app5 ul li { display: inline-block; margin: 0 10px; }
#app5 ul li.active { background-color: antiquewhite; }
.psection {
display: none; background-color: antiquewhite;
text-align: center; margin: 0; line-height: 50px;
}
.psection.active { display: block; }
</style>
<div id="app5">
<ul>
<li v-for="item in items" v-on:click="liclick(item)" v-bind:class="{active:item.isActive}">{{item.title}}</li>
</ul>
<div>
<p v-for="item in items" v-bind:class="['psection',item.isActive?'active':'']">{{item.content}}</p>
<!-- 也可以用v-show实现切换 v-show="item.isActive" -->
<p v-for="item in items" v-show="item.isActive">{{item.content}}</p>
</div>
</div>
<script>
let app5 = new Vue({
el: "#app5",
data: {
items: [{ title: "显示1", content: "1", isActive: false }
, { title: "显示2", content: "2", isActive: true }
, { title: "显示3", content: "3", isActive: false }],
},
methods: {
liclick: function (item) {
this.items.forEach(item => item.isActive = false);
item.isActive = true;
},
}
})
</script>
2.5、style内联样式绑定
对于Style的绑定,Vue直接把style对象化了,通过对象化表达式,或者直接一个对象绑定(更清晰)。
- 对象数组:支持多个对象的数组,合并对象中的style样式规则。
- 支持一定的兼容性前缀,如
transition
,Vue会自动检测浏览器并添加兼容性前缀。 - 对象属性多重值,提供多个值的数组
display:['-ms-flexbox','flex']
,Vue只会渲染数组中最后一个被浏览器支持的值。
<div id="app5">
<div>
<!--数组+对象混合-->
<p :style="[{color:activeColor,'font-size':'20px'},marginStyle]"> p1 - Content </p>
<p v-bind:style="marginStyle"> p2 - Content </p> <!--对象-->
</div>
</div>
<script>
let app5 = new Vue({
el: "#app5",
data: {
activeColor: 'red',
marginStyle: { margin: "30px", transform: "rotate(10deg)", display: ['-ms-flexbox', 'flex'] },
},
})
</script>
2.6、v-on/@事件.修饰符
事件绑定格式:v-on:/@ 事件名.修饰符 = ""
,@
为v-on:
的缩写形式。事件名支持动态 [参数] 绑定。Vue的事件都是直接绑定到元素的,没有用事件委托。
事件参数:方法默认支持事件参数对象Event,内联JavaScript代码通过$event
访问事件对象。
<div id="app7">
<!-- 只响应一次 + 右键点击 -->
<button @click.once.right="arrClick">button</button>
<li v-for="n in arr"><button v-on:click="arrClick($event,n)">{{n}}</button></li>
<!-- 左方向按键,数值增1 -->
<input type="text" v-on:keydown.preven.arrow-left="num=parseInt(num)+1" v-model="num">
</div>
<script>
let app7 = new Vue({
el: "#app7",
data: { arr: [1, 2, 3], num: 0 },
methods: {
arrClick: function (e, arg) { console.log(e?.target.tagName, arg ? arg : "") },
}
})
</script>
🔸修饰符 可以加强事件的能力,使用上可以串联(注意顺序),也可以只有修饰符。
修饰符 | 描述 |
---|---|
.stop | 调用 event.stopPropagation() ,停止向上冒泡(propagation /ˌprɒpəˈɡeɪʃn/ 传播) |
.preven | 调用 event.preventDefault() ,取消默认事件行为,如checkbox、<a> 的默认事件行为,不影响冒泡 |
.self | 只当事件是从侦听器绑定的元素本身触发时才触发回调,只能自身触发,内部冒泡事件的不会响应。 |
.once | 只触发一次回调 |
.capture | 添加事件侦听器时使用 capture (捕获)模式,事件流的捕获阶段就触发事件。 |
.passive | (2.3.0) 以 { passive: true } 模式添加侦听器,passive 设为 true 可以启用性能优化,主要是针对滚动、触摸相关事件。参考MDN:使用 passive 改善滚屏性能,(passive /ˈpæsɪv/被动,消极)。 |
.native | 监听组件根元素的原生事件 |
鼠标键修饰符 | .left :只当点击鼠标左键时触发。.right :鼠标右键触发;.middle :鼠标中键触发 |
辅助键修饰符 | 配合鼠标、按键事件使用:ctrl、shift、alt、meta(windows键,或command) |
键盘事件修饰符 | 针对事件keydown 、keyup 、keypress 按键修饰符:● esc、tab、space、enter、delete、up、down、left、right、a、b、c字母键等。 ● .{keyCode | keyAlias} : 按键修饰符,只当事件是从特定键触发时才触发回调。可使用 KeyboardEvent.key值转换为 kebab-case 来格式使用。 |
2.7、v-model表单绑定
使用v-model
在表单元素<input>
、<textarea>
及 <select>
上创建双向绑定,会忽略他们本身的值属性(value、checked、selected),所以注意设置默认值。分组的单选、多选元素组按照name
进行分组即可,一组的绑定也是一样的。
基本原理就是监听表单的输入事件,实现视图到数据的同步:
text
和textarea
元素使用value property
和input
事件。checkbox
和radio
使用checked property
和change
事件。name
用于分组命名空间,value
作为选中的值。select
字段将value
作为 prop 并将change
作为事件。
🔸v-model
修饰符:
.lazy
:change
事件同步数据,主要针对text
类表单元素,失去焦点时才会触发change
事件。.number
:强制转换值为数值类型,需配合type="number"
使用,如果无法parseFloat()
解析则返回原始值。.trim
:去掉首尾空格,比较实用!
<div id="app8">
<div>
<label>姓名:<input type="text" v-model.lazy.trim="name"></label>
<label>姓名:<input type="number" v-model.number="age">{{age}}</label>
</div>
<div>
<span>性别:</span>
<template v-for="(value,name) in dataset.sex">
<label><input type="radio" v-model.lazy="sex" name="sex" :value="name">{{value}}</label>
</template>
<i>结果:{{sex}}</i>
</div>
<div>
<span>技能:</span>
<label v-for="item in dataset.skill"><input type="checkbox" v-model="skill" :value="item"
name="skill">{{item}}</label>
<i>结果:{{skill}}</i>
</div>
<div>
<span>性别-选择框:</span>
<select name="ssex" v-model="sex">
<option disabled value="">请选择</option>
<option v-for="(value,name) of dataset.sex" v-once :value="name">{{value}}</option>
</select>
<i>结果:{{sex}}</i>
</div>
<div>
<span>技能-选择框:</span>
<select name="sskill" v-model="skill" multiple>
<option disabled value="">请选择</option>
<option v-for="item in dataset.skill" v-once :value="item">{{item}}</option>
</select>
<i>结果:{{skill.join()}}</i>
</div>
</div>
<script>
let app8 = new Vue({
el: "#app8",
data: {
name: '', age: 0, checked: false, sex: '', skill: ["开机"],
dataset: {
sex: { male: '男', female: "女", other: '其他' },
skill: ['开机', '关机', '写Bug', '吃饭'],
}}
})
</script>
2.8、关于数据丢失
基于Vue对数据监听的机制,Vue 不能检测数组和对象的变化。因此有些场景数据无法实现变更监听,就无法同步视图了,造成“数据丢失”的现象。
- 数组通过索引修改、新增的值无法监听。
- 未初始化的新成员,添加的对象新成员,没有被初始化时监听,
app1.user.新属性=20
。 - 被冻结的对象无法监听:
Object.freeze(obj)
。 v-model
绑定的未定义属性不丢失!因为他是用的$set
去更新的,注意不能是data的一级属性。
🟢解决办法:
-
数组整体赋值:新数组赋值,或者用数组方法修改数据,如push方法是被Vue代理实现了拦截的。
-
在Vue初始化时先申明属性。
-
vm.$set.(obj)
强制更新,会先添加响应,然后再更新视图。不建议动态添加data的一级属性!在选项中预先申明。好像是一些边界方面的技术原因。
<div id="app">
<p>p1:
<span v-for="n in arr">{{n}},</span>
</p>
<p>p2:
<b>name:</b>{{user.name}},
<b>age:</b>{{user.age}}
</p>
</div>
<script>
let app = new Vue({
el: '#app',
data: { arr: [1, 2, 3], user: { name: 'sam' }, }
})
app.arr[0] = 100; //修改,无更新
app.arr[app.arr.length] = 4; //新增,无更新
app.$set(app.arr,0,101); //强制更新
app.arr.push(5); //push有效,前面的也生效了,因为触发了更新
app.arr = [100, 2, 3]; //重新赋值数组,有效
app.arr = Array.of(...Array.from(app.arr),10,10); //重新赋值数组,有效
app.user.age = 10; //这是新的属性,无更新
app.$set(app.user,'age',18); //强制更新,有效
app.user = Object.assign({}, app.user);
app.user = user; //赋值对象,有效
</script>
03、Vue动画
Vue提供了两个内置元素,<transition>
和<transition-group>
,用来包装单个元素、列表元素,辅助实现在元素显示隐藏、创建删除、移动过程中的过渡动画效果。更高级、更复杂的动画可以借助第三方动画组件来实现。
- Css动画库 Animate.css,
- Js动画库 anime.js (github star最高),Velocity.js,GreenSock
3.1、transition过渡动画
Vue提供了一个<transition>
封装组件,用来包装需要动画的单个内容。Vue主要提供了一个比较基础的动画机制,帮你处理过渡的事件、动画类调用。所以还是需要你自己来写动画的CSS类,或者动画JS代码。
- 三种类型的过渡:元素初始渲染过渡、元素进入过渡(显示)、元素离开过渡(隐藏/删除)。
- 包裹单元素:
<transition>
只能包裹一个跟元素(包括v-if、v-show切换),动画应用在这个跟元素上,不会产生额外Dom元素。 - 过河拆桥--事后清理:动画完成后,CSS动画资源会被清除,Vue会自动监听
transitionend
,或animationend
事件。 - 过渡CSS类名规范:如下表格,按照CSS类名写样式,Vue自动调用。
v
为动画组件<transition>
的name
值,没有则默认v
。 - CSS过渡类attribute:作用同上,值是Class样式类名,优先级更高。CSS值可以自定义,也可以很方便的调用第三方CSS动画库。
进入过渡 | 离开过渡 | CSS过渡类attribute,优先级更高 |
---|---|---|
v-enter:初始点状态 | v-leave:初始点状态 | enter-class、leave-class |
v-enter-to:结束点状态 | v-leave-to:结束点状态 | enter-to-class、leave-to-class |
v-enter-active:进入过程,设置动画参数 | v-leave-active:离开过程 | enter-active-class、leave-active-class |
🌰过渡动画示例:CodePen
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet">
<div id="app15">
<div>
<button v-on:click="acitve = !acitve" ref="btn">消失</button>
<transition name="h" v-on:after-enter="updateBtn" v-on:after-leave="updateBtn">
<p v-show="acitve" style="color:red">秦时明月汉时关,万里长征人未还!</p>
</transition>
<!-- CSS过渡类Class,使用了animate组件-->
<transition name="h" enter-active-class="animated fadeInRightBig" leave-active-class="animated zoomOut">
<p v-show="acitve" style="color:blue">秦时明月汉时关,赶快下楼做核酸!</p>
</transition>
</div>
</div>
<style>
.h-enter-active,
.h-leave-active { transition: all 0.5s cubic-bezier(1.0, 0.8, 0.5, 1.1); }
.h-enter, .h-leave-to { opacity: 0; }
.h-enter { transform: translateX(80px); }
.h-leave-to { transform: translateX(-80px); }
</style>
<script>
let app15 = new Vue({
el: "#app15",
data: { acitve: true },
methods: {
updateBtn: function (e) { this.$refs.btn.innerText = this.acitve ? "消失" : "出来"; }
}
})
</script>
transition动画元素的属性、钩子事件:
✔️<transition> 属性 |
描述 |
---|---|
name | 用于自动生成 CSS 过渡类名:name-enter 、name-leave |
appear | 开场动画,是否在初始渲染时使用过渡,bool,默认为 false |
css | bool,是否使用 CSS 过渡类,默认ture。如果用钩子函数JS控制动画,可以关闭css。 |
type | Vue 监听过渡事件类型类型,用于动画完成后清理class资源,animation (animationend ) 或 transition(transitionend ) |
mode | 过渡模式,控制新旧元素进入/离开过渡的先后顺序, out-in 、in-out ,默认同时进行。 |
duration | 过渡的持续时间(ms),{ enter: 500, leave: 500} ,用于事后清理资源,而不是设置动画时间 |
✔️CSS 过渡类 attribute | |
进入过渡 | enter-class、enter-to-class、enter-active-class |
离开过渡 | leave-class、leave-to-class、leave-active-class |
初始渲染过渡 | appear-class、appear-to-class、appear-active-class |
✔️JavaScript 钩子 | 用于JS的动画控制,可以借助第三方JS动画库 |
进入过渡 | before-enter(el)、enter(el, done)、after-enter(el)、enter-cancelled(el) 当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。 |
离开过渡 | before-leave(el)、leave(el, done)、after-leave(el)、leave-cancelled (el) leaveCancelled 只用于 v-show 中 |
初始渲染过渡 | before-appear(el)、appear(el)、after-appear(el)、appear-cancelled(el) |
3.2、transition-group列表过渡动画
列表的过渡是针对多个元素,需要 transition-group组件,支持的过渡类、特性和事件和<transition>
类似,多了tag
、移动过渡
。不过过渡针对的不是一个根元素,而是内部的每一个一级元素。<transition-group>
会产生一个元素<span>
,可通过tag修改渲染的元素,比如用来代替<ul>
不错。
<transition-group> |
描述 |
---|---|
tag | <transition-group> 产生的元素标签名,默认为span ,可以根据需要设置。 |
✔️CSS 过渡类 attribute | |
移动过渡 | move-class(类名:v-move) |
如果只是用进入、离开过渡,会导致其他元素的位置变化比较生硬。使用移动过渡v-move/move-class
,在元素改变定位的时候使用。Vue 使用了一个 FLIP 的动画队列,使用transforms
将元素位置平滑移动。
📢v-for 时需设置key:
v-fo
r创建列表元素时,强烈建议设置列表项的key值,列表动画中这是强制要求了!
代码示例:CodePen
<h1>app15: 动画</h1>
<div id="app15">
<h3>列表动画:<button @click="shuffle">随机乱序</button></h3>
<div>
<transition-group tag="ul" name="list" enter-active-class="animated flipInX">
<li v-for="(s,i) in list" v-bind:key="s">{{s}}
<button @click="list.splice(i,1)">-</button>
<button @click="list.splice(i+1,0,s+index++)">+</button>
</li>
</transition-group>
</div>
</div>
<style>
.list-enter { opacity:0; }
.list-enter-to{ transform: translateX(50px); }
.list-leave-active{ transition: all 1s; }
.list-leave-to { opacity: 0; transform: translateX(50px); }
.list-leave-active {
position: absolute; /*让元素移动更顺滑*/
transition: all 1s; }
.list-move { transition: 0.5s; }
</style>
<script>
let app15 = new Vue({
el: "#app15",
data: { acitve: true, index: 1, list: ['张三', '李四', '王五', '对六', '小七', '李白'] },
methods: {
// 随机排序
shuffle: function () { this.list.sort((a, b) => Math.random() - 0.5) }
}
})
</script>
©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀