前言
tips:第一次发技术文章,篇幅比较简短,主要采取文字和关键代码表现的形式,希望帮助到大家。(若有不正确还请多多指正)
nextTick作用和用法
用法:nextTick接收一个回调函数作为参数,它的作用是将回调延迟到下一次DOM更新之后执行,如果没有提供回调函数参数且在支持Promise的环境中,nextTick将返回一个Promise。
适用场景:开发过程中,开发者需要在更新完数据之后,需要对新DOM做一些操作,其实我们当时无法对新DOM进行操作,因为这时候还没有重新渲染,这时候nextTick就派上了用场。
nextTick实现原理
下面我们介绍下nextTick工作原理:
首先我们应该了解到更新完数据(状态)之后,DOM更新这个动作并不是同步进行的,而是异步的。Vue.js中有一个队列,每当需要渲染时,会将Watcher推送到这个队列中,等下一次事件循环中再让Watcher触发渲染流程。这里我们可能会有两个疑问:
**1.为什么更新DOM是异步的?**
我们知道从Vue2.0开始使用虚拟DOM进行渲染,变化侦测只发送到组件级别,组件内部则通过虚拟DOM的diff(比对)而进行局部渲染,而在同一次事件循环中组件假如收到两份通知,组件是否会进行两次渲染呢?事实上一次事件循环组件会在所有状态修改完毕之后只进行一次渲染操作。
**2.什么是事件循环?**
javascript是单线程脚本语言,它具有非阻塞特性,之所以非阻塞是由于在处理异步代码时,主线程会挂起这个任务,当异步任务处理完毕之后会根据一定的规则去执行异步任务的回调,异步任务分宏任务(macrotast)和微任务(microtast),它们会被分配到不同的队列中,当执行栈所有任务执行完毕之后,会先检查微任务队列中是否有事件存在,优先执行微任务队列事件对应的回调,直至为空。然后再执行宏任务队列中事件的回调。无限重复这个过程,形成一个无限循环就叫做事件循环。
常见微任务包括:Promise 、MutationObserver、Object.observer、process.nextTick等
常见宏任务包括:setTimeout、setInterval、setImmediate、MessageChannel、requestAnimation、UI交互事件等
微任务如何注册"htmlcode">
由于微任务优先级太高,可能在某些场景下需要使用到宏任务,所以Vue提供了可以强制使用宏任务的方法withMacroTask。具体实现如下: 上面提供了一个withMacroTask方法强制使用宏任务,通过useMacroTask变量进行控制是否使用注册宏任务执行,withMacroTask实现很简单,先将useMacroTask变量设置为true,然后执行回调,回调执行之后再改回false。 宏任务是如何注册? 注册宏任务优先使用setImmediate,但是存在兼容性问题,只能在IE中使用,所以使用MessageChannel作为备选方案,若以上都不支持则最后会使用setTimeout。具体实现如下: microTimerFunc的实现方法是通过Promise.then,但是并不是所有浏览器都支持Promise,当不支持的时候采取降级为宏任务方式 若未提供回调且环境支持Promise情况下,nextTick会返回一个Promise,具体实现如下: 以上是nextTick运行原理的设计,完整代码如下: 以上便是对nextTick的实现原理的全部介绍。 参考资料 Vue.js深入浅出 总结
const callbacks = []
let pending = false
function flushCallbacks(){ //执行回调
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0 //清空回调队列
for(let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let microTimerFunc
const p = Promise.resolve()
microTimerFunc = () => { //注册微任务
p.then(flushCallbacks)
}
export function nextTick(cb,ctx){
callbacks.push(()=>{
if(cb){
cb.call(ctx)
}
})
if(!pending){
pending = true //将pending设置为true,保证任务在依次事件循环中不会重复添加
microTimerFunc()
}
}
const callbacks = []
let pending = false
function flushCallbacks(){ //执行回调
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0 //清空回调队列
for(let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let microTimerFunc
//新增代码
let macroTimerFunc = function(){
...
}
let useMacroTask = false
const p = Promise.resolve()
microTimerFunc = () => { //注册微任务
p.then(flushCallbacks)
}
//新增代码
export function withMacroTask(fn){
return fn._withTask || fn._withTask = function()=>{
useMacroTask = true
const res = fn.apply(null,arguments)
useMacroTask = false
return res
}
}
export function nextTick(cb,ctx){
callbacks.push(()=>{
if(cb){
cb.call(ctx)
}
})
if(!pending){
pending = true //将pending设置为true,保证任务在依次事件循环中不会重复添加
//修改代码
if(useMacroTask){
macroTimerFunc()
}else{
microTimerFunc()
}
}
}
if(typeof setImmediate !== 'undefined' && isNative(setImmediate)){
macroTimerFunc = ()=>{
setImmediate(flushCallbacks)
}
} else if(
typeof MessageChannel !== 'undefined' &&
(isNative(MessageChannel) || MessageChannel.toString() === '[Object MessageChannelConstructor]')
){
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = ()=>{
port.postMessage(1)
}
} else {
macroTimerFunc = ()=>{
setTimout(flushCallbacks,0)
}
}
if(typeof Promise !== 'undefined' && isNative(Promise)){
const p = Promise.resolve()
microTimerFunc = ()=>{
p.then(flushCallbacks)
}
} else {
microTimerFunc = macroTimerFunc
}
export function nextTick(cb, ctx) {
let _resolve
callbacks.push(()=>{
if(cb){
cb.call(ctx)
}else{
_resolve(ctx)
}
})
if(!pending){
pending = true
if(useMacroTask){
macroTimerFunc()
}else{
microTimerFunc()
}
}
if(typeof Promise !== 'undefined' && isNative(Promise)){
return new Promise(resolve=>{
_resolve = resolve
})
}
}
const callbacks = []
let pending = false
function flushCallbacks(){ //执行回调
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0 //清空回调队列
for(let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let microTimerFunc
let macroTimerFunc
let useMacroTask = false
//注册宏任务
if(typeof setImmediate !== 'undefined' && isNative(setImmediate)){
macroTimerFunc = ()=>{
setImmediate(flushCallbacks)
}
} else if(
typeof MessageChannel !== 'undefined' &&
(isNative(MessageChannel) || MessageChannel.toString() === '[Object MessageChannelConstructor]')
){
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = ()=>{
port.postMessage(1)
}
} else {
macroTimerFunc = ()=>{
setTimout(flushCallbacks,0)
}
}
//微任务注册
if(typeof Promise !== 'undefined' && isNative(Promise)){
const p = Promise.resolve()
microTimerFunc = ()=>{
p.then(flushCallbacks)
}
} else {//降级处理
microTimerFunc = macroTimerFunc
}
export function withMacroTask(fn){
return fn._withTask || fn._withTask = function()=>{
useMacroTask = true
const res = fn.apply(null,arguments)
useMacroTask = false
return res
}
}
export function nextTick(cb,ctx){
let _resolve
callbacks.push(()=>{
if(cb){
cb.call(ctx)
}else{
_resolve(ctx)
}
})
if(!pending){
pending = true //将pending设置为true,保证任务在依次事件循环中不会重复添加
//修改代码
if(useMacroTask){
macroTimerFunc()
}else{
microTimerFunc()
}
}
if(typeof Promise !== 'undefined' && isNative(Promise)){
return new Promise(resolve=>{
_resolve = resolve
})
}
}
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 第五街的士高《印度激情版》3CD [WAV+CUE][2.4G]
- 三国志8重制版哪个武将智力高 三国志8重制版智力武将排行一览
- 三国志8重制版哪个武将好 三国志8重制版武将排行一览
- 三国志8重制版武将图像怎么保存 三国志8重制版武将图像设置方法
- 何方.1990-我不是那种人【林杰唱片】【WAV+CUE】
- 张惠妹.1999-妹力新世纪2CD【丰华】【WAV+CUE】
- 邓丽欣.2006-FANTASY【金牌大风】【WAV+CUE】
- 饭制《黑神话》蜘蛛四妹手办
- 《燕云十六声》回应跑路:年内公测版本完成95%
- 网友发现国内版《双城之战》第二季有删减:亲亲环节没了!
- 邓丽君2024-《漫步人生路》头版限量编号MQA-UHQCD[WAV+CUE]
- SergeProkofievplaysProkofiev[Dutton][FLAC+CUE]
- 永恒英文金曲精选4《TheBestOfEverlastingFavouritesVol.4》[WAV+CUE]
- 群星《国风超有戏 第9期》[320K/MP3][13.63MB]
- 群星《国风超有戏 第9期》[FLAC/分轨][72.56MB]