Vue3渲染器的设计(一)

本文详细介绍了Vue.js中渲染器的工作原理,从简单的DOM渲染开始,逐步引入响应式数据和虚拟DOM的概念。通过自定义渲染器,展示了如何实现跨平台的渲染功能,强调了渲染器在挂载、更新操作中的角色,并探讨了如何通过抽象节点创建API以实现平台无关性。
摘要由CSDN通过智能技术生成

文章参考了霍春阳的《Vue.js设计与实现》,是自己在阅读过程中的一些思考和理解

文章中所用的ref函数,为介绍响应式文章中所写的响应式代码,并非官方所提供的库。推荐想了解响应式原理读者可以去看看 : )

渲染器的设计

目前程度的渲染器暂定在DOM平台。因此简单渲染函数如下:

function render(domString, container) {
  container.innerHTML = domString
}
// 调用函数,将其放在id为app的元素中
render('<h1>芜湖</h1>', document.getElementById('app'))

不仅仅是简单的文本,也可以用模版字符串传变量数据,进而可以传入响应式数据。代码如下:

<body>
  <div id="app"></div>
</body>
<script>
  // 这里调用之前写的vue函数文件
  const count = ref(1)
  function render(domString, container) {
    container.innerHTML = domString
  }
  // effect函数中调用render,传入响应式数据
  effect(() => {
    render(`<h1>${count.value}</h1>`, document.getElementById('app'))
  })
</script>

效果如下:
图片: https://cooper.didichuxing.com/cooper_gateway/cn/shimo-images/zjY0IsjwX8oBkGxJ/image.png

渲染器的基本概念

这里用到了renderer(渲染器)和render(渲染)两个名词。vnode为虚拟节点,将虚拟节点渲染为真实DOM的过程称为挂载(mount)。而真实的DOM元素会经过渲染器将其放在container(容器)里面。代码如下:

// 构造渲染器
function createRenderer() {
  // 渲染函数
  function render(vnode, container) {
    
  }
  // 服务端渲染
  function hydrate(vnode, container) {
    
  }
  return {
    render,
    hydrate
  }
}
// 获取渲染器
const renderer = createRenderer()
// 在app容器里面执行渲染操作
renderer.render(vnode, document.getElementById('app'))

如果同时在同一个container上多次执行渲染操作,除了挂载操作外,还涉及到更新操作。代码如下:

function createRender() {
  function render(vnode, container) {
  // 如果传入了新的节点,就在patch函数中去进行更新
    if(vnode) {
      patch(container._vnode, vnode, container)
    } else {
      // 如果传入的为空,且旧的node存在,则将其置空
      if(container._vnode) {
        container.innerHTML = ''
      }
    }
    // 将容器的node置为新传入的node
    container._vnode = vnode
  }
  return {
    render
  }
}

自定义渲染器

自定义的渲染器是为了达成跨平台,能够通用的渲染器。定义如下vnode:

const vnode = {
  type: 'h1',
  children: '芜湖起飞'
}

vnode要传入渲染函数,渲染函数中会调用patch函数,这里给出patch的功能:

function patch(container._vnode, vnode, container) {
  // 如果不存在旧节点,则说明是挂载
  if(!container._vnode) {
    // 将节点对象和要挂载的容器传入挂载函数
    mountElement(vnode, container)
  } else {
    // 如果存在旧节点,则说明是更新
  }
} 

这里给出mountElement函数的简单实现:

// 挂载函数
function mountElement(vnode, container) {
  // 建立传入节点的类型
  const el = document.createElement(vnode.type)
  // 如果children是字符串,则说明是文本类
  if(typeof vnode.children === 'string') {
    // 直接将其放入标签中
    el.textContent = vnode.children
  }
  // 容器中添加节点
  container.appendChild(el)
}

观察这几个函数,发现使用的都是document的操作,这样就限制了使用的平台仅限于浏览器了,这样肯定是不行的,为了兼容各平台的开发,节点的创建需要抽离出来,并作为配置项传递给createRenderer函数,这样就能在各平台使用各自的节点创建API来实现开发。代码如下:

const renderer = createRenderer({
  // 创建渲染器时,传入节点创建API
  createElement(el) {
    return document.createElement(el)
  },
  // 节点构建API
  insert(el, parent, anchor=null) {
    parent.insertBefore(el, anchor)
  },
  // 内容设置API
  setElementText(el, text) {
    el.textContent = text
  }
})
 // 挂载函数
function mountElement(vnode, container) {
  // 建立传入节点的类型
  const el = createElement(vnode.type)
  // 如果children是字符串,则说明是文本类
  if(typeof vnode.children === 'string') {
    // 直接将其放入标签中
    setElementText(el, vnode.children)
  }
  // 容器中添加节点
  insert(el, container)
}

可以看到,挂载函数不再依赖原始的浏览器API,而是完全使用传入的API,因此只需要配置好当前平台的节点创建API,就可以实现不同平台的开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值