💡带你读懂核心源码且实现Mini-Vue 系列一

232 阅读2分钟

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)
}