从源码解惑-$mount执行后,被挂载的节点最后是如何处理的
2020/5/13 11:26:14
本文主要是介绍从源码解惑-$mount执行后,被挂载的节点最后是如何处理的,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
问题描述
当你用vue-cli创建一个工程后,会看到index-html文件里有一个div,id叫app
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title></title> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app">app</div> <!-- built files will be auto injected --> </body> </html> 复制代码
此时App.vue文件中也有一个div,id也叫app
<template> <div id="app"> hello world! </div> </template> 复制代码
但是根据规定,id应该具有唯一性,那么问题来了,我们在main.js里面挂载的那个#app是哪个?最终渲染到页面上的那个#app又是哪一个呢?
new Vue({ router, render: (h: any) => h(App), }).$mount('#app'); 复制代码
想要知道这些问题的答案,我们可以做一个简单的实验,比如把这两个文件中的某一个id改掉,看一下页面中最终渲染的是哪个,看一下哪个文件中的id改变会导致执行**$mount('#app')**报错,可以很轻松的得出结论。
但是本着知其然,知其所以然的态度,我们打开源码验证一下猜想
验证猜想
为了方便查找到我们要看的源码,所以选择在浏览器打断点调试。那么断点打在哪里比较合适呢,有两种思路
- 将断点打在app.js的new Vue()这行代码,顺着vue初始化的流程去追,追到页面dom节点渲染出来时,就可以往回找,通过打新的断点不断往下追,但是因为vue项目非常庞大,这种方法追踪起来效率很低,很容易迷失。(别问我怎么知道的)
- 第二种方法需要对vue源码项目的目录结构有一定了解,因为这种大型项目的文件分组都是有一定规律的。vue将所有dom更新的方法都放在了patch中,所以我们打开patch.js。这里需要做一个设想,声明了两个#app,最后渲染出来只有一个,那么肯定移除了另一个。所以我们要在这个文件中找一下有没有关于移除dom的方法,果然找到了removeNode,于是我们将断点打在这个方法上,刷新页面,果然进入了这个断点,这也验证了我刚才的猜想
function removeNode (el) { const parent = nodeOps.parentNode(el) // element may have already been removed due to v-html / v-text if (isDef(parent)) { nodeOps.removeChild(parent, el) } } 复制代码
我们将鼠标悬浮在入参el上,可以看到,要被移除的元素是index.html里的#app,而且此时App组件中的内容已经渲染到了页面上,所以另一个#app很大概率是被append到body里面的,如果想分析另一个元素挂载过程,同样可以在patch中找到相应的方法打上断点直接调试
结论
讲到这里,结论基本上已经出来了,那就是在index.html和App.vue中存在两个#app,经过$mount挂载后,最终渲染在body中的是App.vue中的那个#app,index.html中的#app则会被移除。
引申
不知道大家有没有想过作者为什么要这么设计呢?乍一看有点多此一举的感觉。我说一下我的想法。
- 我们现在的spa项目越做越大,即使做了懒加载,但是首屏以然需要加载很多基础环境地方js包,所以避免不了需要等待,那么这个时候,如果我们在index.html的#app里面写一个loading动画,那么在实际的App.vue内容没有被渲染出来,我们就能在页面上看到一个loading状态,能够显著的提升用户体验。当App.vue中的内容渲染完后,之前的节点就会被移除,loading动画自然也就消失了。
- 第二点其实还是关于页面等待体验优化的地方,那就是骨架屏。现在的很多h5页面都采用了骨架屏,不管方案如何,最终骨架屏在正式的内容加载出来后都应该自己消失的,那么写在这里也很合适、
- 至于其他的点,我暂时还没想出来,欢迎大家评论补充
另外,有了完整的调用堆栈信息,想要深挖$mount过程就很容易了
这篇关于从源码解惑-$mount执行后,被挂载的节点最后是如何处理的的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求