深入学习Vue.js(三)渲染器的设计

文章目录

  • 1.渲染器的基本概念
  • 2.自定义渲染器
  • 3.渲染器的挂载与更新
  • 4.HTML Attributes 和 DOM Properties
  • 5.对class属性的设置
  • 6.卸载操作
  • 7.区分vnode的类型
  • 8.对事件的处理
  • 9.事件冒泡机制
  • 10.更新子节点
  • 11.文本和注释节点
  • 12.Fragment

1.渲染器的基本概念

​ 渲染器的作用是把虚拟DOM渲染为特定平台上的真实元素。在浏览器平台上,渲染器会把虚拟DOM渲染为真实DOM元素。而这一过程,被称为挂载,通常用英文字母mount表示。

function render(vnode, container) {
    // 将vnode渲染为真实DOM,然后挂载到container上
}

​ 而渲染器除了要进行渲染操作外,通常还需要执行更新操作。而这个更新操作的具体过程,就是我们耳熟能详的diff函数,这个过程通常也被称为打补丁,也就是英文单词patch。

function render(vnode, container) {
    if(vnode) {
       // 如果vnode存在,进行更新操作
       patch(container._vnode, vnode, container); 
    } else {
        if(container._vnode) {
			// 否则,若干container存在node结点,清空contain的内容
        }
        container._vnode = null;
    }
}

2.自定义渲染器

​ 在前文中我们提到过,因为虚拟DOM将UI抽象成了对象,因此借助虚拟DOM我们可以很容易的实现跨平台的特性。而渲染系统作为最核心的几个子系统之一,将它设计成可配置的,解耦合是非常有必要的。

function createRenderer(options) {
    
    const {
        createElement,
        insert,
        setElementText,
    } = options;
    
    function mountElement(vnode, container) {
        // 调用 createElement 函数创建元素
        const el = createElement(vnode.type)
        if (typeof vnode.children === 'string') {
            // 调用 setElementText 设置元素的文本节点
            setElementText(el, vnode.children)
        } 
        // 调用 insert 函数将元素插入到容器内
        insert(el, container)
    }
    
    function patch(n1, n2, container) {
        if(!n1) {
            mountElement(n2,container);
        } else {
            // 进行比较修改
        }
    }
    
    function render(vnode, container) {
        if(vnode) {
           // 如果vnode存在,进行更新操作
           patch(container._vnode, vnode, container); 
        } else {
            if(container._vnode) {
                // 否则,若干container存在node结点,清空contain的内容
            }
            container._vnode = null;
        }
    }
    
    return {
        render,
    }
}

const renderer = createRenderer({
    createElement(tag) {
        return document.createElement(tag);
    },
    setELementText(el, text) {
        el.textContent = text;
    },
    insert(el, parent, anchor = null) {
        parent.insertBefore(el, anchor);
    }
})

3.渲染器的挂载与更新

​ 通常需要渲染的子元素呈一个多叉树结构,而每一个标签都是具有不同属性的,比如form就有特殊的属性“action”,因此,在vnode对象解构中,我们用数组children来表示一个节点的子元素,props来表示一个元素的属性。

const vnode = {
    type = 'div',
    props: {
		id: 'app',	
	},
    children: [
        {
            type: 'span',
            children: 'hellp'
        }
    ]
}

​ 对于以上两者的挂载,我们在mountELement函数中,分别遍历这两个属性。

function mountElement(vnode, container) {
    const el = createElement(vnode.type);
    if (typeof vnode.children === "string") {
      setElementText(el, vnode.children);
    } else if (Array.isArray(vnode.children)) {
      // 如果当前children属性是一个数组,遍历该数组,然后分别进行挂载
      vnode.children.forEach((child) => {
        patch(null, child, el);
      });
    }
    
    if(vnode.props) {
    	// 如果当前节点具有props属性,遍历props属性,然后挂载props属性
        for(const key in vnode.props) {
        el[key] = vnode.props[key];
      }
    }
    insert(el, container);
  }

4.HTML Attributes 和 DOM Properties

​ 顾名思义,HTML Attributes就是定义在HTML标签上的属性,而DOM Properties就是HTML元素对应的DOM对象上持有的属性,事实上,大部分HTML Attributes和DOM properties的同名属性都指代同一个值,但是在这之中还是存在着少部分的差异。比如说,对于class属性,在HTML Attributes中用class描述,而在DOM Properties中,对应的却是calssName;再比如,在Dom Properties中可以使用textContent属性设置元素的内容,而在HTML Attributes中却没有对应的属性。

​ 另外一点,通过HTML Attributes设置的是对应DOM Properties的初始值。

<input value="1">
    
<script>
    const el = document.querySelector("input");
    el.value = "2";
    console.log(el.getAttribute("value")) // 1
    console.log(el.value) // 2
    console.log(el.defaultValue) // 1
script>

​ 因为HTML Attributes和DOM Properties都存在一定的问题,所以在进行属性设置的时候,我们需要进行一些特殊的处理。比如说,在设置button的disable属性时,如果我们希望按钮禁用,通常会以

你可能感兴趣的:(Vue3,vue.js,学习,前端,渲染器,Diff)