背??景和总结先说结论:有很多所谓的最佳实践告诉你这个观点,但实际上这个观点是非常片面的,甚至在大多数场景下都是错误的。声明一下:本文用几个案例来说明一些同行观点的片面性,分析这种观点的原因,稍微涉及到Vue的渲染原理。最后给出一个比较新近的做法(因人而异),个人可以借鉴,避免苦恼。很多文章分析给出的原因是:调用mounted回调函数时,组件已经挂载到DOM上,但是mounted之前的生命周期还没有挂载。这句话本身没有错,但是和mounted中异步请求的执行没有必然的因果关系。关键是mounted回调执行的时候,组件已经挂载到DOM上了,但是我们的异步请求和DOM是什么关系呢?没有直接的关系,虽然我们请求的结果需要通过DOM呈现在界面上,用户才能看到。但是,别忘了,通常我们的请求结果是经过处理后复制到data的一个属性中的,像这样:getData(){axios.get(url).then(response=>{//这里只是一个复制操作this.dataset=response.data})}我们只是做了一个赋值操作,并没有操作DOM。之后这个赋值操作会被Vue拦截,然后调用render函数生成新的vnode,进一步diff找出需要更新的属性、节点、样式等,并最后才是真正的读写DOM的操作。这也是为什么说异步请求应该在挂载的回调中执行,因为赋值操作会引起DOM的操作。但是这个理由根本站不住脚,关键就在异步这个词上。如果您正在发出同步请求,那么我同意上述内容,但抱歉,您不是同步的。让我们看一下案例并比较两个例子:首先,我们在创建和修改请求的属性中有一个异步操作。{{requested}}
结果:代码运行正常,界面显示正常,没有出现你担心的created中读写DOM错误的问题。而且从输出日志可以看出,异步回调是mounted后执行的,这个很重要。如果换成同步操作呢?{{requested}}
结果:代码运行正常,界面显示正常,读写问题您担心的创建的DOM错误没有发生。从输出日志可以看出,created中的同步操作是在mounted之前执行的。这里就奇怪了,虽然我们在data中修改了一个state会导致vue读写DOM,但是此时并没有报错。vue渲染原理(部分)其实这里需要简单了解一下vue-diff的渲染过程,里面会比较oldVnode和newVnode。一开始,有一个非常简单的逻辑。下面是伪代码:if(!oldVnode){//没有前面的vnode,说明是第一次渲染//直接执行mount方法,不需要其他比较mountComponent()}else{//比较的普通属性,子节点等}所以,created就不用担心同步修改状态下读写DOM错误的问题了。在哪里放置异步请求?业内同行建议在mounted中执行异步代码是一个相对安全的选择。对于一些初学者来说,避免了业务开发的难度和疑难问题的频发。由于各种前端开发者对Vue原理的理解不足,所以根据自己的经验给出这个最佳实践无可厚非。其实从上面的案例可以看出,在created中是可以执行异步/同步请求的。当然有一个前提:请求的回调中不能直接读写DOM。甚至,你的异步(非同步)请求在beforeCreate中执行也是可行的,而且在请求的回调中不能直接读写DOM还是有前提的。{{requested}}
结果:代码执行成功,界面正常显示,不可思议。哈哈,有一点很重要。vue中的init、beforeCreate、created、beforeMount、mounted等生命周期钩子的调用是同步的,所以异步请求回调只会在这些同步钩子执行完后才会执行(可能意味着你要等待其他同步任务来完成)完全的)。这里的原理涉及到js的事件循环,但是如果你有兴趣,可以看这篇文章【JS】深入理解事件循环,这篇文章就够了!。另外,以上分析案例均基于vue2.x版本。其实用vue3.x,结论还是成立的。综上所述,如果你是初学者或者只是临时用Vue实现简单的业务,不想深入太深,那么直接在mounted中执行异步代码一般不会有明显问题;如果你追求更好的性能,并且对vue有更深刻的理解,那么你不妨更深入地思考最佳实践的原因,它是否适合你和你的项目。
