transition组件可以给任何元素和组件添加进入/离开过渡,但只能给单个组件实行过渡效果(多个元素可以用transition-group组件,下一节再讲),调用该内置组件时,可以传入如下特性:

name         用于自动生成CSS过渡类名        例如:name:'fade'将自动拓展为.fade-enter,.fade-enter-active等
    appear      是否在初始渲染时使用过渡         默认为false
    css            是否使用 CSS 过渡类。             默认为 true。如果设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。
    mode        控制离开/进入的过渡时间序列    可设为"out-in"或"in-out";默认同时生效
    type          指定过渡事件类型                      可设为transition或animation,用于侦听过渡何时结束;可以不设置,Vue内部会自动检测出持续时间长的为过渡事件类型
    duration    定制进入和移出的持续时间        以后用到再看

type表示transition对应的css过渡类里的动画样式既可以用transition也可以用animation来设置动画(可以同时使用),然后我们可以用指定,Vue内部会自动判断出来

除了以上特性,我们还可以设置如下特性,用于指定过渡的样式:

appear-class             初次渲染时的起始状态    ;如果不存在则等于enter-class属性                 这三个属性得设置了appear为true才生效
    appear-to-class         初次渲染时的结束状态    如果不存在则等于enter-to-class    属性
    appear-active-class   初次渲染时的过渡           如果不存在则等于enter-active-class属性
    enter-class                进入过渡时的起始状态  
    enter-to-class            进入过渡时的结束状态 
    enter-active-class     进入过渡时的过渡          
    leave-class               离开过渡时的起始状态    
    leave-to-class          离开过渡时的结束状态    
    leave-active-class    离开过渡时的过渡

对于后面六个class,内部会根据name拼凑出对应的class来,例如一个transition的name="fade",拼凑出来的class名默认分别为:fade-enter、fade-enter-to、fade-enter-active、fade-leave、fade-leave-to、fade-leave-active

除此之外还可以在transition中绑定自定义事件,所有的自定义事件如下

before-appear          初次渲染,过渡前的事件                         未指定则等于before-enter事件    
    appear                     初次渲染开始时的事件                             未指定则等于enter事件 
    after-appear             初次渲染,过渡结束后的事件                  未指定则等于enter-cancelled事件    
    appear-cancelled     初次渲染未完成又触发隐藏条件而重新渲染时的事件,未指定则等于enter-cancelled事件    
    before-enter             进入过渡前的事件
    enter                        进入过渡时的事件                            
    after-enter               进入过渡结束后的事件
    enter-cancelled       进入过渡未完成又触发隐藏条件而重新渲染时的事件    
    before-leave           离开过渡前的事件
    leave                      离开时的事件                                   
    after-leave              离开后的事件
    leave-cancelled      进入过渡未完成又触发隐藏条件而重新渲染时的事件

