Vue源码
1.真实DOM渲染
- 在传统的前端开发中,我们是编写HTML然后最终被渲染到浏览器上的,那么它是什么样的过程呢?
<!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">
<title>Document</title>
</head>
<body>
<div>
<ul>
<li>邓</li>
<li>典</li>
<li>果</li>
</ul>
</div>
</body>
</html>
[注意] html会编译成dom树结构,浏览器是可以直接认识这样的结构,最后会在页面中展示
2.虚拟DOM优势
-
目前框架都会引入虚拟的DOM来对真实的DOM进行抽象,这样做有啥好处呢?
-
首先是可以对真实的元素节点进行抽象,抽象成VNode(虚拟节点),这样方便后续对其进行各种操作
- 因为对于直接操作DOM是非常有限制,比如diff,clone等等,但是使用JS编程语言操作这些,就变得非常简单
- 我们其实可以用JS来表达非常多的逻辑,而对于DOM本身来说是非常不便的
-
其次就是方便实现跨品台,包括你可以将VNode节点渲染成任意想要的节点
- 如渲染canvas,WebGL,SSR,Native(IOS,Android)上
- 并且允许开发属于自己的渲染器(render),在其他的平台进行渲染。
3.虚拟DOM的渲染过程
4.Vue之三大核心
-
Vue源码包含三大核心:
- Compiler模块:编译模版系统
- Runtime模块:Renderer模块,真正渲染的模块
- Reactivity模块:响应式系统
5.Mini-Vue
-
通过以上理解的源码来实现一个简单版的Vue框架,包括三个模块:
- 渲染系统模块(runtime) -> vnode ->真实DOM
- 可响应式系统模块 (reactive)
- 应用程序入口模块 (createApp(rootComponent).mount("#app"))
6.渲染系统实现
渲染系统,这个模块主要包含三个功能
1. h函数,用于返回一个VNode对象
2. mount函数,用于将VNode挂载到DOM上
3. patch函数,用于对两个VNode进行对比,决定如何处理心的VNode
<!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">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./renderer.js"></script>
<script>
//1.通过h函数来创建一个vnode
const vnode = h("div",{class:"active"},[
h2("h2",null,"当前计数")
h2("button",null,"+1")
])
console.log(vnode)//看下面这张图
//2.通过mount函数,将vnode挂载到div#app上
mount(vnode,document.querySelector("#app"))
</script>
</body>
</html>
- renderer.js
//renderer.js
const h = (tag,props,children) => {
//VNode => javascript对象 => {}
return {
tag,
props,
children,
}
}
//把vnode拿到之后放在某个容器里面
const mount = (vnode,container) => {
//1.先将虚拟dom vnode -> 转为 真实DOM
//创建出真实的元素,然后在对象中追加保留一个el
const el = vnode.el = document.createElement(vnode.tag)
//2.处理props
if(vnode.props) {
for(const key in vnode.props) {
const value = vnode.props[key]
if(key.startsWith("on")) {
el.addEventListener(key.slice(2).toLowerCase(),value)
}else {
el.setAttribute(key,value)
}
}
}
//3.处理children
if(vnode.children) {
if(typeof vnode.children === "string") {
el.textContent = vnode.children
}else {
vnode.children.forEach(item => {
mount(item,el)
})
}
}
//4.将el挂载到container中
container.appendChild(el)
}