Vue核心-深入本质
1. 虚拟DOM的本质
- 在 Vue 中,可以通过一个名叫 h 的函数,该函数的调用结果就是返回虚拟 DOM,也就意味着虚拟DOM的本质是由渲染函数调用生成的普通JS对象.
真实DOM和虚拟DOM都设计到了两个层面计算
真实DOM
- 解析字符串(JS层面)
- 创建DOM的节点(DOM层面)
虚拟DOM
- 创建Js对象(虚拟DOM, 属于Js层面)
- 根据 JS 对象创建对应的 DOM 节点(DOM 层面)
实际上无论使用虚拟 DOM 还是 innerHTML,在初始化的时候性能是相差无几的。虚拟 DOM 发挥威力的时候,实际上是在更新的时候。
计算层面
innerHTML 来更新涉及到的层面如下
- 销毁所有旧的DOM(DOM层面)
- 解析新的字符串(JS层面)
- 重新创建所有 DOM 节点(DOM层面)
如果使用虚拟 DOM,那么只有两个层面的计算:
- 使用 diff 计算出更新的节点(JS 层面)
- 更新必要的 DOM 节点(DOM 层面)
因此,总结一下,平时所说的虚拟DOM“快”,是有前提的:
- 首先看你和谁进行比较
- 如果是和原生 JS 操作 DOM 进行对比,那么虚拟 DOM 性能肯定更低而非更高,因为多了一层计算
- 其次就算你和 innerHTML 进行比较
- 初始化渲染的时候两者之间的差距并不大
- 虚拟 DOM 是在更新的时候相比 innerHTML 性能更高
总结:使用虚拟 DOM 是为了防止组件在 重渲染 时导致的性能恶化。
2. 模板的本质
- Vue 里面的单文件组件是会被一个 模板编译器 进行编译的,编译后的结果并不存在什么模板,而是会把模板编译为渲染函数的形式。
这意味着我们完全可以使用纯 JS 来书写组件,文件的内部直接调用渲染函数来描述你的组件视图。
vue3的方式
import { defineComponent, h } from 'vue'
import styles from './Card.module.css'
export default defineComponent({
name: 'Card',
props: {
name: String,
email: String,
img: String
},
setup(props) {
// 下面我们使用了渲染函数的形式来描述了原本在模板中所描述的视图结构
return () =>
h(
'div',
{
class: styles.userCard
},
[
h('img', {
class: styles.avatar,
src: props.img,
alt: 'img img'
}),
h(
'div',
{
class: styles.userInfo
},
[h('h2', props.name), h('p', props.email)]
)
]
)
}
})
甚至也可以使用 Vue2 经典的 options API 的语法来写:
import styles from './UserCard.module.css'
import { h } from 'vue'
export default {
name: 'UserCard',
props: {
name: String,
email: String,
avatarUrl: String
},
render() {
return h(
'div',
{
class: styles.userCard
},
[
h('img', {
class: styles.avatar,
src: this.avatarUrl,
alt: 'User avatar'
}),
h(
'div',
{
class: styles.userInfo
},
[h('h2', this.name), h('p', this.email)]
)
]
)
}
}
- 至此我们就知道了,Vue 里面之所以提供模板的方式,是为了让开发者在描述视图的时候,更加的轻松。Vue 在运行的时候本身是不需要什么模板的,它只需要渲染函数,调用这些渲染函数后所得到的虚拟 DOM.
模板的编译
单文件组件中所书写的模板,对于模板编译器来讲,就是普通的字符串。
模板内容:
<template>
<div>
<h1 :id="someId">Hello</h1>
</div>
</template>
对于模板编译器来讲,仅仅是一串字符串:
'<template><div><h1 :id="someId">Hello</h1></div></template>'
模板编译器需要对上面的字符串进行操作,最终生成的结果:
function render(){
return h('div', [
h('h1', {id: someId}, 'Hello')
])
}
模板编译器在对模板字符串进行编译的时候,是一点一点转换而来的,整个过程
模板---> 解析器 ---> 转换器---> 生成器
解析器:负责将模板字符串解析为对应的模板AST
转换器:负责将模板AST转换为 JS AST
生成器:将 JS AST 生成最终的渲染函数
编译的时机
整体来讲会有两种情况:
- 运行时编译:是直接通过 CDN 的方式引入的 Vue
- 预编译: 预编译是发生在工程化环境下面。
- 所谓预编译,指的是工程打包过程中就完成了模板的编译工作,浏览器拿到的是打包后的代码,是完全没有模板的。
3. 组件树和虚拟DOM树
- 组件:组件的本质就是对一组 DOM 进行复用。
- 组件树:指的是一个一个组件所形成的树结构。
- 虚拟 DOM 树:指的是某一个组件内部的虚拟 DOM 数据结构,并非整个应用的虚拟 DOM 结构。
组件和组件之间就形成了树结构,这就是组件树,而每个组件的背后,对应的是一组虚拟 DOM,虚拟 DOM 的背后又是真实 DOM 的映射:
本文作者:HuangBingQuan
本文链接:https://www.cnblogs.com/bingquan1/p/18668944
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步