transition相关的所有属性应该都列出来了(应该比官网还多吧,我是从源码里找到的),我们举一个例子,如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<style>
.fade-enter,.fade-leave-to{background: #f00;transform:translateY(20px);} /*.fade-enter和.fade-leave-to一般写在一起,当然也可以分开*/
.fade-enter-active,.fade-leave-to{transition:all 1s linear 500ms;}
</style>
<body>
<div id="app">
<button @click="show=!show">按钮</button>
<transition name="fade" :appear="true" @before-enter="beforeenter" @enter="enter" @after-enter="afterenter" @before-leave="beforeleave" @leave="leave" @after-leave="afterleave">
<p v-if="show">你好</p>
</transition>
</div>
<script>
Vue.config.productionTip=false;
Vue.config.devtools=false;
var app = new Vue({
el:"#app",
data:{
show:true
},
methods:{
beforeenter(){console.log('进入过渡前的事件')},
enter(){console.log('进入过渡开始的事件')},
afterenter(){console.log('进入过渡结束的事件')},
beforeleave(){console.log('离开过渡前的事件')},
leave(){console.log('离开过渡开始的事件')},
afterleave(){console.log('离开过渡结束的事件')}
}
})
</script>
</body>
</html>

我们调用transition组件时设置了appear特性为true,这样页面加载时动画就开始了,如下:

控制台输出如下:

文字从透明到渐显,同时位移也发生了变化,我们点击按钮时又会触发隐藏,继续点击,又会显示,这是因为我们在transition的子节点里使用了v-show指令。

对于transition组件来说,在下列情形中,可以给任何元素和组件添加进入/离开过渡:

条件渲染 (使用 v-if)
    条件展示 (使用 v-show)
    动态组件
    组件根节点

用原生DOM模拟transition组件


Vue内部是通过修改transition子节点的class名来实现动画效果的,我们用原生DOM实现一下这个效果,如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
.trans{transition:all 2s linear;}
.start{transform:translatex(100px);opacity: 0;}
</style>
<body>
<div id="con">
<button name="show">显式</button>
<button name="hide">隐藏</button>
</div>
<p id="p">Hello Vue!</p>
<script>
var p = document.getElementsByTagName('p')[0];
document.getElementById('con').addEventListener('click',function(event){
switch(event.target.name){
case "show":
p.style.display="block";
p.classList.add('trans');
p.classList.remove('start')
break;
case "hide":
p.classList.add('trans')
p.classList.add('start')
break;
}
})
</script>
</body>
</html>

渲染的页面如下:

我们点击隐藏按钮后,Hello Vue!就逐渐隐藏了,然后我们查看DOM,如下:

这个DOM元素还是存在的,只是opacity这个透明度的属性为0,Vue内部的transition隐藏后是一个注释节点,这是怎么实现的,我们能不能也实现出来,当然可以。

Vue内部通过window.getComputedStyle()这个API接口获取到了transition或animation的结束时间,然后通过绑定transitionend或animationend事件(对应不同的动画结束事件)执行一个回调函数,该回调函数会将DOM节点设置为一个注释节点(隐藏节点的情况下)

我们继续改一下代码,如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
.trans{transition:all 2s linear;}
.start{transform:translatex(100px);opacity: 0;}
</style>
<body>
<div id="con">
<button name="show">显式</button>
<button name="hide">隐藏</button>
</div>
<p id="p">Hello Vue!</p>
<script>
var p = document.getElementsByTagName('p')[0],
tid = null,
pDom = null,
CommentDom = document.createComment("");
document.getElementById('con').addEventListener('click',function(event){
switch(event.target.name){
case "show":
CommentDom.parentNode.replaceChild(p,CommentDom)
setTimeout(function(){p.classList.remove('start')},10)
ModifyClass(1)
break;
case "hide":
p.classList.add('trans')
p.classList.add('start')
ModifyClass(0)
break;
}
}) function ModifyClass(n){ //s=1:显式过程 s=0:隐藏过程
var styles = window.getComputedStyle(p);
var transitionDelays = styles['transitionDelay'].split(', '); //transition的延迟时间 ;比如:["0.5s"]
var transitionDurations = styles['transitionDuration'].split(', '); //transition的动画持续时间 ;比如:"1s"
var transitionTimeout = getTimeout(transitionDelays, transitionDurations); //transition的获取动画结束的时间,单位ms,比如:1500
tid && clearTimeout(tid);
tid=setTimeout(function(){
if(n){ //如果是显式
p.classList.remove('trans')
p.removeAttribute('class');
}else{ //如果是隐藏
p.parentNode.replaceChild(CommentDom,p);
}
},transitionTimeout)
} function getTimeout(delays, durations) { //从Vue源码里拷贝出来的代码的,获取动画完成的总时间,返回ms格式
while (delays.length < durations.length) {
delays = delays.concat(delays);
}
return Math.max.apply(null, durations.map(function (d, i) {
return toMs(d) + toMs(delays[i])
}))
}
function toMs(s) {
return Number(s.slice(0, -1)) * 1000
}
</script> </body>
</html>

这样当动画结束后改DOM就真的隐藏了,变为了一个注释节点,如下:

当再次点击时,就会显式出来,如下:

完美,这里遇到个问题,就是当显式的时候直接设置class不会有动画,应该是和重绘有关的吧m所以用了一个setTImeout()来实现。

Vue也就是把这些原生DOM操作进行了封装,我们现在来看Vue的源码

源码分析


transition是Vue的内置组件,在执行initGlobalAPI()时extend保存到Vue.options.component(第5052行),我们可以打印看看,如下:

Transition组件的格式为:

var Transition = {    //第8012行  transition组件的定义
name: 'transition',
props: transitionProps,
abstract: true, render: function render (h) {
/**/
}
}

也就是说transition组件定义了自己的render函数。

以上面的第一个例子为例,执行到transition组件时会执行到它的render函数,如下:

  render: function render (h) {         //第8217行  transition组件的render函数,并没有template模板,初始化或更新都会执行到这里
var this$1 = this; var children = this.$slots.default;
if (!children) {
return
} // filter out text nodes (possible whitespaces)
children = children.filter(function (c) { return c.tag || isAsyncPlaceholder(c); });
/* istanbul ignore if */
if (!children.length) { //获取子节点
return //如果没有子节点,则直接返回
} // warn multiple elements
if ("development" !== 'production' && children.length > 1) { //如果过滤掉空白节点后,children还是不存在,则直接返回
warn(
'<transition> can only be used on a single element. Use ' +
'<transition-group> for lists.',
this.$parent
);
} var mode = this.mode; //获取模式 // warn invalid mode
if ("development" !== 'production' &&
mode && mode !== 'in-out' && mode !== 'out-in' //检查mode是否规范只能是in-out或out-in
) {
warn(
'invalid <transition> mode: ' + mode,
this.$parent
);
} var rawChild = children[0]; //获取所有子节点 // if this is a component root node and the component's
// parent container node also has transition, skip.
if (hasParentTransition(this.$vnode)) { //如果当前的transition是根组件,且调用该组件的时候外层又套了一个transition
return rawChild //则直接返回rawChild
} // apply transition data to child
// use getRealChild() to ignore abstract components e.g. keep-alive
var child = getRealChild(rawChild);
/* istanbul ignore if */
if (!child) {
return rawChild
} if (this._leaving) {
return placeholder(h, rawChild)
} // ensure a key that is unique to the vnode type and to this transition
// component instance. This key will be used to remove pending leaving nodes
// during entering.
var id = "__transition-" + (this._uid) + "-"; //拼凑key,比如:__transition-1 ;this._uid是transition组件实例的_uid,在_init初始化时定义的
child.key = child.key == null
? child.isComment
? id + 'comment'
: id + child.tag
: isPrimitive(child.key)
? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key)
: child.key; var data = (child.data || (child.data = {})).transition = extractTransitionData(this); //获取组件上的props和自定义事件,保存到child.data.transition里
var oldRawChild = this._vnode;
var oldChild = getRealChild(oldRawChild); // mark v-show
// so that the transition module can hand over the control to the directive
if (child.data.directives && child.data.directives.some(function (d) { return d.name === 'show'; })) { //如果child带有一个v-show指令
child.data.show = true; //则给child.data新增一个show属性,值为true
} if (
oldChild &&
oldChild.data &&
!isSameChild(child, oldChild) &&
!isAsyncPlaceholder(oldChild) &&
// #6687 component root is a comment node
!(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment) //这里是更新组件,且子组件改变之后的逻辑
) {
// replace old child transition data with fresh one
// important for dynamic transitions!
var oldData = oldChild.data.transition = extend({}, data);
// handle transition mode
if (mode === 'out-in') {
// return placeholder node and queue update when leave finishes
this._leaving = true;
mergeVNodeHook(oldData, 'afterLeave', function () {
this$1._leaving = false;
this$1.$forceUpdate();
});
return placeholder(h, rawChild)
} else if (mode === 'in-out') {
if (isAsyncPlaceholder(child)) {
return oldRawChild
}
var delayedLeave;
var performLeave = function () { delayedLeave(); };
mergeVNodeHook(data, 'afterEnter', performLeave);
mergeVNodeHook(data, 'enterCancelled', performLeave);
mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; });
}
} return rawChild //返回DOM节点
}

