起因
引入vue tag 这段代码在ant-design-vue 1.4.9版本生效,1.5.4 版本不生效 nextTick不生效
<template>
<div>
<template v-for="(tag, index) in tags">
<a-tooltip v-if="tag.length > 20" :key="tag" :title="tag">
<a-tag :key="tag" :closable="index !== 0" @close="() => handleClose(tag)">
{{ `${tag.slice(0, 20)}...` }}
</a-tag>
</a-tooltip>
<a-tag v-else :key="tag" :closable="index !== 0" @close="() => handleClose(tag)">
{{ tag }}
</a-tag>
</template>
<a-input
v-if="inputVisible"
ref="input"
type="text"
size="small"
:style="{ width: '78px' }"
:value="inputValue"
@change="handleInputChange"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<a-tag v-else style="background: #fff; borderStyle: dashed;" @click="showInput">
<a-icon type="plus" /> New Tag
</a-tag>
</div>
</template>
<script>
export default {
data() {
return {
tags: ['Unremovable', 'Tag 2', 'Tag 3Tag 3Tag 3Tag 3Tag 3Tag 3Tag 3'],
inputVisible: false,
inputValue: '',
};
},
methods: {
handleClose(removedTag) {
const tags = this.tags.filter(tag => tag !== removedTag);
console.log(tags);
this.tags = tags;
},
showInput() {
this.inputVisible = true;
this.$nextTick(function() {
this.$refs.input.focus();
});
},
handleInputChange(e) {
this.inputValue = e.target.value;
},
handleInputConfirm() {
const inputValue = this.inputValue;
let tags = this.tags;
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue];
}
console.log(tags);
Object.assign(this, {
tags,
inputVisible: false,
inputValue: '',
});
},
},
};
</script>
上诉代码在一个Dialog执行,在ant-design-vue 1.4.9版本生效,1.5.4版本,
showInput(){
this.$nextTick(function() {
this.$refs.input.focus();
});
}
不生效。nextTick并不是在页面重绘之后执行,也就是Input被创建,有了引用之后,才执行,此时获取的this.$refs.input仍然为undefined,会报错。 并且,该代码运行在blog.codepen.io/documentati… 1.5.4版本不存在问题,当然运行在codepen上的代码没有dialog包裹,不知道与这个原因有关否
解决方案
让nextTick的返回结果为一个promise,在promise结束之后,再去调用 this.$refs.input.focus();
this.$nextTick().then( ()=> {
console.log(this.$refs.inputRef)
this.inputRef.focus();
});
因为this.$nextTick(cb?:Fucntion)不传递时,就返回一个Promise
可看this.$nextTick源码
//将nextTick定义到Vue原型链上代码位于src/core/instance/render.js
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
//传入回调函数cb,如果cb为undefined并且该运行环境支持Promise ,就返回一个Promise
export function nextTick (cb?: Function, ctx?: Object) { // next-tick.js
//是否完成的函数
let _resolve
callbacks.push(() => {
//如果cb不为undefined
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
或者
setTimeout(() => {
this.input.focus();
})
可知传入的回调函数,并非如同官网所说
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM 猜测,没有数据的改变,只是v-if来控制显示,有可能正在DOM更新循环前执行的cb正在执行
宏任务 与 微任务
第一个例子
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
打印:
script start
script end
promise1
promise2
setTimeout
原理
为什么Promise比setTimeout先执行
st=>start: 任务进入执行栈
cond=>condition: 是否是同步任务
asyncOp1=>operation: Event Table
asyncOp2=>operation: Event Queue
op1=>operation: 主线程
op2=>operation: 任务全部执行完毕
e=>end: 读取任务队列的结果,进入主线程执行
st->cond->e
cond(yes)->op1->op2->e
cond(no)->asyncOp1->asyncOp2->e
console.log('script start');//同步任务,放入主线程任务栈
setTimeout(function() { //异步任务,放入事件队列
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {//异步任务 注意这里是resolve.then()调用的函数
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');//放入主线程任务栈
现在任务栈的顺序:同步任务, 异步任务
异步任务又分为宏任务和微任务
是需要先执行微任务再执行宏任务
Promise中的then方法的函数会被推入 microtasks 队列,而setTimeout的任务会被推入 macrotasks 队列。在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直提取,直到 microtasks 队列清空。
继续解决Promise和setTimeout
setTimeout(function(){
console.log(4)
},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
i==9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
}
);
console.log(3);
我的解析:先执行同步任务,log3,然后执行微任务Promise ,回调函数,log1,log2,达到i===9999时,进入resolve函数,调用log5,执行完毕微任务,调用下一个宏任务,setTimeout log4
我的答案是3,1,2,5,4 错在没看清楚new Promise构造函数传递call call是同步的
正确的答案是1,2,3,5,4
正确的解析:整个script是一个宏任务,setTimeout是一个宏任务,Promise.then是微任务,new Promise(cb)这一句是宏任务中顺序执行的语句
深入的解析Promise和setTimeout的问题
setTimeout不属于ECMA规范,这是浏览器宿主引擎window上的对象,其他环境(Windows脚本宿主,NodeJS等)不一定具有这些功能 可查看该答案获取信息 stackoverflow.com/questions/8… 会延迟4s setTimeout嵌套超过5层,会有一个4ms的最小延迟
Event loops
8.1.4 Event loops 8.1.4.1 Definitions To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. Each agent has an associated event loop, which is unique to that agent. The event loop of a similar-origin window agent is known as a window event loop. The event loop of a dedicated worker agent, shared worker agent, or service worker agent is known as a worker event loop. And the event loop of a worklet agent is known as a worklet event loop.(为了协调事件,用户交互,脚本,渲染,发起网络请求,诸如此类的事件,用户代理必须本章节描述的使用事件循环。每一个代理都会拥有一个事件循环,对于这个代理(环境)是唯一的。比如,类似源窗口事件循环被称为窗口事件循环。事件循环的代理被称为事件循环)
Event loops do not necessarily correspond to implementation threads. For example, multiple window event loops could be cooperatively scheduled in a single thread.(事件循环并不等于实现了线程。举例,多个窗口循环事件可以被一个单线程协同着调度)
However, for the various worker agents that are allocated with [[CanBlock]] set to true, the JavaScript specification does place requirements on them regarding forward progress, which effectively amount to requiring dedicated per-agent threads in those cases. (然而,对于各种分配与[[CanBlock]]设置为true的代理引擎, JavaScript规范并要求他们关于进步,这有效地要求独立的每个代理线程在这些情况下。)
An event loop has one or more task queues. A task queue is a set of tasks.(一个事件循环可以有一个或多个任务队列。一个任务队列可以是多个的任务的集合。)
Task queues are sets, not queues, because step one of the event loop processing model grabs the first runnable task from the chosen queue, instead of dequeuing the first task. (任务队列是一个集合,不是队列。因为事件循环处理模型的第一步从选择的队列中获取第一个可运行任务,而不是使第一个任务出列。)
The microtask queue is not a task queue. (微任务队列并不是一个任务队列)
Tasks encapsulate algorithms that are responsible for such work as:(封装好的任务算法负责以下工作:) Events Dispatching an Event object at a particular EventTarget object is often done by a dedicated task.(在特定EventTarget对象上调度事件对象通常被一个独立任务完成) Not all events are dispatched using the task queue; many are dispatched during other tasks. (并不是所有的事件都被一个任务队列调度,很多事件被其他的任务队列调度。)
Parsing The HTML parser tokenizing one or more bytes, and then processing any resulting tokens, is typically a task.(HTML解析器分词一个或多个字节,然后处理每一个结果分词,这是一个任务。) Callbacks Calling a callback is often done by a dedicated task.(调用回调通常由独立任务完成。)
Using a resource When an algorithm fetches a resource, if the fetching occurs in a non-blocking fashion then the processing of the resource once some or all of the resource is available is performed by a task.(当一个算法拉取资源,并且以一种非阻塞的方式发生,一旦一些货者全部资源处于可用的状态后,执行这个任务)
html.spec.whatwg.org/multipage/w…
各任务的优先级
setImmediate(function () {
console.log(1);
}, 0);
setTimeout(function () {
console.log(2);
}, 0);
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
答案 :3 4 6 8 7 5 2 1
微任务 宏任务 的优先级 process.nextTick > promise.then > setTimeout > setImmediate
微任务
宏任务
WHATWG规范 html.spec.whatwg.org/ ECMA官方文档 www.ecma-international.org/ www.zhihu.com/question/36… www.cnblogs.com/rogerwu/p/1… 谷歌开发者 jakearchibald.com/2015/tasks-…