十一,深入响应式原理
声明响应式属性
由于Vue不允许动态添加根级响应式属性,所以你必须在初始化实例钱声明根级响应式属性,哪怕只有一个空值。
var vm = new Vue({
data:{
// 声明 message 为一个空字符串
message: ' '
},
template: '<div>{{ message }}</div>'
})
// vm.message = 'Hello!'
vm.message = 'Hello!'
如果你在data 选项中未声明 message,Vue 将警告你渲染函数早试图访问的属性不存在。
异步更新队列
<div id="example">{{message}}</div>
var vm = new Vue({
el:'#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function() {
vm.$el.textContent === 'new message' //true
})
在组件内使用 vm.$nextTick() 实例方法特别方便,应为它不需要全局Vue ,并且回调函数中 this
将自动绑定到当前Vue
Vue.component('example', {
template: '<span> {{ message }}</span>',
data: function() {
return {
message: 'not updated'
}
},
methods: {
updateMessage: function() {
this.message = 'updated'
console.log(this.$el.textContent) // => 'not updated'
this.$nextTick(function () {
console.log(this.$el.textContent) // => 'updated'
})
}
}
})
十二,过度效果
在插入,更新或者移除DOM 时,提供多种不同方式的应用过度效果。
在css过度和动画中自动应用class
可以配合使用第三方css 动画库,如Animate.css
在过度钩子函数中使用JavaScript 直接操作DOM
可以配合使用第三方JavaScript 动画库,如velocity.js
单元素/组件的过度。
vue提供l了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加entering/leaving过度
条件渲染使用(使用 v-if)
条件展示(使用 v-show)
动态组件
组件跟节点
例子:
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name=fade>
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el:'#demo',
data: {
show: true
}
})
.fade-enter-active, .fade-leave-active {
transition:opacity .5s
}
.fade-enter, .fade-leave {
opacity: 0
}
元素封装成过渡组件之后,在遇到插入或删除时,Vue 将
1.自动嗅探目标元素是否有 CSS 过渡或动画,并在合适时添加/删除 CSS 类名。
2.如果过渡组件设置了过渡的 JavaScript 钩子函数,会在相应的阶段调用钩子函数
3.如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作(插入/删除)在下一帧中立即执行
#过度的-css-类名
会有4个css 类名在enter/leave 的过度中切换
1. v-enter: 定义进入过度的开始状态,在元素被插入的时生效,在下一个帧移除。
2.v-enter-actvie 定义进入过度的结束状态,在元素被插入时生效,在transition
/animation 完成之后移除。
3.v-leave: 定义离开过度的开始状态,在离开过度被触发时生效,在下一个帧移除。
4.v-leave-active: 定义离开过度的结束状态,在离开过度被处罚时生效,在transition/animation 完成之后移除。
css 过渡
常用的过度都是使用css 过渡
例子
<div id="example-1">
<button @click="show= !show">
Toggle render
</button>
<transition name="slide-fade">
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el: '#example-1,
data: {
show:true
}
})
// 可以设置不同的进入和离开动画
//设置持续时间和动画函数
.slide-fade-enter-active{
transition: all .3s ease;
}
.slide-fade-leave-active{
transition: all .8s cubic-bezier(1.0,0.5,0.8,1.0);
}
.slide-fade-enter, .slide-fade-leave-active{
transition: translateX(10px);
opacity: 0;
}
css 动画
css 动画用法同 css 过渡,区别是在动画中v-enter 类名节点插入DOM后
会不会立即删除,而是在animationend 事件触发时删除。
实例:
<div id="example-2">
<button @click="show = !show">Toggle show</button>
<transition name="bounce">
<p v-if="show">Look at me!</p>
</transition>
</div>
new Vue({
el: '#example-2',
data: {
show: true
}
})
.bounce-enter-active {
animation: bounce .5s;
}
.bounce-leave-active {
animation: bounce .5s;
}
@keyframes bounce-in {
0%{
transeform: sceale(0);
}
50%{
transeform: sceale(1.5);
}
100%{
transeform: sceale(1);
}
}
@keyframes bounce-out {
0%{
transeform: sceale(1);
}
50%{
transeform: sceale(1.5);
}
100%{
transeform: sceale(0);
}
}
自定义过渡名
我们可以通过以下特性来自定义过渡名:
enter-class
enter-active-class
leave-class
leace-active-class
他们的优先级别高于普通的类名,对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用
实例
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transititon"
enter-active-class="aniamated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el:'#example-3',
data: {
show: true
}
})
同时使用Transitions 和Animations
Vue 为了知道过渡的完成,必须设置相应的事件监听器
JavaScript 钩子
可以在属性中声明 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 结合时使用
enter: function (el, done) {
done()
},
afterEnter: function ( el) {
},
enterCancelled: function (el) {
},
// 离开时
beforeLeave: function(el) {
//
},
// 此函数是可选项的设置
// 与 css 结合时使用
leave: function (el, done) {
//...
done()
},
afterLeave: function (el) {
//...
},
// leaveCancelled 只用于v-show 中
leaveCancelled: function(el) {
// ...
}
}
这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。
初始渲染的过度
可以通过 appear 特性设置节点的在初始渲染的过度。
<transition appear></transition>
这里默认的和进入和离开过度一样,同样也可以自定义css类名
<tranaition appear
appear-class="custom-appear-class"
appear-active-class="custom-appear-active-class"
>
</tranaition>
自定义 JavaScript 钩子:
<transition
appear
v-on:before-appear="customBeforAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
>
</transition>
多个元素的过度
多个组件的过度很简单-我们不需要使用key 特性。我们只需要使用动态组件。
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
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>'
}
}
})
.component-fade-enter-active, .component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-active {
opacity: 0;
}
列表过度
目前为止,关于过度我们已经完成了:
单个节点
多个节点,其中每次只渲染一个
有一个完整的列表 v-for 我们如何做到同时渲染,我们将使用
<transition -group> 组件。
不同于 <transition> 他会以一个真实元素渲染。默认为<span>也可以通过 tag 属性更换为其他渲染元素。
他的元素必须具有为一个的key 属性
列表的进入和离开
进入和离开的过度使用之前一样的CSS 类名
<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>
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.radomIndex(), 0,this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
}
}
})
.list-item {
display: inline-balock;
margin-right:10px;
}
.list-enter-active, .list-leave-active {
transition: all ls;
}
.list-enter, .list-leave-active {
opacity: 0;
transform: translateY(30px);
}
列表的位移过度
<transition-group> 组件还有一个特殊之处,不仅可以进入和离开动画,还可以改变定位,
要使用这个新功能 v-move 特性,它会在元素的改变定位的过程中应用。
可以通过 name 属性来定义前缀,也可以通过move-class 属性手动设置。
v-move 对于设置过度的过度的切换时机和过度曲线非常有用,
<div id="flip-list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="flip-list" tage="ul">
<li v-for="item in items" v-bind:key="item">
{{item}}
</li>
</transition-group>
</div>
new Vue({
el: '#flip-list-demo',
data: {
items: [1,2,3,4,5,6,7,8,9]
},
methods: {
shuffle: function () {
this.items = _.shuffle(this.items)
}
}
})
.flip-list-move {
transition: transform 1s;
}
列表的渐进过度
通过data 属性 与 JavaScript 通信,可以实现列表的渐进过度
<div id="staggered-list-demo">
<input v-mode="query">
<transition-group
name="staggered-fade"
tage="ul"
v-bind:css= " false"
v-on:before-enter="beforeEnter"
v-on:enter = "enter"
v-on:leave="l >
<li
v-for="{item, index} in computedList"
v-bind:key = "item.msg"
v-bind:data-index="index"
>{{ item.msg}}</li>
</transition-group>
</div>
new Vue({
el: '#staggered-list-demo',
data: {
query:' ',
list: [
{msg: 'Bruce Lee'},
{msg: '*'},
{msg: 'Chunck Norris'},
{msg: 'Jet Li'},
{msg: 'Kung Fury'} ]
},
computed: {
computedList: function () {
var vm = this
return this.list.filter(function (item) {
return item.msg.toLowerCase().indexOf
(vm.query.toLowerCase()) !=== -1
})
}
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
el.style.height = 0
},
enter: function (el,done) {
var delay= el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{opacity: 1, height: '1.6em'},
{complete: done}
)
}, delay)
},
leave: function (el, done) {
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{opacity:0, height:0},
{complete: done}
)
},delay)
}
}
})
可复用的过度
过度可以通过 Vue 的组件系统实现复用,要创建一个可复用的过度组件,你需要做的就是
将 <transition> 或者 <transition-group> 作为一个跟=根组件,放置在其中就可以了
使用 template 的简单例子
Vue.component('my-special-transition', {
template:`\
<transition\
name="very-special-transition"\
mode= "out-in"\
v-on:before-enter="beforeEnter"\
v-on:after-enter="afterEnter"\
>\
<slot></slot>
</transition>\
\`,
methods: {
beforeEnter: function(el) {
// ...
},
afterEnter: function (el) {
// ...
}
}
})
函数组件更适合完成这个任务:
动态过渡
在Vue 中及时是过度也是数据驱动的,动态过度基本是通过name 特性来绑定动态值
<transition v-bind:name="transitionName"></transition>
所有的过渡特性都是动态绑定。它不仅是简单的特性,通过事件的钩子函数方法,可以在获取到相应上下文数据
<div id="dynmic-fade-">
Fade In: <input type="range" v-model="fadeInDuration"
min="0" v-bind:max="maxFadeFuration">
Fade Out: <input type="range" v-model="fadeOtDuration"
min="0" v-bind:max="maxFadeFuration">
<transition
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
>
<p v-if="show">hello</p>
</transition>
<button v-on:click="stop = true">Stop it!</button>
</div>
new Vue({
el: '#dynamic-fade-demo',
data: {
show: true,
fadeInDuration: 1000,
fadeOutDuration: 1000,
maxFadeDuration: 1500,
stop: false
},
mounted: function () {
this.show = false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
},
enter: function (el, done) {
var vm = this
Velocity(el,
{ opacity: 1 },
{
duration: this.fadeInDuration,
complete: function () {
done()
if (!vm.stop) vm.show = false
}
}
)
},
leave: function (el, done) {
var vm = this
Velocity(el,
{ opacity: 0 },
{
duration: this.fadeOutDuration,
complete: function () {
done()
vm.show = true
}
}
)
}
}
})