extractTransitionData()可以获取transition组件上的特性等,如下:

function extractTransitionData (comp) {   //第8176行  提取在transition组件上定义的data
var data = {};
var options = comp.$options; //获取comp组件的$options字段
// props
for (var key in options.propsData) { //获取propsData
data[key] = comp[key]; //并保存到data里面 ,例如:{appear: true,name: "fade"}
}
// events.
// extract listeners and pass them directly to the transition methods
var listeners = options._parentListeners; //获取在transition组件上定义的自定义事件
for (var key$1 in listeners) { //遍历自定义事件
data[camelize(key$1)] = listeners[key$1]; //也保存到data上面
}
return data
}

例子里的transition组件执行到返回的值如下:

也就是说transition返回的是子节点VNode,它只是在子节点VNode的data属性上增加了transition组件相关的信息

对于v-show指令来说,初次绑定时会执行bind函数(可以看https://www.cnblogs.com/greatdesert/p/11157771.html),如下:

var show = {        //第8082行
bind: function bind (el, ref, vnode) { //初次绑定时执行
var value = ref.value; vnode = locateNode(vnode);
var transition$$1 = vnode.data && vnode.data.transition; //尝试获取transition,如果v-show绑定的标签外层套了一个transition则会把信息保存到该对象里
var originalDisplay = el.__vOriginalDisplay =
el.style.display === 'none' ? '' : el.style.display; //保存最初的display属性
if (value && transition$$1) { //如果transition$$1存在的话
vnode.data.show = true;
enter(vnode, function () { //执行enter函数,参数2是个函数,是动画结束的回掉函数
el.style.display = originalDisplay;
});
} else {
el.style.display = value ? originalDisplay : 'none';
}
},

最后会执行enter函数,enter函数也就是动画的入口函数,比较长,如下:

function enter (vnode, toggleDisplay) {             //第7599行  进入动画的回调函数
var el = vnode.elm; // call leave callback now
if (isDef(el._leaveCb)) { //如果el._leaveCb存在,则执行它,离开过渡未执行完时如果重新触发了进入过渡,则执行到这里
el._leaveCb.cancelled = true;
el._leaveCb();
} var data = resolveTransition(vnode.data.transition); //调用resolveTransition解析vnode.data.transition里的css属性
if (isUndef(data)) {
return
} /* istanbul ignore if */
if (isDef(el._enterCb) || el.nodeType !== 1) {
return
} var css = data.css; //是否使用 CSS 过渡类
var type = data.type; //过滤类型,可以是transition或animation 可以为空,Vue内部会自动检测
var enterClass = data.enterClass; //获取进入过渡是的起始、结束和过渡时的状态对应的class
var enterToClass = data.enterToClass;
var enterActiveClass = data.enterActiveClass;
var appearClass = data.appearClass; //获取初次渲染时的过渡,分别是起始、结束和过渡时的状态对应的class
var appearToClass = data.appearToClass;
var appearActiveClass = data.appearActiveClass;
var beforeEnter = data.beforeEnter; //进入过渡前的事件,以下都是相关事件
var enter = data.enter;
var afterEnter = data.afterEnter;
var enterCancelled = data.enterCancelled;
var beforeAppear = data.beforeAppear;
var appear = data.appear;
var afterAppear = data.afterAppear;
var appearCancelled = data.appearCancelled;
var duration = data.duration; // activeInstance will always be the <transition> component managing this
// transition. One edge case to check is when the <transition> is placed
// as the root node of a child component. In that case we need to check
// <transition>'s parent for appear check.
var context = activeInstance; //当前transition组件的Vue实例vm
var transitionNode = activeInstance.$vnode; //占位符VNode
while (transitionNode && transitionNode.parent) { //如果transitoin组件是作为根节点的
transitionNode = transitionNode.parent; //则修正transitionNode为它的parent
context = transitionNode.context; //修正context为对应的parent的context
} var isAppear = !context._isMounted || !vnode.isRootInsert; //当前是否还未初始化 如果transition组件还没有挂载,则设置isAppear为true if (isAppear && !appear && appear !== '') { //如果appear为false(当前是初始化),且appear为false(即初始渲染时不使用过渡),或不存在
return //则直接返回,不做处理
} var startClass = isAppear && appearClass //进入过渡的起始状态
? appearClass
: enterClass;
var activeClass = isAppear && appearActiveClass //进入过渡时的状态
? appearActiveClass
: enterActiveClass;
var toClass = isAppear && appearToClass //进入过渡的结束状态
? appearToClass
: enterToClass; var beforeEnterHook = isAppear
? (beforeAppear || beforeEnter)
: beforeEnter;
var enterHook = isAppear
? (typeof appear === 'function' ? appear : enter)
: enter;
var afterEnterHook = isAppear
? (afterAppear || afterEnter)
: afterEnter;
var enterCancelledHook = isAppear
? (appearCancelled || enterCancelled)
: enterCancelled; var explicitEnterDuration = toNumber(
isObject(duration)
? duration.enter
: duration
); if ("development" !== 'production' && explicitEnterDuration != null) {
checkDuration(explicitEnterDuration, 'enter', vnode);
} var expectsCSS = css !== false && !isIE9; //是否使用 CSS 过渡类 IE9是不支持的
var userWantsControl = getHookArgumentsLength(enterHook); var cb = el._enterCb = once(function () { //完成后的回调函数
if (expectsCSS) {
removeTransitionClass(el, toClass);
removeTransitionClass(el, activeClass);
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, startClass);
}
enterCancelledHook && enterCancelledHook(el);
} else {
afterEnterHook && afterEnterHook(el);
}
el._enterCb = null;
}); if (!vnode.data.show) {
// remove pending leave element on enter by injecting an insert hook
mergeVNodeHook(vnode, 'insert', function () {
var parent = el.parentNode;
var pendingNode = parent && parent._pending && parent._pending[vnode.key];
if (pendingNode &&
pendingNode.tag === vnode.tag &&
pendingNode.elm._leaveCb
) {
pendingNode.elm._leaveCb();
}
enterHook && enterHook(el, cb);
});
} // start enter transition
beforeEnterHook && beforeEnterHook(el); //如果定义了beforeEnterHook钩子函数,则执行它,例子里的beforeenter会执行这里,输出:进入过渡前的事件
if (expectsCSS) { //如果expectsCSS为true
addTransitionClass(el, startClass); //给el元素新增一个class,名为startClass
addTransitionClass(el, activeClass); //给el元素新增一个class,名为activeClass
nextFrame(function () { //下次浏览器重绘时
removeTransitionClass(el, startClass); //移除startClass这个class ;因为有设置了activeClass,所以此时就会开始执行动画了
if (!cb.cancelled) { //如果cb.cancelled为空
addTransitionClass(el, toClass); //添加toClass这个class
if (!userWantsControl) {
if (isValidDuration(explicitEnterDuration)) { //如果用户自定义了动画时间
setTimeout(cb, explicitEnterDuration);
} else {
whenTransitionEnds(el, type, cb); //否则执行默认的whenTransitionEnds()函数(等到动画结束后就会执行cb这个回调函数了)
}
}
}
});
} if (vnode.data.show) {
toggleDisplay && toggleDisplay();
enterHook && enterHook(el, cb);
} if (!expectsCSS && !userWantsControl) {
cb();
}
}

