基于 vue3源码 尝试 mini-vue 的实现

本文详细解析了如何基于Vue3源码实现mini-vue,包括渲染系统中的h函数、创建和挂载vnode,响应式系统的依赖收集与proxy劫持,以及diff算法的优化。作者通过实例展示了如何构建一个简单的mini-vue程序并进行测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于 vue3源码 尝试 mini-vue 的实现

预览:
gif01

1. 实现思路

  • 渲染系统模块
  • 响应式系统
  • mini-vue 程序入口

2. 渲染系统模块

2.1 初识 h 函数

以下是 vue 的模版语法代码:

<template>
	<div class='container'>hello mini-vue</div>
</template>

它并不是传统的 html 代码,而是通过 h 函数生成的虚拟 dom 节点,h 函数接收 3 个参数:

  • 第一个参数是标签名 (tag),此例中为 ‘div’
  • 第二个参数是标签的属性 (props), 此例中为 ‘container’
  • 第三个参数是子节点,可以是字符串、数组 (children), 此例中为 ‘hello mini-vue’
/**
 * h 函数
 * 功能:返回vnode
 *
 * @param {String} tagName  - 标签名
 * @param {Object | Null} props  - 传递过来的参数
 * @param {Array | String} children  - 子节点
 * @return {vnode} 虚拟节点
 */
const h = (tagName, props, children) => {
   
    // 直接返回一个对象,里面包含vnode结构
    return {
   
        tagName,
        props,
        children,
    };
};

export default h;

2.2 创建一个 vnode

vnode 就是虚拟 dom 节点,创建方法很简单:

h(
    'div', {
    class: 'container' }, [
        h('h1', {
   }, `文本:${
     this.data.msg},可变数字:${
     this.data.count}`),
        h('button', {
   
            onclick: () =>{
   
                this.data.msg = 'hello miniVue',
                    this.data.count++
            }
        }, '点击试试'),
    ]
)

2.3 挂载真实 DOM

mount函数的主要功能是将虚拟节点(vnode)挂载到真实的DOM容器(container)中:

  1. 创建真实元素:

    • 使用 document.createElement 创建一个具有指定标签名的真实元素。
    • 将创建的元素保存在 vnode.el 属性中,以便后续的操作。
  2. 处理属性(props):

    • 遍历虚拟节点的 props 属性,分别处理函数类型的事件监听器和其他类型的属性。
    • 如果属性名以 “on” 开头,将其作为事件处理函数添加到元素上。
    • 否则,使用 setAttribute 方法设置元素的属性。
  3. 处理子节点(children):

    • 如果虚拟节点有子节点,分两种情况处理:
      • 如果子节点是字符串,直接设置元素的文本内容。
      • 如果子节点是数组,递归调用 mount 函数挂载每个子节点。
  4. 挂载到容器中:

    • 最后,使用 container.appendChild 将创建的真实元素挂载到指定的DOM容器中。

这个mount函数的重点在于递归调用,通过递归处理子节点,实现了对整个虚拟DOM树的挂载。

/**
 * mount 函数
 * 功能:挂载 vnode 为 真实dom
 * 重点:递归调用处理子节点
 *
 * @param {Object} vnode -虚拟节点
 * @param {elememt} container -需要被挂载节点
 */
const mount = (vnode, container) => {
   
  console.log(vnode);
  // 1. 创建出真实元素, 同时给 vnode 添加 el 属性
  const el = (vnode.el = document.createElement(vnode.tagName));

  // 2. 处理 props
  if (vnode.props) {
   
    for (const key in vnode.props) {
   
      const value = vnode.props[key];

      // 2.1 prop 是函数
      if (key.startsWith("on")) {
   
        el.addEventListener(key.slice(2).toLowerCase(), value);
      } else {
   
        // 2.2 prop 是字符串
        el.setAttribute(key, value);
      }
    }
  }

  // 3. 处理 children
  if (vnode.children) {
   
    // 3.1 如果 children 是字符串,直接设置文本内容
    if (typeof vnode.children === "string") {
   
      el.textContent = vnode.children;
    }
    // 3.2 如果 children 是数组,递归挂载每个子节点
    else {
   
      console.log(vnode.children);
      // 先拿到里面的每一个 vnode
      vnode.children.forEach((item) => {
   
        // 再把里面的vnode递归调用
        mount(item, el);
      });
    }
  }
  // 4. 挂载
  container.appendChild(el
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

John Rivers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值