Vue
-
什么是MVVM?
-
mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?
-
组件之间的传值?
-
Vue 双向绑定原理
-
描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
-
虚拟 DOM 实现原理
-
Vue 中 key 值的作用?
-
Vue 的生命周期
-
Vue 组件间通信有哪些方式?
-
vue 中怎么重置 data?
-
组件中写 name 选项有什么作用?
-
Vue 的 nextTick 的原理是什么?
-
Vuex 有哪几种属性?
Vue原型上的$nextTick
方法只是调用了nextTick方法,具体实现其实在nextTick中。
由于vm.$nextTick
会将回调添加到任务队列中延迟执行,所以在回调执行前,如果反复调用vm.$nextTick
,Vue.js并不会反复将回调添加到任务队列中,只会向任务队列中添加一个任务。(这个我们在上面说过了)
Vue.js内部有一个列表用来存储vm.$nextTick
参数中提供的回调。在一轮事件循环中,vm.$nextTick
只会向任务队列添加一个任务,多次使用vm.$nextTick
只会将回调添加到回调列表中缓存起来。当任务触发时,依次执行列表中的所有回调并清空列表。
(5)nextTick方法的实现方式
const callbacks = [];
let pending = false;
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++){
copiesi;
}
}
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;
microTimerFunc();
}
}
nextTick(function(){
console.log(this.name);//Berwin
},{name:‘Berwin’})
解释上面代码
1、通过数组callbacks来存储用户注册的回调。
2、声明了变量pending来标记是否已经向队列中添加一个任务了。每当向任务队列中插入任务时,将pending设置为true,每当任务被执行时将pending设置为false,这样就可以通过pending的值来判断是否需要向任务队列中添加任务。
3、函数flushCallbacks,即被注册的那个任务。当这个函数被触发时,会将callbacks中的所有函数依次执行,然后清空callbacks,并将pending设置为false。即一轮事件循环中,flushCallbacks只会执行一次。
4、microTimerFunc函数,它的作用是使用Promise.then将flushCallbacks添加到微任务队列中。这个其实就是我们所说的包装成异步。
5、执行nextTick函数注册回调时,首先将回调函数添加到callbacks中,然后使用pending判断是否需要向任务队列中新增任务。
在Vue.js2.4版本之前,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++){
copiesi;
}
}
let microTimerFunc;
let macroTimerFunc = function(){…}
let userMacroTask = false;
const p = Promise.resolve();
microTimerFunc = () =>{
p.then(flushCallbacks)
}
export function withMacroTask(fn){
return fn._withTask || (fn_withTask = function({
userMacroTask = true;
const res = fn.apply(null,arguments);
userMacroTask = false;
retrun res;
}))
}
export function nextTick(cb,ctx){
callbacks.push(()=>{
if(cb){
cb.call(ctx);
}
})
if(!pending){
pending = true;
if(userMacroTask){
macroTimerFunc();
}else{
microTimerFunc();
}
}
}
1、新增了withMacroTask函数,它的作用是给回调函数做一层包装,保证在整个回调函数执行过程中,如果修改了状态(数据),那么更新DOM的操作会被推到宏任务队列中,也就是说,更新DOM的执行时间会晚于回调函数的执行时间。
2、withMacroTask先将变量userMacroTask设置为true,然后执行回调,如果这时候回调中修改了数据(触发了更新DOM的操作),而userMacroTask是true,那么更新DOM的操作会被推送到宏任务队列中。当回调执行完毕后,将userMacroTask恢复为false。
3、被withMacroTask包裹的函数所使用的所有vm.$nextTick
方法都会将回调添加到宏任务队列中,其中包括状态被修改后触发的更新DOM的回调和用户自己使用vm.$nextTick
注册的回调等。
Vue.js优先使用setImmediate,但是它存在兼容性问题,只能在IE中使用,所以使用MessageChannel作为备选方案。如果浏览器也不支持MessageChannel,那么最后会使用setTimeout来将回调添加到宏任务队列中。
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 = () =>{
setTimeout(flushCallbacks,0);
}
}
microTimerFunc的实现原理是使用Promise.then,但并不是所有浏览器都支持Promise,当不支持时,会被降级成macroTimerFunc。
if(typeof Promise !== ‘undefined’ && isNative(Promise)){
const p = Promise.resolve();
microTimerFunc = () =>{
p.then(flushCallbacks);
}
}else{
microTimerFunc = macroTimerFunc;
}
官方文档中有一句话:如果没有提供回调且在支持Promise的环境中,则返回一个Promise。
this.$nextTick()
.then(function({
//DOM更新了
}))
要实现这个功能,需要在nextTick中进行判断,如果没有提供回调且当前环境支持Promise,那么返回Promsie,并且在callbacks中添加一个函数,当这个函数执行时,执行Promise 的resolve即可。
export function nextTick(cb,ctx){
let _resolve;
callbacks.push(()=>{
if(cb){
cb.call(ctx);
}else if(_resolve){
_resolve(ctx);
}
})
if(!pending){
pending = true;
if(userMacroTask){
macroTimerFunc();
}else{
microTimerFunc();
}
}
if(!cb && typeof Promise !== ‘undefined’){
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++){
copiesi;
}
}
let microTimerFunc;
let macroTimerFunc;
let userMacroTask = 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 = () =>{
setTimeout(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(){
userMacroTask = true;
const res = fn.apply(null,arguments);
userMacroTask = false;
retrun res;
})
}
export function nextTick(cb,ctx){
let _resolve;
callbacks.push(()=>{
if(cb){
cb.call(ctx);
}else if(_resolve){
ES6
-
列举常用的ES6特性:
-
箭头函数需要注意哪些地方?
-
let、const、var
-
拓展:var方式定义的变量有什么样的bug?
-
Set数据结构
-
拓展:数组去重的方法
-
箭头函数this的指向。
-
手写ES6 class继承。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
微信小程序
-
简单描述一下微信小程序的相关文件类型?
-
你是怎么封装微信小程序的数据请求?
-
有哪些参数传值的方法?
-
你使用过哪些方法,来提高微信小程序的应用速度?
-
小程序和原生App哪个好?
-
简述微信小程序原理?
-
分析微信小程序的优劣势
-
怎么解决小程序的异步请求问题?