resolveTransition会根据transitioin里的name属性自动拼凑css名,如下:

function resolveTransition (def) {        //第7419行 解析transition
if (!def) {
return
}
/* istanbul ignore else */
if (typeof def === 'object') { //如果def是一个对象
var res = {};
if (def.css !== false) { //如果css不等于false
extend(res, autoCssTransition(def.name || 'v')); //获取class样式
}
extend(res, def);
return res
} else if (typeof def === 'string') {
return autoCssTransition(def)
}
} var autoCssTransition = cached(function (name) {
return {
enterClass: (name + "-enter"),
enterToClass: (name + "-enter-to"),
enterActiveClass: (name + "-enter-active"),
leaveClass: (name + "-leave"),
leaveToClass: (name + "-leave-to"),
leaveActiveClass: (name + "-leave-active")
}
});

例子里执行到这里时返回的如下:

回到enter函数,最后会执行whenTransitionEnds函数,如下:

function whenTransitionEnds (       //第7500行 工具函数,当el元素的动画执行完毕后就去执行cb函数
el,
expectedType,
cb
) {
var ref = getTransitionInfo(el, expectedType); //获取动画信息
var type = ref.type; //动画的类型,例如:transition
var timeout = ref.timeout; //动画结束时间
var propCount = ref.propCount; //如果是transition类型的动画,是否有transform动画存在
if (!type) { return cb() }
var event = type === TRANSITION ? transitionEndEvent : animationEndEvent; //如果是transition动画则设置event为transitionend(transition结束事件),否则设置为animationend(animate结束事件)
var ended = 0;
var end = function () {
el.removeEventListener(event, onEnd);
cb();
};
var onEnd = function (e) { //动画结束事件
if (e.target === el) {
if (++ended >= propCount) {
end(); //如果所有的动画都执行结束了,则执行end()函数
}
}
};
setTimeout(function () {
if (ended < propCount) {
end();
}
}, timeout + 1);
el.addEventListener(event, onEnd); //在el节点上绑定event事件,当动画结束后会执行onEnd函数
}

