• 【vue设计与实现】渲染器 2-自定义渲染器


    下面来实现的是渲染器的跨平台能力
    先从渲染一个普通的

    标签开始,可以用如下vnode对象来描述一个

    标签

    const vnode = {
    	type: 'h1',
    	children: 'hello'
    }
    
    • 1
    • 2
    • 3
    • 4

    上面的vnode对象,使用type属性来描述vnode类型,当type属性时字符串类型值时,可以认为其描述的是普通标签,并使用该type属性的字符串值作为标签的名称。这样我们可以使用render函数来渲染,如下面代码所示:

    const vnode = {
    	type: 'h1',
    	children: 'hello'
    }
    // 创建一个渲染器
    const renderer = createRenderer()
    // 调用render函数渲染该vnode
    renderer.render(vnode, document.querySelector('#app'))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    为了完成渲染工作,需要补充patch函数

    function createRenderer(){
    	function patch(n1,n2,container){
    		// 如果n1不存在,意味着挂载,则调用mountElement函数完成挂载
    		if(!n1){
    			mountElement(n2,container)
    		}else{
    			// n1存在,意味着打补丁,暂时省略
    		}
    	}
    	// 省略其他代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    当n1不存在时,意味着没有旧vnode,此时只需要执行挂载即可。这时调用mountElement完成挂载,其实现如下:

    function mountElement(vnode,container){
    	// 创建DOM元素
    	const el = document.createElement(vnode.type)
    	// 处理子节点,如果子节点是字符串,代表元素具有文本节点
    	if(typeof vnode.children === 'string'){
    		// 因此值需要设置元素的textContent属性即可
    		el.textContent = vnode.children
    	}
    	// 将元素添加到容器中
    	container.appendChild(el)
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    知识扩展
    createElement() 方法通过指定名称创建一个元素
    document.createElement(nodename)
    nodename是创建元素的名称。

    textContent 属性设置或者返回指定节点的文本内容
    如果设置了 textContent 属性, 任何的子节点会被移除及被指定的字符串的文本节点替换。

    上面的代码实现了挂载一个普通标签元素的工作,但是我们的目的是设计一个不依赖于浏览器平台的通用渲染器,而mountElement函数内调用了大量依赖于浏览器的api,所以第一步要做的就是将这些浏览器特有的api抽离。具体可以将这些操作DOM的API作为配置项,该配置项可以作为createRenderer的参数,如下面代码

    // 创建renderer时传入配置项
    const renderer = createRenderer({
    	// 用于创建元素
    	createElement(tag){
    		return document.createElement(tag)
    	},
    	// 用于设置元素的文本节点
    	setElementText(el,text){
    		el.textContent = text
    	},
    	// 用于在给定的parent下添加指定元素
    	insert(el,parent,anchor = null){
    		parent.insertBefore(el,anchor)
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里直接把用于操作DOM的API封装为一个对象,并将其传递给createRenderer函数。这样在mountElement等函数内就可以通过配置项来取得操作DOM的API了

    function createRenderer(options){
    	// 通过options得到操作DOM的API
    	const{
    		createElement,
    		insert,
    		setElementText
    	} = options
    
    	// 在这个作用域内定义的函数都可以访问那些API
    	function mountElement(vnode, container){
    		// ...
    	}
    
    	function patch(n1, n2, container){
    		// ...
    	}
    
    	function patch(n1, n2, container){
    		// ...
    	}
    
    	return {
    		render
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    接着从配置项取出得到的API重新实现mountElement函数:

    function mountElement(vnode, container){
    	// 调用createElement函数创建元素
    	const el = createElement(vnode.type)
    	// 处理子节点,如果子节点是字符串,代表元素具有文本节点
    	if(typeof vnode.children === 'string'){
    		// 调用setElementText设置元素的文本节点
    		setElement(el,vnode.children)
    	}
    	// 调用insert函数将元素插入到容器内
    	insert(el,container)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样通过传入不同的配置项,就能够完成非浏览器环境下的渲染工作。

  • 相关阅读:
    C++PrimerPlus 第六章 分支语句和逻辑运算符(复习题含答案)
    MySQL 中 count() 和 count(1) 有什么区别?哪个性能最好?
    基于C51实现按键控制
    20天拿下华为OD笔试之【模拟】2023B-阿里巴巴找黄金宝箱(1)【欧弟算法】全网注释最详细分类最全的华为OD真题题解
    leetcode622.设计循环队列(C语言)
    大数据架构师——音乐数据中心平台离线数仓综合项目(一)
    国外Windows主机的特点
    labview中循环停止事件的深入研究
    『期末复习』计算机中常用术语
    在 ABAP 开发工具运行时错误显示界面里植入思否猫
  • 原文地址:https://blog.csdn.net/loyd3/article/details/125778099