Vue.js 是一套构建用户界面的渐进式框架,响应式编程、组件化、轻量、模块化、简洁
核心:允许采用简洁的模板语法来声明式的将数据渲染进DOM
Vue.js 技术:
Vue-cli Vue-router Vuex ES6 NPM
Webpack (打包压缩工具)
Babel (翻译浏览器不支持的ES6、ES7)
Weex (andrio、ios、web通用框架)
jquery 对比 vue.js
var $btnMinus = $( 'body>button:nth-child(1)' )
var $btnAdd = $( 'body>button:nth-child(2)' )
$btnMinus.click(function(){
var n = + $('span').html();
$('span').html() = ++n;
})
$btnAdd.click(function(){
var n = + $('span').html();
$('span').html() = --n;
})
<script src="js/vue.js"></script>
<div id="app"> // 唯一父元素,vue页面必须包在唯一爹内
<button @click="minus">-</button> // @ 替代 on,不传参禁止加括号
<span> {{ n }} </span> // 动态变量
<button @click="add>+</button>
</div>
var vm = new Vue({ // (快递员)新建Vue类型实例对象
el: '#app', // (服务区)指定配送范围并监控
data: { // (变量仓库)缺陷写法,函数包裹方可重用
n: 17
},
methods: { // (函数仓库)
minus(){ this.n>0 && this.n-- }, // 用 this. 访问 data 内变量
add(){ this.n++ }
}
})
// data内变量、methods内函数,并不在其内部,而是直接创建在Vue对象下,所以直接写 this.n、this.add()
Vue 指令
动态绑定变量 v-html {{}} v-text v-cloak
// v-html 更新innerHTML <p> <a style="color:red">17</a> </p>
<p v-html="n"></p>
// {{}} 更新textContent 延迟会被用户看到语句
<p> {{n>17 ? '少年' n>18 ? '青年' : '小儿'}} </p>
// v-text 更新textContent 无延迟bug,不适合过长语句
<p v-text=" `曹操${n}岁` "><p>
v-cloak 加载阶段不显示;加载完成自我销毁,断交css,又显示;
// 这个指令保持在元素上直到关联实例结束编译。
// v-cloak、display:none 一起用时,可以隐藏未编译的 Mustache 标签直到实例准备完毕。
// Mustache 是一款「logic-less(轻逻辑)」的前端模板引擎。
// 主要用于在表现和数据相分离的前端技术架构中,根据数据生成特定的动态内容,
// 网页中指HTML结构,小程序中指WXML结构。
// 原本基于javascript,但因为轻量易用,经过拓展已支持更多的平台、语言。
[v-cloak] { display: none } // 必须配合css属性选择器方可生效
<h1 v-cloak> 年龄:{{age}} </h1> // 舞台剧,演员不齐,不揭幕布
// 3s前隐藏,3s后自动删除<h1>内v-cloak
<h1 v-text=" `年龄:${age}` "></h1>
setTimeout( ()=>{ // 延迟3秒后渲染页面
var vm = new Vue({
el: '#app',
data: {age: 17}
})
}, 3000)
动态绑定属性 :src v-bind:src v-model
// 动态地绑定一个或多个特性,或一个组件prop到表达式
// 在绑定class或style特性时,支持其它类型的值,如数组或对象
// 在绑定prop时,prop必须在子组件中声明。可以用修饰符指定不同的绑定类型
<a v-bind:href="linkUrl">home</a>
:href="linkUrl" // 缩写
<img :src="
n<10 ? 'img/1.png' :
n<20 ? 'img/2.png' :
n<30 ? 'img/3.png' :
'img/4.png' " alt="">
// v-model 双向绑定,用于获取文本框、表单内容
<input type="text" v-model:value="kws">
v-model="kws" // 缩写
data:{ kws: '' },
methods:{
search(){ console.log( `您输入了${this.kws}` )
}
v-show v-if
// v-show (控一人生死) 根据表达式真假值,切换元素的css-display属性
<div v-show=" show ">1<div>
data:{ show:false }
// v-if (控多人生死) 根据表达式值的真假条件,来添加删除元素,效率较低
<p v-if=" n<7 ">1</p>
<p v-if=" n>7 ">2</p>
<p v-if=" n>9 ">3</p>
v-if v-else-if v-else
<div v-if=" isLogin===false ">
<a>登陆</a> <a>注册</a>
</div>
<div v-else>
<a>用户</a> <a>注销</a>
</div>
<div v-if=" n<10 ">1</div>
<div v-else-if=" n<20 ">2</div>
<div v-else-if=" n<30 ">3</div>
<div v-else>4</div>
v-for
// 无需再区分对象数组关联数组。必须为被遍历元素提供别名:alias in/of expression
<li v-for="item in list"> {{item}} </li> // warn 无法获取当前编号
<li v-for="(item, i) of list"> {{i+1}}-{{item}} </li> // err 一参变全参刷新
v-for="(item, key) in list" :key="key"
@ v-on:
// 绑定事件监听器,事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句。
< button v-on:click="say()" > clickMe </button>
@click="say" // 缩写
// 用在自定义元素组件上,可以监听子组件触发的自定义事件
// 基本写法
<div @click=" say "></div>
methods: {
say( e ){ ... }
}
// 传参写法
// 先用关键词 $event接住事件对象,再由开发者决定书写位置,再传给e
<div id="jack" @click=" 'jack', say($event) "></div>
methods: {
say( name,e ){
alert( `${name} x:${e.offsetX} y:${e.offsetY}` )
}
}
v-pre
// 跳过编译:跳过这个元素和它的子元素的编译过程。
// 可以用来显示 原始 Mustache 标签。跳过大量没有指令的节点会加快编译
<p v-pre> Vue的{{变量}}语法很牛批 </p>
// {{}}失去功能,变为普通字符
v-once
// 只渲染元素和组件一次,重新渲染直接跳过。优化性能,节省内存。
<h1 v-once> 页面加载时间:{{ new Date(timer).toLocaleString() }} </h1>
<h1> 实时系统时间:{{ new Date(timer).toLocaleString() }} </h1>
data: {
timer: new data().getTime()
}
methods: {
setInterval( ()=>{
vm.timer = new Date().getTime();
}, 1000);
}
组件属性
Data
Vue实例的数据对象。
Vue递归将data的属性转换为getter/setter,从而让data的属性能够响应数据变化。
对象必须是纯粹的对象(含有零个或多个的key/value对)
注意:不应该对data属性使用箭头函数
理由:箭头函数绑定了父级作用域的上下文,
所以this将不会按照期望指向Vue实例,this.myProp 将是undefined
Props
props可以是数组或对象,用于接收来自父组件的数据。
使用对象作为替代,允许配置高级选项,如类型检测、自定义校验和设置默认值
Computed
计算属性将被混入到Vue实例中。
所有getter和setter的this上下文自动地绑定为Vue实例
注意:不应该使用箭头函数来定义计算属性函数。例如 aDouble: () => this.a*2
理由:是箭头函数绑定了父级作用域的上下文,
所以this将不会按照期望指向Vue实例,this.a 将是undefined
<p>总价:¥ {{ total().toFixed(2) }} </p>
data: {
cart: [ {price:1999, count:2}, {price:3879, count:3} ]
}
computed: { // 封装 不存在、依赖于其他数据、计算后得到的数据属性
total(){
var sum = 0;
for(var p of this.cart){
sum += p.price*p.count;
}
ruturn sum;
}
}
使用 methods:{},作为函数,页面刷新即重复计算。
使用 computed:{},作为属性,值会被new vue缓存,刷新后只是调内存。
Methods
// methods将被混入到Vue实例中。
// 可直接通过VM实例访问这些方法,或者在指令表达式中使用。
// 方法中的this自动绑定为Vue实例。
var vm = new Vue({
data:{a: 1 },
methods: {
plus: function () {this.a+ +}
}
})
vm.plus()
console.log( vm.a ) // 2
Watch
一个对象,键是需要观察的表达式,值是对应回调函数。
值也可以是方法名,或者包含选项的对象。
Vue 实例将会在实例化时调用 $watch(),遍历 watch对象的每一个属性。
watch 监听变量,变量变化,则自动执行一个操作
watch 内函数名,要和被监视的变量一样,变量一变就触发它
var vm = new Vue({
data: {a: 1, b: 2, c: 3},
watch: {
a: function (val, oldVal) {
console.log( `new: %s, old: %s`, val, oldVal)
}
}
}
vm.a = 2 // -> new: 2, old: 1
百度搜索:停顿半秒后自动发送请求效果
// 防抖和节流:每输入删除一次,重置定时器;用户不输入500ms后,才触发自动请求;
// trim() 去除字符两端多余空格
<input type="text" v-model="kws"> <button @click="search">百度一下</button>
data: {
kws: "",
timer: 0 // 定义定时器变量
},
methods: {
search(){
clearTimeout(this.timer); // 每次事件首先停止正在运行定时器
this.timer = setTimeout( ()=>{ // 匿名、回调函数this默认指向window,改用箭头函数
if( this.kws.trim()!=="" ){ // 然后重新启动一个定时器
console.log( `搜索${this.kws}相关内容` )
}
}, 500)
}
},
watch: {
kws(){
this.search()
}
}
组件
Vue的组件系统,允许我们使用小型、自包含和通常可复用的组件构建大型应用。 | 组件(Component),可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素。 |
---|---|
new Vue( ) | 先建小区,再有居民 |
Vue.component( ) | 先有移民,后建小区,有多少居民,建多少小区 |
template: | 模板,要求唯一父元素 |
data(){ return {} } | 既然作为模板,data就必须能被重复创建为各子组件专属data对象 |
组件命名用 ‘-’ | 不要用驼峰命名,html不区分大小写 |
基本组件
// 创建
Vue.component( 'my-counter', {
template: `<div> // 必须置于唯一父元素内
<button @click="minus">-</button><span>{{n}}</span><button @click="add">+</button
</div>`,
data(){
return {
n: 1
}
},
methods: {
minus(){ this.n>1 && this.n-- },
add(){ this.n++ }
}
})
// 使用
<ul>
<li><my-counter></my-counter></li>
<li><my-counter></my-counter></li>
</ul>
页面组件
<template>
<h1> Hellp World </h1>
</template>
<script>
export default {
name: 'demo1'
}
</script>
<style scoped></style>
复合组件 components:[ ]
// 组件也可以由多个组件构成,称为复合组件
Vue.component('item',{
template: '<li>A custom component!</li>'
})
Vue.component('my-list',{
template: '<ol>
<item></item>
<item></item>
</ol>'
})
组件声明后,默认为全局组件,可放至父组件之外,所以必须约束子组件,禁止外出。
子组件一律降级为对象,并用驼峰命名(网页使用时,会自动翻译为以“-”分隔的非驼峰命名)
最后在父组件内,用专有属性 components 滴血认爹,才能成为真子组件。
子组件a的子组件b,a也必须对b公开叫一声吾儿。
注意顺序,父组件必须最后声明。
步骤:
// 子组件
var todoAdd = {
template: `<div>
<input type="text"><button>+</button>
<div>`
}
// 子组件
var todoList = {
template: `<ul>
<todo-item></todo-item>
<todo-item></todo-item>
<todo-item></todo-item>
</ul>`,
components: { todoItem }
}
// 子组件
var todoItem = {
template: `<li>
1 - 吃饭 <a href="javascript:;">X</a>
</li>`
}
// 父组件
Vue.component('todo',{
template: `<div>
<h1>代办事项列表</h1>
<todo-add></todo-add>
<todo-list></todo-list>
</div>`,
components: { todoAdd, todoList }
})
组件生命周期
组件的生命周期分为四个阶段:create / mount / update / destroy
钩子函数 hook: beforeCreate: function(){ … }
created
beforeCreate 观察数据、初始化事件
beforeMount
mounted 创建虚拟DOM树,页面挂载
beforeUpdate
updated
beforeDestroy
destroyed
Vue 中没有 window.onload 或 $(function)
自定义指令
除了默认设置的核心指令(v-model 和 v-show),Vue也允许注册自定义指令。
注意,在Vue2.0里面,代码复用的主要形式和抽象是组件,
然而有时仍需要对纯DOM元素进行底层操作,这时就会用到自定义指令。
// 定义时不加前缀
Vue.directive('focus', {})
// 使用时要加前缀 v-
<p v-focus></p>
自定义函数提供了几个钩子函数(可选):
bind:
// 只调用一次,指令第一次绑定到元素时调用,用于定义在绑定时执行一次的初始化动作。
inserted:
// 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
update:
// 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。
// 通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
unbind:
// 只调用一次,指令与元素解绑时调用。
示例:百度搜索,页面初始化,输入框自动获得焦点效果
<script>
Vue.directive('focus',{
inserted(domEle){ // Vue少有的执行后会传入dom元素的地方
domEle.focus(); // 任何表单元素都可以调用 foucs()获焦
}
})
</script>
<input type="text" v-focus>
<button>百度一下</button>
页面加载 - 被绑定元素插入父节点 - 触发v-foucs - 传入dom - 获得焦点
自定义过滤器
过滤器的本质是有返回值的函数。
Vue支持自定义过滤器,在使用过滤器时支持多重过滤并传参。
使用时直接 “竖线”+“过滤器名”。
“|” 将旧值通向filter的oldVal,返回值自动替换过滤器名。
oldVal 有参数增强的默认值属性。
" || ‘¥’ ",不传参、只传一个都行,增加了容错率。
<p> {{ price | moneyFilter('¥') }} </p>
<p> {{ price | moneyFilter('$') }} </p>
<p> {{ price | moneyFilter }} </p>
<script>
Vue.filter( 'moneyFilter', function( oldVal,type ){
return `${type || '¥'}${ price.toFixed(2) }`
})
</script>
组件通信
父子组件嵌套,产生了父与子组件如何通信的问题。
组件实例的作用域是孤立的,无法在子组件模板内直接引用父组件的数据。
Vue中父子组件的关系可以总结为 props down,events up。
父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息
父向子通信
子组件要显式地用 props 声明它期待从父组件那获得的数据。
在模板中用 v-bind,动态地绑定父组件的数据到子模板的 props
每当父组件的数据变化时,该变化也会传导给子组件
// 子组件
Vue.component( 'child',{
props: ['message'],
template: '<p>{{ message }}</p>'
})
// 父组件
<div>
<input v-model="parentMsg">
<child v-bind:my-message="parentMsg"></child>
</div>
// 示例
var todoAdd = {
template: `<div>
<input type="text"><button>+</button>
<div>`
}
var todoList = {
template: `<ul>
<todo-item v-for="(t, i) in tasks" :key="i"
:i="i" :t="t" :tasks="tasks"></todo-item>
</ul>`,
components: { todoItem },
props: ["tasks"]
}
var todoItem = {
template: `<li>
{{i+1}} - {{t}} <a href="javascript:;" @click="del">X</a>
</li>`,
props: ["i", "t", "tasks"],
methods: {
del(){
this.tasks.splice(this.i,1)
}
}
}
Vue.component('todo',{
template: `<div>
<h1>代办事项列表</h1>
<todo-add></todo-add>
<todo-list :tasks="tasks"></todo-list>
</div>`,
components: { todoAdd, todoList },
data(){
return {
tasks: ["吃饭", "睡觉", "打亮亮"]
}
}
})
子向父通信
子组件要把数据传递回父组件,用自定义事件。
使用 $on(eventName) 监听事件 就是 @
使用 $emit(eventName) 触发事件
注意:
不能用 $on 侦听子组件抛出的事件,而必须在模板里直接用 v-on 绑定
// 父组件内:给子组件绑定一个eventName的事件
<child v-on:eventName="handleIncre($event)"></child>
// 子组件内:在子组件内触发父组件指定的叫做eventName事件
this.$emit('increment')
// 示例
var todoAdd = {
template: `<div>
<input type="text"><button @click="add">添加</button>
<div>`,
methods: {
add(){
this.$emit('add',this.task) // 子 1.发射
}
}
}
Vue.component('todo',{
template: `<div>
<h1>代办事项列表</h1>
<todo-add @add="add"></todo-add> // 爹 2.监听
</div>`,
methods: {
add(task){
this.tasks.push(task) // 爹 3.执行
}
}
})
父子互通
打通父子之间所有数据和方法的共享方法
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs对象上。
在普通DOM元素上使用,引用指向的就是DOM元素;在子组件上,引用就指向组件实例;
父组件注册: <child ref="myChild"></child>
父访问子: this.$refs.myChild.子组件的数据名或方法名
子访问父: this.$parent.父组件的数据名或方法名
// 示例
var todoAdd = {
template: `<div>
<input type="text" v-model="task" ref="input"><button @click="add">添加</button>
<div>`,
methods: {
add(){
this.$parent.add() // 2.子直接调用
}
}
}
Vue.component('todo',{
template: `<div>
<h1>代办事项列表</h1>
<todo-add ref="todoAdd"></todo-add>
</div>`,
methods: {
add(){
this.tasks.push( this.$refs.todoAdd.task ) // 1.爹先定义好方法
}
},
mounted: {
this.$refs.input.focus() // $refs 可以获得dom
}
})
逐级引用,需要逐级命名ref,都要命名 this.$parent.$parent
兄弟通信(全能通信、像vuex)
非父子关系组件也需要通信,通过一个空的Vue实例作为事件传输的中介。
每个组件可将自己数据及方法的API,托管到Bus平台,供任意组件使用及访问
另建vue实例:
var bus = new Vue();
组件a:绑定事件
bus.$on( 'msgToBrother', function(data){ ... }
组件b:触发事件
bus.$emit( 'msgToBrother', 'it is a message')
// 示例
var bus = new Vue();
bus.$on("del",this.del) 最好在那个组件内加,绑当前数据
bus.$emit("del",this.i)
组件路由
路由机制 router-view
vue-router是Vue.js官方的路由插件,与vue.js深度集成,适用于构建单页面应用。
vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。
传统的页面应用,是用一些超链接来实现页面切换和跳转的。
在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。
单页面应用(single page application)页面切换优势:
1.只有一个页面,一次性请求所有资源到客户端,直接更换组件实现切换页面,节省服务器资源。
2.一些公共资源如boot、jquery等,切页面都不用再二次下载,优化冗余。
3.内存构建dom树,切页面只更换局部节点,节省客服端内存。
4.可呈现页面切换动画,因为俩页面都在我本地了,可同时存在,瞬间跳转很无趣。
早期缺陷:首次请求下载所有资源,首屏加载慢,已通过懒加载解决
懒加载:先加载首页,后台悄悄ajax异步下载组件
vue-router: 监视地址栏变化的路由管理员
路由字典routes: vue-router手里的谷歌地图
http://localhost:8080/#/
http://localhost:8080/#/details
实现路由的基本步骤:
1.html上指定一个盛放组件的容器来占位 <router-view </router-view
2.用var创建各个子组件
3.配置路由词典
4.访问指定路由下的组件(超链接、编程导航)
// router.js
var router = new VueRouter({
routes: [
{path:"/", component: Index},
{path:"/details", component: Details},
]
})
// index.js
var Index = {
template: `<h1>这是首页</h1>`
}
// index.html
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script src="router.js"></script>
<script src="index.js"></script>
<div id="app">
<my-header></my-header>
<router-view></router-view>
</div>
new Vue({
el: "#app",
router
})
脚手架里的路由词典配置,需要vue-router模块,引入后才可配置:
import Router from 'vue-router'
Vue.use( Router )
export default new Router({
routes: [
{ path: '/start,component: Start },
{ path: /main' component: Main }
]
})
路由跳转 <router-link to=" "
vue-router的路由鉴别标准,默认采用客户端锚点地址 # ,不写 # 就变成服务端地址了。
<a>标签都每个url,必须手写 #,非常麻烦。
<router-link>会自动翻译为<a>,自动判断加不加 # ,非常方便。
<a href="#/foo">jump to foo</a> // 超链接跳转
<a href="javascript:;" @click="logout">返回首页</a> // js 跳转
this.$router.push('/') // new vue对象内router会被改名,被加上$
<router-link to="/foo">go to foo</router-link> // 自定义指令跳转
路由跳转传参
1.配置接收方路由:
{ path: '/detail/:lid', component: Detail }
2.发送参数:
this.$router.push('/detail/' +lid) methods里,斜线带参,且只写值
3.接受参数:
this.$route.params.id 下个页面这样拿参数,$route像Bom的location
路由跳转传参另一种简捷方法,组件传参:
路由词典:
{ path: '/detail/:lid', component: Detail, props: true }
目标页面:
props: ["lid"]
路由嵌套
1.父组件内指定router-view盛放子组件 2.配置父组件路由地址时,指定children属性
const routes = [
{
path: '/', component: Home, children: [
{path: '/', component: Index },
{path: '/details:lid', component: Details, props: true }
]
},
{ path: '*', component: NotFound }
]
路由判断顺序:
1. http://localhost:8080/#/ 首先渲染页头
2. /#/index
3. /#/details 然后加载了页面
...
99. http://localhost:8080/#/NotFound 则404页不会有页头
组件路由
VueResource
Vue.js通过vue-resource模块进行异步请求、跨域请求,
与jquery的$.ajax相同,可通过 XMLHttpRequest 或 JSONP 发起请求并处理响应,且API更简洁。
vue-resource 特点:
1.体积小,压缩后大约12KB,服务端启用gzip压缩后仅4.5KB
2.支持主流的浏览器,同Vue.js,除IE9以下,其他都支持。
3.支持Promise API 和 URI Templates
Promise是ES6的特性,Promise的中文含义为”先知”, Promise对象用于异步计算。
URI Templates表示URI模板,有些类似于ASP .NET MVC的路由模板。
4.支持拦截器
拦截器是全局的,可在请求发送前、发送后做一些处理。
比如请求发送前,在headers中设置 access_token,或在请求失败时,提供共通的处理方式。
npm install --save vue-resource 安装vue-resource
import VueResource from 'vue-resource' 引入vue-resource
Vue.use( VueResource )
this.$http.get( 'http://localhost:8080/#/details', { 发起GET请求
params: { lid:5 }
}).then( (result)=>{
console.log( result.data )
})
this.$http.post( 'http://localhost:8080/#/login', { 发起POST请求
uname: "caocao", upwd: "00000000" 不支持拼接字符串传参
}).then( (result)=>{
console.log( result.data )
})
vue2.0 已经推崇 axos 取代了 vue-resource,且 axios支持拼接字符串传参
axios
// 安装 axios
npm i -save axios
npm install -save axios
// main.js 下引入并配置
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8080';
Vue.prototype.axios = axios;
this.axios.get( `/user/v1/userRegister/${uname}&${upwd}` )
.then( result=>{ ... });
this.axios.get( '/details', {
params: { lid:5 }
}).then( ... )
this.axios.post( '/login', {
uname: "caocao", upwd: "00000000"
}).then( ... )
this.axios.post( '/login', "uname=caocao&upwd=00000000"
).then( ... )
VueResource 拦截器
Vue.http.interceptors.push( (req, next)=>{
... // 请求发送前的处理逻辑
next( (res)=>{
... // 请求发送后的处理逻辑
return res; // res参数会返回给 successCallback 或 errorCallback
})
})
axios拦截器
var axios = axios.create()
axios.interceptors.request.use( config=>{ 发送拦截器
console.log('发送请求前拦截');
return config;
})
axios.interceptors.reponse.use( res=>{ 响应拦截器
console.log('收到响应后拦截');
return res;
})
过度效果
Vue在插入、更新、移除DOM时,提供了多种不同方式的应用过渡效果。包括以下工具:
- 在CSS过渡和动画中自动应用class
- 可以配合使用第三方CSS动画库,如Animate.css
- 在过渡钩子函数中使用JavaScript直接操作DOM
- 可以配合使用第三方JavaScript动画库,如Velocity.js
会有6个css类名在 enter/leave 的过渡中切换
v-enter:
定义过渡的开始状态。在元素被插入时生效,在下一帧移除。
v-enter-active:
定义过渡的状态。在整个过渡过程中生效,在transition/animation完成之后移除。
还可以定义过渡的过程时间、延迟、曲线函数。
v-enter-to:
定义过渡的结束状态。在元素被插入一帧后生效(于此同时v-enter被删除),
在transition/animation完成之后移除。
.fade-enter-active, .fade-leave-active { 进入离开时,透明变化.5s
transition: opacity .5s
}
.fade-enter, .fade-leave-to{ 进入离开时,透明度 0
opacity: 0
}
<div id="demo">
<button @click=" show=!show ">Toggle</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
data: { show: false }