getTransitionInfo用于获取动画的信息,返回一个对象格式,如下:

function getTransitionInfo (el, expectedType) {     //第7533行 获取el元素上上的transition信息
var styles = window.getComputedStyle(el); //获取el元素所有最终使用的CSS属性值
var transitionDelays = styles[transitionProp + 'Delay'].split(', '); //transition的延迟时间 ;比如:["0.5s"]
var transitionDurations = styles[transitionProp + 'Duration'].split(', '); //动画持续时间
var transitionTimeout = getTimeout(transitionDelays, transitionDurations); //获取动画结束的时间
var animationDelays = styles[animationProp + 'Delay'].split(', ');
var animationDurations = styles[animationProp + 'Duration'].split(', ');
var animationTimeout = getTimeout(animationDelays, animationDurations); var type;
var timeout = 0;
var propCount = 0;
/* istanbul ignore if */
if (expectedType === TRANSITION) { //如果expectedType等于TRANSITION(全局变量,等于字符串:'transition')
if (transitionTimeout > 0) {
type = TRANSITION;
timeout = transitionTimeout;
propCount = transitionDurations.length;
}
} else if (expectedType === ANIMATION) { //如果是animation动画
if (animationTimeout > 0) {
type = ANIMATION;
timeout = animationTimeout;
propCount = animationDurations.length;
}
} else {
timeout = Math.max(transitionTimeout, animationTimeout); //获取两个变量的较大值,保存到timeout里
type = timeout > 0
? transitionTimeout > animationTimeout //修正类型
? TRANSITION
: ANIMATION
: null;
propCount = type
? type === TRANSITION //动画的个数 transition可以一次性指定多个动画的,用,分隔
? transitionDurations.length
: animationDurations.length
: 0;
}
var hasTransform =
type === TRANSITION &&
transformRE.test(styles[transitionProp + 'Property']);
return { //最后返回一个动画相关的对象
type: type,
timeout: timeout,
propCount: propCount,
hasTransform: hasTransform
}
}

