vue3的实现思路

126 阅读2分钟

实现过程

  1. 编译器将模板编译成渲染函数
  2. 渲染函数返回一个虚拟DOM
  3. 渲染器将虚拟DOM渲染成页面

渲染器

渲染器实现

function renderer(vnode, container) {
  // 使用 vnode.tag 作为标签名称创建 DOM 元素
  const el = document.createElement(vnode.tag)
  // 遍历 vnode.props,将属性、事件添加到 DOM 元素
  for (const key in vnode.props) {
    if (/^on/.test(key)) {
      /*target.addEventListener(type, listener[, options]);
            target: 必须,需要为其添加事件监听器的DOM元素、Window对象或Document对象。
            type: 必须,字符串类型,表示要监听的事件类型,如"click", "mouseover", "load"等。
            listener: 必须,事件处理函数,当指定的事件在目标元素上触发时,该函数会被调用。
            options: 可选,一个可选的对象,用来指定添加事件监听器的一些额外选项,如:
                capture: 布尔值,表示事件是否在捕获阶段触发,默认为 false,即冒泡阶段触发。
                once: 布尔值,表示事件处理函数是否只执行一次,之后自动移除,默认为false。
                passive: 布尔值,用于改善滚动性能,默认为false,设置为true表示浏览器在滚动期间不会阻止对scroll事件的默认行为。*/
      // 如果 key 以 on 开头,说明它是事件
      el.addEventListener(
        key.substr(2).toLowerCase(), // 事件名称 onClick ---> click
        vnode.props[key] // 事件处理函数
      )
    }
  }

  // 处理 children
  if (typeof vnode.children === 'string') {
    // 如果 children 是字符串,说明它是元素的文本子节点
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    // 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点
    vnode.children.forEach(child => renderer(child, el))
  }

  // 将元素添加到挂载点下
  container.appendChild(el)
}

虚拟dom

const vnode = {
  tag: 'div',
  children: [
    {
      tag: 'div',
      children: 'click me1',
      props: {
        onClick: () => alert('hello')
      }
    }
  ]
}

调用

renderer(vnode, document.body)

扩展

命令式编程

命令式编程关注怎么做,强调一步一步改变程序状态达到预期效果

声明式编程

声明式编程关注做什么,专注于描述预期结果和约束条件,而不关注过程,程序更抽象,通过调用表达式、函数、规则和查询等方式描述数据转换和计算逻辑

我的理解

Vue用了声明式来描述UI,用户不用关注过程,但是命令式编程是不可避免的,内部的实现也是使用了命令式,只是用户不需要关注而已,他们没有孰轻孰重,只有适不适合

扩展例子

下面是一个分别用命令式和声明式实现数组相加的例子

  • 命令式
// 命令式编程实现数组求和
function imperativeSum(array) {
  let sum = 0;
  for (let i = 0; i < array.length; i++) {
    sum += array[i];
  }
  return sum;
}

let numbers = [1, 2, 3, 4, 5];
console.log(imperativeSum(numbers)); // 输出:15
  • 声明式
// 声明式编程实现数组求和(此处使用JavaScript中的Array.prototype.reduce方法)
function declarativeSum(array) {
  return array.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
}

let numbers = [1, 2, 3, 4, 5];
console.log(declarativeSum(numbers)); // 输出:15

备注:部分例子来源于《Vue.js的设计与实现》