writer by:大沙漠 QQ:22969969

例子里返回后的对象信息如下:

回到whenTransitionEnds函数,等到动画结束时就会执行参数3,也就是enter函数内定义的cb局部函数,该函数最终会移除toClass和activeClass,最后执行afterEnter回掉函数。

Vue.js 源码分析(二十八) 高级应用 transition组件 详解的更多相关文章

  1. Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解

    对于过度动画如果要同时渲染整个列表时,可以使用transition-group组件. transition-group组件的props和transition组件类似,不同点是transition-gr ...

  2. Vue.js 源码分析(二十六) 高级应用 作用域插槽 详解

    普通的插槽里面的数据是在父组件里定义的,而作用域插槽里的数据是在子组件定义的. 有时候作用域插槽很有用,比如使用Element-ui表格自定义模板时就用到了作用域插槽,Element-ui定义了每个单 ...

  3. Vue.js 源码分析(二十四) 高级应用 自定义指令详解

    除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上 ...

  4. Vue.js 源码分析(二十五) 高级应用 插槽 详解

    我们定义一个组件的时候,可以在组件的某个节点内预留一个位置,当父组件调用该组件的时候可以指定该位置具体的内容,这就是插槽的用法,子组件模板可以通过slot标签(插槽)规定对应的内容放置在哪里,比如: ...

  5. Vue.js 源码分析(二十二) 指令篇 v-model指令详解

    Vue.js提供了v-model指令用于双向数据绑定,比如在输入框上使用时,输入的内容会事实映射到绑定的数据上,绑定的数据又可以显示在页面里,数据显示的过程是自动完成的. v-model本质上不过是语 ...

  6. Vue.js 源码分析(二十) 指令篇 v-once指令详解

    数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值,例如:<p>Message: {{ msg }}</p>以后每当msg属性发生了改变,插值处的内 ...

  7. jQuery 源码分析(二十) DOM操作模块 插入元素 详解

    jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...

  8. Vue.js 源码分析(六) 基础篇 计算属性 computed 属性详解

    模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护,比如: <div id="example">{{ messag ...

  9. Vue.js 源码分析(二十七) 高级应用 异步组件 详解

    当我们的项目足够大,使用的组件就会很多,此时如果一次性加载所有的组件是比较花费时间的.一开始就把所有的组件都加载是没必要的一笔开销,此时可以用异步组件来优化一下. 异步组件简单的说就是只有等到在页面里 ...

随机推荐

  1. SqlServer ----- 拷贝数据表

    两种方式,第一种方式只是把表中的字段进行拷贝,第二种把表中的关联关系,主键自增长全部拷贝. 1.把表中的关联关系,主键自增长全部拷贝. 选择需要拷贝的表,点击编辑器窗口,会看到这个表的sql 语句,主 ...

  2. shell 命令 tar -zxvf 解压 tar -zcvf 压缩

    tar -zxvf 解压 tar -zcvf 压缩

  3. 最近的项目系之3——core3.0整合Senparc

    1.前言 既然是.net下微信开发,自然少不了Senparc,可以说这个框架的存在, 至少节省了微信相关工作量的80%.事实上,项目开始前,还纠结了下是Java还是core,之所以最终选择core,除 ...

  4. Hive表导出成csv文件

    命令 hive -e " set hive.cli.print.header=true; #将表头输出 select * from data_table where some_query_c ...

  5. PlayJava Day004

    今日所学: /* 2019.08.19开始学习,此为补档. */ JDK 1.6:byte , int , short , char , enum JDK 1.7:byte , int , short ...

  6. Add a Class from the Business Class Library 从业务类库添加类 (XPO)

    In this lesson, you will learn how to use business classes from the Business Class Library as is. Fo ...

  7. Spring源码解析系列汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...

  8. CSS3 盒模型---css初始化会用到:box-sizing: border-box 盒子大小为 width 就是说 padding 和 border 是包含到width里面的

    CSS3中可以通过box-sizing 来指定盒模型,即可指定为content-box.border-box,这样我们计算盒子大小的方式就发生了改变. 可以分成两种情况: 1.box-sizing: ...

  9. Redis哨兵模式大key优化

    目前,Redis哨兵模式,内存资源有限,有很多key大于500M,性能待优化.需要迁移至Redis-cluster集群中.        涉及到的key如下: 0,hash,duser_record, ...

  10. opencv::基于颜色跟踪检测

    基于颜色跟踪 inRange过滤 形态学操作提取 轮廓查找 外接矩形获取 位置标定